refactored Casper.evaluate() in order to accept arguments!
So basically now you can do: ``` js casper.evaluate(function(username, password) { document.querySelector('#username').value = username; document.querySelector('#password').value = password; document.querySelector('#submit').click(); }, { username: 'Bazoonga', password: 'baz00nga' }) ```
Showing
2 changed files
with
92 additions
and
82 deletions
... | @@ -120,11 +120,9 @@ | ... | @@ -120,11 +120,9 @@ |
120 | * @return string Base64 encoded result | 120 | * @return string Base64 encoded result |
121 | */ | 121 | */ |
122 | base64encode: function(url) { | 122 | base64encode: function(url) { |
123 | return this.evaluate(function() { | 123 | return this.evaluate(function(url) { |
124 | return __utils__.getBase64(__casper_params__.url); | 124 | return __utils__.getBase64(url); |
125 | }, { | 125 | }, { url: url }); |
126 | url: url | ||
127 | }); | ||
128 | }, | 126 | }, |
129 | 127 | ||
130 | /** | 128 | /** |
... | @@ -168,9 +166,9 @@ | ... | @@ -168,9 +166,9 @@ |
168 | * @return Casper | 166 | * @return Casper |
169 | */ | 167 | */ |
170 | captureSelector: function(targetFile, selector) { | 168 | captureSelector: function(targetFile, selector) { |
171 | return this.capture(targetFile, this.evaluate(function() { | 169 | return this.capture(targetFile, this.evaluate(function(selector) { |
172 | try { | 170 | try { |
173 | var clipRect = document.querySelector(__casper_params__.selector).getBoundingClientRect(); | 171 | var clipRect = document.querySelector(selector).getBoundingClientRect(); |
174 | return { | 172 | return { |
175 | top: clipRect.top, | 173 | top: clipRect.top, |
176 | left: clipRect.left, | 174 | left: clipRect.left, |
... | @@ -178,11 +176,9 @@ | ... | @@ -178,11 +176,9 @@ |
178 | height: clipRect.height | 176 | height: clipRect.height |
179 | }; | 177 | }; |
180 | } catch (e) { | 178 | } catch (e) { |
181 | __utils__.log("Unable to fetch bounds for element " + __casper_params__.selector, "warning"); | 179 | __utils__.log("Unable to fetch bounds for element " + selector, "warning"); |
182 | } | 180 | } |
183 | }, { | 181 | }, { selector: selector })); |
184 | selector: selector | ||
185 | })); | ||
186 | }, | 182 | }, |
187 | 183 | ||
188 | /** | 184 | /** |
... | @@ -224,8 +220,8 @@ | ... | @@ -224,8 +220,8 @@ |
224 | click: function(selector, fallbackToHref) { | 220 | click: function(selector, fallbackToHref) { |
225 | fallbackToHref = isType(fallbackToHref, "undefined") ? true : !!fallbackToHref; | 221 | fallbackToHref = isType(fallbackToHref, "undefined") ? true : !!fallbackToHref; |
226 | this.log("click on selector: " + selector, "debug"); | 222 | this.log("click on selector: " + selector, "debug"); |
227 | return this.evaluate(function() { | 223 | return this.evaluate(function(selector, fallbackToHref) { |
228 | return __utils__.click(__casper_params__.selector, __casper_params__.fallbackToHref); | 224 | return __utils__.click(selector, fallbackToHref); |
229 | }, { | 225 | }, { |
230 | selector: selector, | 226 | selector: selector, |
231 | fallbackToHref: fallbackToHref | 227 | fallbackToHref: fallbackToHref |
... | @@ -323,24 +319,12 @@ | ... | @@ -323,24 +319,12 @@ |
323 | 319 | ||
324 | /** | 320 | /** |
325 | * Evaluates an expression in the page context, a bit like what | 321 | * Evaluates an expression in the page context, a bit like what |
326 | * WebPage#evaluate does, but can also replace values by their | 322 | * WebPage#evaluate does, but the passed function can also accept |
327 | * placeholer names: | 323 | * parameters if a context Object is also passed: |
328 | * | 324 | * |
329 | * casper.evaluate(function() { | 325 | * casper.evaluate(function(username, password) { |
330 | * document.querySelector('#username').value = '%username%'; | 326 | * document.querySelector('#username').value = username; |
331 | * document.querySelector('#password').value = '%password%'; | 327 | * document.querySelector('#password').value = password; |
332 | * document.querySelector('#submit').click(); | ||
333 | * }, { | ||
334 | * username: 'Bazoonga', | ||
335 | * password: 'baz00nga' | ||
336 | * }) | ||
337 | * | ||
338 | * As an alternative, CasperJS injects a `__casper_params__` Object | ||
339 | * instance containing all the parameters you passed: | ||
340 | * | ||
341 | * casper.evaluate(function() { | ||
342 | * document.querySelector('#username').value = __casper_params__.username; | ||
343 | * document.querySelector('#password').value = __casper_params__.password; | ||
344 | * document.querySelector('#submit').click(); | 328 | * document.querySelector('#submit').click(); |
345 | * }, { | 329 | * }, { |
346 | * username: 'Bazoonga', | 330 | * username: 'Bazoonga', |
... | @@ -351,25 +335,15 @@ | ... | @@ -351,25 +335,15 @@ |
351 | * arguments to the function. | 335 | * arguments to the function. |
352 | * TODO: don't forget to keep this backward compatible. | 336 | * TODO: don't forget to keep this backward compatible. |
353 | * | 337 | * |
354 | * @param function fn The function to be evaluated within current page DOM | 338 | * @param Function fn The function to be evaluated within current page DOM |
355 | * @param object replacements Parameters to pass to the remote environment | 339 | * @param Object context Object containing the parameters to inject into the function |
356 | * @return mixed | 340 | * @return mixed |
357 | * @see WebPage#evaluate | 341 | * @see WebPage#evaluate |
358 | */ | 342 | */ |
359 | evaluate: function(fn, replacements) { | 343 | evaluate: function(fn, context) { |
360 | replacements = isType(replacements, "object") ? replacements : {}; | 344 | context = isType(context, "object") ? context : {}; |
361 | this.page.evaluate(replaceFunctionPlaceholders(function() { | 345 | var newFn = new phantom.Casper.FunctionArgsInjector(fn, context).process(); |
362 | window.__casper_params__ = {}; | 346 | return this.page.evaluate(newFn); |
363 | try { | ||
364 | var jsonString = unescape(decodeURIComponent('%replacements%')); | ||
365 | window.__casper_params__ = JSON.parse(jsonString); | ||
366 | } catch (e) { | ||
367 | __utils__.log("Unable to replace parameters: " + e, "error"); | ||
368 | } | ||
369 | }, { | ||
370 | replacements: encodeURIComponent(escape(JSON.stringify(replacements).replace("'", "\'"))) | ||
371 | })); | ||
372 | return this.page.evaluate(replaceFunctionPlaceholders(fn, replacements)); | ||
373 | }, | 347 | }, |
374 | 348 | ||
375 | /** | 349 | /** |
... | @@ -395,11 +369,9 @@ | ... | @@ -395,11 +369,9 @@ |
395 | * @return Boolean | 369 | * @return Boolean |
396 | */ | 370 | */ |
397 | exists: function(selector) { | 371 | exists: function(selector) { |
398 | return this.evaluate(function() { | 372 | return this.evaluate(function(selector) { |
399 | return __utils__.exists(__casper_params__.selector); | 373 | return __utils__.exists(selector); |
400 | }, { | 374 | }, { selector: selector }); |
401 | selector: selector | ||
402 | }); | ||
403 | }, | 375 | }, |
404 | 376 | ||
405 | /** | 377 | /** |
... | @@ -411,11 +383,9 @@ | ... | @@ -411,11 +383,9 @@ |
411 | * @return Boolean | 383 | * @return Boolean |
412 | */ | 384 | */ |
413 | visible: function(selector) { | 385 | visible: function(selector) { |
414 | return this.evaluate(function() { | 386 | return this.evaluate(function(selector) { |
415 | return __utils__.visible(__casper_params__.selector); | 387 | return __utils__.visible(selector); |
416 | }, { | 388 | }, { selector: selector }); |
417 | selector: selector | ||
418 | }); | ||
419 | }, | 389 | }, |
420 | 390 | ||
421 | /** | 391 | /** |
... | @@ -437,11 +407,9 @@ | ... | @@ -437,11 +407,9 @@ |
437 | * @return String | 407 | * @return String |
438 | */ | 408 | */ |
439 | fetchText: function(selector) { | 409 | fetchText: function(selector) { |
440 | return this.evaluate(function() { | 410 | return this.evaluate(function(selector) { |
441 | return __utils__.fetchText(__casper_params__.selector); | 411 | return __utils__.fetchText(selector); |
442 | }, { | 412 | }, { selector: selector }); |
443 | selector: selector | ||
444 | }); | ||
445 | }, | 413 | }, |
446 | 414 | ||
447 | /** | 415 | /** |
... | @@ -459,8 +427,8 @@ | ... | @@ -459,8 +427,8 @@ |
459 | if (!isType(vals, "object")) { | 427 | if (!isType(vals, "object")) { |
460 | throw "form values must be provided as an object"; | 428 | throw "form values must be provided as an object"; |
461 | } | 429 | } |
462 | var fillResults = this.evaluate(function() { | 430 | var fillResults = this.evaluate(function(selector, values) { |
463 | return __utils__.fill(__casper_params__.selector, __casper_params__.values); | 431 | return __utils__.fill(selector, values); |
464 | }, { | 432 | }, { |
465 | selector: selector, | 433 | selector: selector, |
466 | values: vals | 434 | values: vals |
... | @@ -489,15 +457,13 @@ | ... | @@ -489,15 +457,13 @@ |
489 | } | 457 | } |
490 | // Form submission? | 458 | // Form submission? |
491 | if (submit) { | 459 | if (submit) { |
492 | this.evaluate(function() { | 460 | this.evaluate(function(selector) { |
493 | var form = document.querySelector(__casper_params__.selector); | 461 | var form = document.querySelector(selector); |
494 | var method = form.getAttribute('method').toUpperCase() || "GET"; | 462 | var method = form.getAttribute('method').toUpperCase() || "GET"; |
495 | var action = form.getAttribute('action') || "unknown"; | 463 | var action = form.getAttribute('action') || "unknown"; |
496 | __utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info'); | 464 | __utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info'); |
497 | form.submit(); | 465 | form.submit(); |
498 | }, { | 466 | }, { selector: selector }); |
499 | selector: selector | ||
500 | }); | ||
501 | } | 467 | } |
502 | }, | 468 | }, |
503 | 469 | ||
... | @@ -532,8 +498,7 @@ | ... | @@ -532,8 +498,7 @@ |
532 | * @return mixed | 498 | * @return mixed |
533 | */ | 499 | */ |
534 | getGlobal: function(name) { | 500 | getGlobal: function(name) { |
535 | var result = this.evaluate(function() { | 501 | var result = this.evaluate(function(name) { |
536 | var name = window.__casper_params__.name; | ||
537 | var result = {}; | 502 | var result = {}; |
538 | try { | 503 | try { |
539 | result.value = JSON.stringify(window[name]); | 504 | result.value = JSON.stringify(window[name]); |
... | @@ -786,14 +751,14 @@ | ... | @@ -786,14 +751,14 @@ |
786 | * Adds a new navigation step to perform code evaluation within the | 751 | * Adds a new navigation step to perform code evaluation within the |
787 | * current retrieved page DOM. | 752 | * current retrieved page DOM. |
788 | * | 753 | * |
789 | * @param function fn The function to be evaluated within current page DOM | 754 | * @param function fn The function to be evaluated within current page DOM |
790 | * @param object replacements Optional replacements to performs, eg. for '%foo%' => {foo: 'bar'} | 755 | * @param object context Optional function parameters context |
791 | * @return Casper | 756 | * @return Casper |
792 | * @see Casper#evaluate | 757 | * @see Casper#evaluate |
793 | */ | 758 | */ |
794 | thenEvaluate: function(fn, replacements) { | 759 | thenEvaluate: function(fn, context) { |
795 | return this.then(function(self) { | 760 | return this.then(function(self) { |
796 | self.evaluate(fn, replacements); | 761 | self.evaluate(fn, context); |
797 | }); | 762 | }); |
798 | }, | 763 | }, |
799 | 764 | ||
... | @@ -818,15 +783,15 @@ | ... | @@ -818,15 +783,15 @@ |
818 | * Adds a new navigation step for opening and evaluate an expression | 783 | * Adds a new navigation step for opening and evaluate an expression |
819 | * against the DOM retrieved from the provided location. | 784 | * against the DOM retrieved from the provided location. |
820 | * | 785 | * |
821 | * @param String location The url to open | 786 | * @param String location The url to open |
822 | * @param function fn The function to be evaluated within current page DOM | 787 | * @param function fn The function to be evaluated within current page DOM |
823 | * @param object replacements Optional replacements to performs, eg. for '%foo%' => {foo: 'bar'} | 788 | * @param object context Optional function parameters context |
824 | * @return Casper | 789 | * @return Casper |
825 | * @see Casper#evaluate | 790 | * @see Casper#evaluate |
826 | * @see Casper#open | 791 | * @see Casper#open |
827 | */ | 792 | */ |
828 | thenOpenAndEvaluate: function(location, fn, replacements) { | 793 | thenOpenAndEvaluate: function(location, fn, context) { |
829 | return this.thenOpen(location).thenEvaluate(fn, replacements); | 794 | return this.thenOpen(location).thenEvaluate(fn, context); |
830 | }, | 795 | }, |
831 | 796 | ||
832 | /** | 797 | /** |
... | @@ -1681,6 +1646,51 @@ | ... | @@ -1681,6 +1646,51 @@ |
1681 | }; | 1646 | }; |
1682 | 1647 | ||
1683 | /** | 1648 | /** |
1649 | * Function argument injector. | ||
1650 | * | ||
1651 | */ | ||
1652 | phantom.Casper.FunctionArgsInjector = function(fn, values) { | ||
1653 | this.fn = fn; | ||
1654 | this.values = typeof values === "object" ? values : {}; | ||
1655 | |||
1656 | this.extract = function(fn) { | ||
1657 | var match = /^function\s?(\w+)?\s?\((.*)\)\s?\{([\s\S]*)\}/i.exec(fn.toString().trim()); | ||
1658 | if (match && match.length > 1) { | ||
1659 | var args = match[2].split(',').map(function(arg) { | ||
1660 | return arg.replace(new RegExp(/\/\*+.*\*\//ig), "").trim(); | ||
1661 | }).filter(function(arg) { | ||
1662 | return arg; | ||
1663 | }) || []; | ||
1664 | return { | ||
1665 | name: match[1] ? match[1].trim() : null, | ||
1666 | args: args, | ||
1667 | body: match[3] ? match[3].trim() : '' | ||
1668 | }; | ||
1669 | } | ||
1670 | }; | ||
1671 | |||
1672 | this.process = function() { | ||
1673 | var fnObj = this.extract(this.fn); | ||
1674 | var inject = this.getArgsInjectionString(fnObj.args, this.values); | ||
1675 | return 'function ' + (fnObj.name || '') + '(){' + inject + fnObj.body + '}'; | ||
1676 | }; | ||
1677 | |||
1678 | this.getArgsInjectionString = function(args, values) { | ||
1679 | values = typeof values === "object" ? values : {}; | ||
1680 | var jsonValues = escape(encodeURIComponent(JSON.stringify(values))); | ||
1681 | var inject = [ | ||
1682 | 'var __casper_params__ = JSON.parse(decodeURIComponent(unescape(\'' + jsonValues + '\')));' | ||
1683 | ]; | ||
1684 | args.forEach(function(arg) { | ||
1685 | if (arg in values) { | ||
1686 | inject.push('var ' + arg + '=__casper_params__["' + arg + '"];'); | ||
1687 | } | ||
1688 | }); | ||
1689 | return inject.join('\n') + '\n'; | ||
1690 | }; | ||
1691 | }; | ||
1692 | |||
1693 | /** | ||
1684 | * JUnit XML (xUnit) exporter for test results. | 1694 | * JUnit XML (xUnit) exporter for test results. |
1685 | * | 1695 | * |
1686 | */ | 1696 | */ |
... | @@ -1817,7 +1827,7 @@ | ... | @@ -1817,7 +1827,7 @@ |
1817 | } | 1827 | } |
1818 | } | 1828 | } |
1819 | } | 1829 | } |
1820 | // Client utils injection | 1830 | // Client-side utils injection |
1821 | var injected = page.evaluate(replaceFunctionPlaceholders(function() { | 1831 | var injected = page.evaluate(replaceFunctionPlaceholders(function() { |
1822 | eval("var ClientUtils = " + decodeURIComponent("%utils%")); | 1832 | eval("var ClientUtils = " + decodeURIComponent("%utils%")); |
1823 | __utils__ = new ClientUtils(); | 1833 | __utils__ = new ClientUtils(); | ... | ... |
... | @@ -78,7 +78,7 @@ fs.remove(testFile); | ... | @@ -78,7 +78,7 @@ fs.remove(testFile); |
78 | 78 | ||
79 | // Casper#evaluate() | 79 | // Casper#evaluate() |
80 | casper.then(function(self) { | 80 | casper.then(function(self) { |
81 | self.test.comment('evaluating'); | 81 | self.test.comment('Casper.evaluate()'); |
82 | var params = { | 82 | var params = { |
83 | "boolean true": true, | 83 | "boolean true": true, |
84 | "boolean false": false, | 84 | "boolean false": false, |
... | @@ -92,7 +92,7 @@ casper.then(function(self) { | ... | @@ -92,7 +92,7 @@ casper.then(function(self) { |
92 | return __casper_params__; | 92 | return __casper_params__; |
93 | }, params); | 93 | }, params); |
94 | self.test.assertType(casperParams, "object", 'Casper.evaluate() exposes parameters in a dedicated object'); | 94 | self.test.assertType(casperParams, "object", 'Casper.evaluate() exposes parameters in a dedicated object'); |
95 | self.test.assertEquals(Object.keys(casperParams).length, 7, 'Casper.evaluate() exposes parameters object has the correct length'); | 95 | self.test.assertEquals(Object.keys(casperParams).length, 7, 'Casper.evaluate() object containing parameters has the correct length'); |
96 | for (var param in casperParams) { | 96 | for (var param in casperParams) { |
97 | self.test.assertEquals(JSON.stringify(casperParams[param]), JSON.stringify(params[param]), 'Casper.evaluate() can pass a ' + param); | 97 | self.test.assertEquals(JSON.stringify(casperParams[param]), JSON.stringify(params[param]), 'Casper.evaluate() can pass a ' + param); |
98 | self.test.assertEquals(typeof casperParams[param], typeof params[param], 'Casper.evaluate() preserves the ' + param + ' type'); | 98 | self.test.assertEquals(typeof casperParams[param], typeof params[param], 'Casper.evaluate() preserves the ' + param + ' type'); | ... | ... |
-
Please register or sign in to post a comment