integrated PR #22 into modularization branch
Showing
6 changed files
with
188 additions
and
43 deletions
... | @@ -68,7 +68,6 @@ | ... | @@ -68,7 +68,6 @@ |
68 | this.currentUrl = 'about:blank'; | 68 | this.currentUrl = 'about:blank'; |
69 | this.currentHTTPStatus = 200; | 69 | this.currentHTTPStatus = 200; |
70 | this.defaultWaitTimeout = 5000; | 70 | this.defaultWaitTimeout = 5000; |
71 | this.delayedExecution = false; | ||
72 | this.history = []; | 71 | this.history = []; |
73 | this.loadInProgress = false; | 72 | this.loadInProgress = false; |
74 | this.logFormats = {}; | 73 | this.logFormats = {}; |
... | @@ -81,14 +80,16 @@ | ... | @@ -81,14 +80,16 @@ |
81 | }; | 80 | }; |
82 | this.options = mergeObjects(this.defaults, options); | 81 | this.options = mergeObjects(this.defaults, options); |
83 | this.page = null; | 82 | this.page = null; |
83 | this.pendingWait = false; | ||
84 | this.requestUrl = 'about:blank'; | 84 | this.requestUrl = 'about:blank'; |
85 | this.resources = []; | ||
85 | this.result = { | 86 | this.result = { |
86 | log: [], | 87 | log: [], |
87 | status: "success", | 88 | status: "success", |
88 | time: 0 | 89 | time: 0 |
89 | }; | 90 | }; |
90 | this.started = false; | 91 | this.started = false; |
91 | this.step = 0; | 92 | this.step = -1; |
92 | this.steps = []; | 93 | this.steps = []; |
93 | this.test = new phantom.Casper.Tester(this); | 94 | this.test = new phantom.Casper.Tester(this); |
94 | }; | 95 | }; |
... | @@ -188,11 +189,13 @@ | ... | @@ -188,11 +189,13 @@ |
188 | * @param function onComplete An options callback to apply on completion | 189 | * @param function onComplete An options callback to apply on completion |
189 | */ | 190 | */ |
190 | checkStep: function(self, onComplete) { | 191 | checkStep: function(self, onComplete) { |
191 | var step = self.steps[self.step]; | 192 | if (self.pendingWait || self.loadInProgress) { |
192 | if (!self.loadInProgress && isType(step, "function")) { | 193 | return; |
193 | self.runStep(step); | ||
194 | } | 194 | } |
195 | if (!isType(step, "function") && !self.delayedExecution) { | 195 | var step = self.steps[self.step++]; |
196 | if (isType(step, "function")) { | ||
197 | self.runStep(step); | ||
198 | } else { | ||
196 | self.result.time = new Date().getTime() - self.startTime; | 199 | self.result.time = new Date().getTime() - self.startTime; |
197 | self.log("Done " + self.steps.length + " steps in " + self.result.time + 'ms.', "info"); | 200 | self.log("Done " + self.steps.length + " steps in " + self.result.time + 'ms.', "info"); |
198 | clearInterval(self.checker); | 201 | clearInterval(self.checker); |
... | @@ -592,6 +595,24 @@ | ... | @@ -592,6 +595,24 @@ |
592 | }, | 595 | }, |
593 | 596 | ||
594 | /** | 597 | /** |
598 | * Checks if a given resource was loaded by the remote page. | ||
599 | * | ||
600 | * @param Function/String test A test function or string. In case a string is passed, | ||
601 | * @return Boolean | ||
602 | */ | ||
603 | resourceExists: function(test) { | ||
604 | var testFn; | ||
605 | if (isType(test, "string")) { | ||
606 | testFn = function (res) { | ||
607 | return res.url.match(test); | ||
608 | }; | ||
609 | } else { | ||
610 | testFn = test; | ||
611 | } | ||
612 | return this.resources.some(testFn); | ||
613 | }, | ||
614 | |||
615 | /** | ||
595 | * Runs the whole suite of steps. | 616 | * Runs the whole suite of steps. |
596 | * | 617 | * |
597 | * @param function onComplete an optional callback | 618 | * @param function onComplete an optional callback |
... | @@ -615,7 +636,7 @@ | ... | @@ -615,7 +636,7 @@ |
615 | */ | 636 | */ |
616 | runStep: function(step) { | 637 | runStep: function(step) { |
617 | var skipLog = isType(step.options, "object") && step.options.skipLog === true; | 638 | var skipLog = isType(step.options, "object") && step.options.skipLog === true; |
618 | var stepInfo = "Step " + (this.step + 1) + "/" + this.steps.length; | 639 | var stepInfo = "Step " + (this.step) + "/" + this.steps.length; |
619 | var stepResult; | 640 | var stepResult; |
620 | if (!skipLog) { | 641 | if (!skipLog) { |
621 | this.log(stepInfo + ' ' + this.getCurrentUrl() + ' (HTTP ' + this.currentHTTPStatus + ')', "info"); | 642 | this.log(stepInfo + ' ' + this.getCurrentUrl() + ' (HTTP ' + this.currentHTTPStatus + ')', "info"); |
... | @@ -721,7 +742,20 @@ | ... | @@ -721,7 +742,20 @@ |
721 | if (!isType(step, "function")) { | 742 | if (!isType(step, "function")) { |
722 | throw "You can only define a step as a function"; | 743 | throw "You can only define a step as a function"; |
723 | } | 744 | } |
724 | this.steps.push(step); | 745 | // check if casper is running |
746 | if (this.checker === null) { | ||
747 | // append step to the end of the queue | ||
748 | step.level = 0; | ||
749 | this.steps.push(step); | ||
750 | } else { | ||
751 | // insert substep a level deeper | ||
752 | step.level = this.steps[this.step - 1].level + 1; | ||
753 | var insertIndex = this.step; | ||
754 | while (this.steps[insertIndex] && step.level === this.steps[insertIndex].level) { | ||
755 | insertIndex++; | ||
756 | } | ||
757 | this.steps.splice(insertIndex, 0, step); | ||
758 | } | ||
725 | return this; | 759 | return this; |
726 | }, | 760 | }, |
727 | 761 | ||
... | @@ -825,21 +859,25 @@ | ... | @@ -825,21 +859,25 @@ |
825 | this.die("wait() a step definition must be a function"); | 859 | this.die("wait() a step definition must be a function"); |
826 | } | 860 | } |
827 | return this.then(function(self) { | 861 | return this.then(function(self) { |
828 | self.delayedExecution = true; | 862 | self.waitStart(); |
829 | var start = new Date().getTime(); | 863 | setTimeout(function() { |
830 | var interval = setInterval(function(self, then) { | 864 | self.log("wait() finished wating for " + timeout + "ms.", "info"); |
831 | if (new Date().getTime() - start > timeout) { | 865 | if (then) { |
832 | self.delayedExecution = false; | 866 | then.call(self, self); |
833 | self.log("wait() finished wating for " + timeout + "ms.", "info"); | 867 | } |
834 | if (then) { | 868 | self.waitDone(); |
835 | self.then(then); | 869 | }, timeout); |
836 | } | ||
837 | clearInterval(interval); | ||
838 | } | ||
839 | }, 100, self, then); | ||
840 | }); | 870 | }); |
841 | }, | 871 | }, |
842 | 872 | ||
873 | waitStart: function() { | ||
874 | this.pendingWait = true; | ||
875 | }, | ||
876 | |||
877 | waitDone: function() { | ||
878 | this.pendingWait = false; | ||
879 | }, | ||
880 | |||
843 | /** | 881 | /** |
844 | * Waits until a function returns true to process a next step. | 882 | * Waits until a function returns true to process a next step. |
845 | * | 883 | * |
... | @@ -857,32 +895,49 @@ | ... | @@ -857,32 +895,49 @@ |
857 | if (then && !isType(then, "function")) { | 895 | if (then && !isType(then, "function")) { |
858 | this.die("waitFor() next step definition must be a function"); | 896 | this.die("waitFor() next step definition must be a function"); |
859 | } | 897 | } |
860 | this.delayedExecution = true; | 898 | return this.then(function(self) { |
861 | var start = new Date().getTime(); | 899 | self.waitStart(); |
862 | var condition = false; | 900 | var start = new Date().getTime(); |
863 | var interval = setInterval(function(self, testFx, onTimeout) { | 901 | var condition = false; |
864 | if ((new Date().getTime() - start < timeout) && !condition) { | 902 | var interval = setInterval(function(self, testFx, onTimeout) { |
865 | condition = testFx(self); | 903 | if ((new Date().getTime() - start < timeout) && !condition) { |
866 | } else { | 904 | condition = testFx(self); |
867 | self.delayedExecution = false; | ||
868 | if (!condition) { | ||
869 | self.log("Casper.waitFor() timeout", "warning"); | ||
870 | if (isType(onTimeout, "function")) { | ||
871 | onTimeout.call(self, self); | ||
872 | } else { | ||
873 | self.die("Expired timeout, exiting.", "error"); | ||
874 | } | ||
875 | clearInterval(interval); | ||
876 | } else { | 905 | } else { |
877 | self.log("waitFor() finished in " + (new Date().getTime() - start) + "ms.", "info"); | 906 | self.waitDone(); |
878 | if (then) { | 907 | if (!condition) { |
879 | self.then(then); | 908 | self.log("Casper.waitFor() timeout", "warning"); |
909 | if (isType(onTimeout, "function")) { | ||
910 | onTimeout.call(self, self); | ||
911 | } else { | ||
912 | self.die("Expired timeout, exiting.", "error"); | ||
913 | } | ||
914 | clearInterval(interval); | ||
915 | } else { | ||
916 | self.log("waitFor() finished in " + (new Date().getTime() - start) + "ms.", "info"); | ||
917 | if (then) { | ||
918 | self.then(then); | ||
919 | } | ||
920 | clearInterval(interval); | ||
880 | } | 921 | } |
881 | clearInterval(interval); | ||
882 | } | 922 | } |
883 | } | 923 | }, 100, self, testFx, onTimeout); |
884 | }, 100, this, testFx, onTimeout); | 924 | }); |
885 | return this; | 925 | }, |
926 | |||
927 | /** | ||
928 | * Waits until a given resource is loaded | ||
929 | * | ||
930 | * @param String/Function test A function to test if the resource exists. A string will be matched against the resources url. | ||
931 | * @param Function then The next step to perform (optional) | ||
932 | * @param Function onTimeout A callback function to call on timeout (optional) | ||
933 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) | ||
934 | * @return Casper | ||
935 | */ | ||
936 | waitForResource: function(test, then, onTimeout, timeout) { | ||
937 | timeout = timeout ? timeout : this.defaultWaitTimeout; | ||
938 | return this.waitFor(function(self) { | ||
939 | return self.resourceExists(test); | ||
940 | }, then, onTimeout, timeout); | ||
886 | }, | 941 | }, |
887 | 942 | ||
888 | /** | 943 | /** | ... | ... |
... | @@ -177,6 +177,16 @@ | ... | @@ -177,6 +177,16 @@ |
177 | }; | 177 | }; |
178 | 178 | ||
179 | /** | 179 | /** |
180 | * Asserts that the current page has a resource that matches the provided test | ||
181 | * | ||
182 | * @param Function/String test A test function that is called with every response | ||
183 | * @param String message Test description | ||
184 | */ | ||
185 | this.assertResourceExists = function(test, message) { | ||
186 | return this.assert(casper.resourceExists(test), message); | ||
187 | }; | ||
188 | |||
189 | /** | ||
180 | * Asserts that at least an element matching the provided CSS3 selector | 190 | * Asserts that at least an element matching the provided CSS3 selector |
181 | * exists in remote DOM. | 191 | * exists in remote DOM. |
182 | * | 192 | * | ... | ... |
... | @@ -70,6 +70,7 @@ function createPage(casper) { | ... | @@ -70,6 +70,7 @@ function createPage(casper) { |
70 | casper.log(msg, level, "remote"); | 70 | casper.log(msg, level, "remote"); |
71 | }; | 71 | }; |
72 | page.onLoadStarted = function() { | 72 | page.onLoadStarted = function() { |
73 | casper.resources = []; | ||
73 | casper.loadInProgress = true; | 74 | casper.loadInProgress = true; |
74 | }; | 75 | }; |
75 | page.onLoadFinished = function(status) { | 76 | page.onLoadFinished = function(status) { |
... | @@ -119,6 +120,9 @@ function createPage(casper) { | ... | @@ -119,6 +120,9 @@ function createPage(casper) { |
119 | if (isType(casper.options.onResourceReceived, "function")) { | 120 | if (isType(casper.options.onResourceReceived, "function")) { |
120 | casper.options.onResourceReceived.call(casper, casper, resource); | 121 | casper.options.onResourceReceived.call(casper, casper, resource); |
121 | } | 122 | } |
123 | if (resource.stage === "end") { | ||
124 | casper.resources.push(resource); | ||
125 | } | ||
122 | if (resource.url === casper.requestUrl && resource.stage === "start") { | 126 | if (resource.url === casper.requestUrl && resource.stage === "start") { |
123 | casper.currentHTTPStatus = resource.status; | 127 | casper.currentHTTPStatus = resource.status; |
124 | if (isType(casper.options.httpStatusHandlers, "object") && resource.status in casper.options.httpStatusHandlers) { | 128 | if (isType(casper.options.httpStatusHandlers, "object") && resource.status in casper.options.httpStatusHandlers) { | ... | ... |
tests/site/resources.html
0 → 100644
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||
5 | <title>CasperJS test resource</title> | ||
6 | <script> | ||
7 | setTimeout(function () { | ||
8 | document.querySelector("img").setAttribute("src","images/phantom.png"); | ||
9 | }, 1000); | ||
10 | </script> | ||
11 | </head> | ||
12 | <body> | ||
13 | <img width="55" height="55" border="1"> | ||
14 | </body> | ||
15 | </html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
tests/suites/casper/flow.coffee
0 → 100644
1 | do(casper) -> | ||
2 | step = 0 | ||
3 | |||
4 | # testing resources | ||
5 | casper.start "tests/site/resources.html", -> | ||
6 | @test.assertEquals ++step, 1, "step 1" | ||
7 | @wait 400, -> | ||
8 | @test.assertEquals ++step, 2, "step 1.1" | ||
9 | @wait 200, -> | ||
10 | @test.assertEquals ++step, 3, "step 1.1.1" | ||
11 | @wait 200, -> | ||
12 | @test.assertEquals ++step, 4, "step 1.1.1.1" | ||
13 | @then -> | ||
14 | @test.assertEquals ++step, 5, "step 1.1.2.1" | ||
15 | @wait 400, -> | ||
16 | @test.assertEquals ++step, 6, "step 1.2" | ||
17 | |||
18 | casper.wait 200, -> | ||
19 | @test.assertEquals ++step, 7, "step 2" | ||
20 | |||
21 | casper.waitForSelector( | ||
22 | '#noneExistingSelector' | ||
23 | -> @test.fail "should run into timeout" | ||
24 | -> @test.assertEquals ++step, 8, "step 3 sucessfully timed out" | ||
25 | 1000 | ||
26 | ) | ||
27 | casper.then -> | ||
28 | @test.assertEquals ++step, 9, "step 4" | ||
29 | @wait 300, -> | ||
30 | @test.assertEquals ++step, 10, "step 4.1" | ||
31 | @wait 300, -> | ||
32 | @test.assertEquals ++step, 11, "step 4.1.1" | ||
33 | @wait 100, -> | ||
34 | @test.assertEquals ++step, 12, "step 5.2" | ||
35 | |||
36 | casper.then -> | ||
37 | @test.assertEquals ++step, 13, "last step" | ||
38 | |||
39 | casper.run(-> @test.done()) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
tests/suites/casper/resources.coffee
0 → 100644
1 | do(casper) -> | ||
2 | casper.onError = -> | ||
3 | console.log 'err' | ||
4 | casper.start "tests/site/resources.html", -> | ||
5 | console.log 'loaded' | ||
6 | @test.assertEquals @resources.length, 1, "only one resource found" | ||
7 | @waitForResource "phantom.png", -> | ||
8 | @test.assertEquals( | ||
9 | @resources.length | ||
10 | 2 | ||
11 | "two resources found" | ||
12 | ) | ||
13 | @test.assertResourceExists( | ||
14 | (res) -> res.url.match "phantom.png" | ||
15 | "phantom image found via test function" | ||
16 | ) | ||
17 | @test.assertResourceExists( | ||
18 | "phantom.png" | ||
19 | "phantom image found via test string" | ||
20 | ) | ||
21 | |||
22 | casper.run(-> @test.done()) |
-
Please register or sign in to post a comment