diff --git a/zephyr/tests/frontend/casperjs/.jshintconfig b/zephyr/tests/frontend/casperjs/.jshintconfig index 87f894276c..7c4d5e4b91 100644 --- a/zephyr/tests/frontend/casperjs/.jshintconfig +++ b/zephyr/tests/frontend/casperjs/.jshintconfig @@ -9,6 +9,7 @@ "maxdepth": 3, "maxstatements": 15, "maxcomplexity": 7, + "proto": true, "regexdash": true, "strict": true, "sub": true, diff --git a/zephyr/tests/frontend/casperjs/.travis.yml b/zephyr/tests/frontend/casperjs/.travis.yml index 375ec62857..8a9e7e086a 100644 --- a/zephyr/tests/frontend/casperjs/.travis.yml +++ b/zephyr/tests/frontend/casperjs/.travis.yml @@ -1,13 +1,16 @@ branches: only: - - master + - "master" + - "1.0" before_script: + - "npm install -g jshint" - "phantomjs --version" - "export PHANTOMJS_EXECUTABLE='phantomjs --local-to-remote-url-access=yes --ignore-ssl-errors=yes'" - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" script: - - "DISPLAY=:99.0 ./bin/casperjs selftest" + - "./bin/casperjs selftest" + - "./bin/casperjs __selfcommandtest" +after_script: + - "jshint --config=.jshintconfig ." notifications: irc: channels: diff --git a/zephyr/tests/frontend/casperjs/CHANGELOG.md b/zephyr/tests/frontend/casperjs/CHANGELOG.md index 107a4f8c30..05cea5b9b3 100644 --- a/zephyr/tests/frontend/casperjs/CHANGELOG.md +++ b/zephyr/tests/frontend/casperjs/CHANGELOG.md @@ -1,6 +1,186 @@ CasperJS Changelog ================== +2013-02-08, v1.0.2 +------------------ + +- fixed [#375](https://github.com/n1k0/casperjs/pull/375) - Fixes a bug with getting form values for radio inputs, and introduces a minor optimization to avoid processing the same form fields more than once. +- closed [#373](https://github.com/n1k0/casperjs/issues/373) - added RegExp support to `Casper.waitForText()` +- fixed [#368](https://github.com/n1k0/casperjs/issues/368) - Remote JS error is thrown when a click target is missing after `click()` +- merged PR [#357](https://github.com/n1k0/casperjs/pull/357) - fire the `input` event after setting input value (required to support [angular.js](http://angularjs.org/) apps) + +2013-01-17, v1.0.1 +------------------ + +- fixed [#336](https://github.com/n1k0/casperjs/issues/336) - Test result duration may have an exotic value +- Added `casper.mouse.doubleclick()` +- fixed [#343](https://github.com/n1k0/casperjs/issues/343) - Better script checks +- fixed an edge case with xunit export when `phantom.casperScript` may be not defined + +2012-12-24, v1.0.0 +------------------ + +### Important Changes & Caveats + +- PhantomJS 1.6.x support has been dropped. Both PhantomJS [1.7](http://phantomjs.org/release-1.7.html) & [1.8](http://phantomjs.org/release-1.8.html) will be supported. +- the deprecated `injector` module has been removed from the codebase (RIP dude) +- a [`1.0` maintenance branch](https://github.com/n1k0/casperjs/tree/1.0) has been created +- CasperJS 1.1 development is now taking place on the `master` branch + +### Bugfixes & enhancements + +- fixed `page.initialized` event didn't get the initialized `WebPage` instance +- fixed a bug preventing `Casper.options.onPageInitialized()` from being called +- fixed [#215](https://github.com/n1k0/casperjs/issues/215) - fixed broken `--fail-fast` option creating an endless loop on error +- fixed `Tester.renderFailureDetails()` which couldn't print failure details correctly in certain circumstances +- fixed `Casper.getHTML()` wasn't retrieving active frame contents when using `Casper.withFrame()` +- fixed [#327](https://github.com/n1k0/casperjs/issues/327) - event handler for `page.confirm` always returns true +- merged PR [#322](https://github.com/n1k0/casperjs/pull/322) - Support number in `Casper.withFrame()` +- fixed [#323](https://github.com/n1k0/casperjs/issues/323) - `thenEvaluate()` should be updated to take the same parameters as `evaluate()`, while maintaining backwards compatibility. +- merged PR [#319](https://github.com/n1k0/casperjs/pull/319), fixed [#209](https://github.com/n1k0/casperjs/issues/209) - test duration has been added to XUnit XML result file. +- `Casper.userAgent()` does not require the instance to be started anymore +- dubious tests now have dedicated color & styling +- added hint printing when a possible `casperjs` command call is detected + +2012-12-14, v1.0.0-RC6 +---------------------- + +I'm still expecting a 1.0 stable for Christmas. Feedback: bring it on. + +### Important Changes & Caveats + +#### Added experimental support for frames + +A minimal convenient API has been added to Casper in order to ease the switch of current page context: + +```js +casper.start('tests/site/frames.html', function() { + this.test.assertTitle('CasperJS frameset'); +}); + +casper.withFrame('frame1', function() { + this.test.assertTitle('CasperJS frame 1'); +}); + +casper.then(function() { + this.test.assertTitle('CasperJS frameset'); +}); +``` + +#### Reverted to emulated mouse events + +Native mouse events didn't play well with (i)frames, because the computed element coordinates of the clicked element were erroneous. + +So programmatic mouse events are reintroduced back into this corrective RC until a better solution is found. + +### Bugfixes & enhancements + +- merged [#269](https://github.com/n1k0/casperjs/issues/269) - Windows Batch script: fixed unsupported spaces in path and argument splitting + +2012-12-10, v1.0.0-RC5 +---------------------- + +I told you there won't be an 1.0.0-RC5? I lied. Expect 1.0 stable for Christmas, probably. + +### Important Changes & Caveats + +#### Casper.evaluate() signature compatibility with PhantomJS + +`Casper.evaluate()` method signature is now compatible with PhantomJS' one, so you can now write: + +```js +casper.evaluate(function(a, b) { + return a === "foo" && b === "bar"; +}, "foo", "bar"); // true +``` + +The old way to pass arguments has been kept backward compatible in order not to break your existing scripts though: + +```js +casper.evaluate(function(a, b) { + return a === "foo" && b === "bar"; +}, {a: "foo", b: "bar"}); // true +``` + +#### Specification of planned tests + +In order to check that every planned test has actuall been executed, a new optional `planned` parameter has been added to `Tester.done()`: + +```js +casper.test.assert(true); +casper.test.assert(true); +casper.test.assert(true); +casper.test.done(4); +``` + +Will trigger a failure: + +``` +fail: 4 tests planned, 3 tests executed. +``` + +That's especially useful in case a given test script is abruptly interrupted leaving you with no obvious way to know it and an erroneous success status. + +The whole [CapserJS test suite](https://github.com/n1k0/casperjs/tree/master/tests/) has been migrated to use this new feature. + +#### Experimental support for popups + +PhantomJS 1.7 ships with support for new opened pages — aka popups. CasperJS can now wait for a popup to be opened and loaded to react accordingly using the new [`Casper.waitForPopup()`](http://casperjs.org/api.html#casper.waitForPopup) and [`Casper.withPopup()`](http://casperjs.org/api.html#casper.withPopup) methods: + +```js +casper.start('http://foo.bar/').then(function() { + this.test.assertTitle('Main page title'); + this.clickLabel('Open me a popup'); +}); + +// this will wait for the popup to be opened and loaded +casper.waitForPopup(/popup\.html$/, function() { + this.test.assertEquals(this.popups.length, 1); +}); + +// this will set the popup DOM as the main active one only for time the +// step closure being executed +casper.withPopup(/popup\.html$/, function() { + this.test.assertTitle('Popup title'); +}); + +// next step will automatically revert the current page to the initial one +casper.then(function() { + this.test.assertTitle('Main page title'); +}); +``` + +#### `Casper.mouseEvent()` now uses native events for most operations + +Native mouse events from PhantomJS bring a far more accurate behavior. + +Also, `Casper.mouseEvent()` will now directly trigger an error on failure instead of just logging an `error` event. + +### Bugfixes & enhancements + +- fixed [#308](https://github.com/n1k0/casperjs/issues/308) & [#309](https://github.com/n1k0/casperjs/issues/309) - proper module error backtraces +- fixed [#306](https://github.com/n1k0/casperjs/issues/306) - Raise an explicit error on invalid test path +- fixed [#300](https://github.com/n1k0/casperjs/issues/300) - Ensure that `findOne()` and `findAll()` observe the scope for XPath expressions, not just when passed CSS selectors +- fixed [#294](https://github.com/n1k0/casperjs/issues/294) - Automatically fail test on any runtime error or timeout +- fixed [#281](https://github.com/n1k0/casperjs/issues/281) - `Casper.evaluate()` should take an array as context not object +- fixed [#266](https://github.com/n1k0/casperjs/issues/266) - Fix `tester` module and its self tests +- fixed [#268](https://github.com/n1k0/casperjs/issues/266) - Wrong message on step timeout +- fixed [#215](https://github.com/n1k0/casperjs/issues/215) - added a `--fail-fast` option to the `casper test` command, in order to terminate a test suite execution as soon as any failure is encountered +- fixed [#274](https://github.com/n1k0/casperjs/issues/274) - some headers couldn't be set +- fixed [#277](https://github.com/n1k0/casperjs/issues/277) - multiline support in `ClientUtils.echo()` +- fixed [#282](https://github.com/n1k0/casperjs/issues/282) - added support for remote client scripts loading with a new `remoteScripts` casper option +- fixed [#290](https://github.com/n1k0/casperjs/issues/#290) - add a simplistic RPM spec file to make it easier to (un)install casperjs +- fixed [`utils.betterTypeOf()`](http://casperjs.org/api.html#casper.betterTypeOf) to properly handle `undefined` and `null` values +- fixed `Casper.die()` and `Casper.evaluateOrDie()` were not printing the error onto the console +- added JSON support to `require()` +- added [`Tester.assertTruthy()`](http://casperjs.org/api.html#tester.assertTruthy) and [`Tester.assertFalsy()`](http://casperjs.org/api.html#tester.assertFalsy) +- added [`Casper.sendKeys()`](http://casperjs.org/api.html#casper.sendKeys) to send native keyboard events to the element matching a given selector +- added [`Casper.getFormValues()`](http://casperjs.org/api.html#casper.getFormValues) to check for the field values of a given form +- added [`Tester.assertTextDoesntExist()`](http://casperjs.org/api.html#tester.assertTextDoesntExist) +- added `Tester.assertFalse()` as an alias of `Tester.assertNot()` +- added `page.resource.requested` and `page.resource.received` events +- added [`translate.js`](https://github.com/n1k0/casperjs/tree/master/samples/translate.js) and [`translate.coffee`](https://github.com/n1k0/casperjs/tree/master/samples/translate.coffee) samples + 2012-10-31, v1.0.0-RC4 ---------------------- diff --git a/zephyr/tests/frontend/casperjs/CONTRIBUTORS.md b/zephyr/tests/frontend/casperjs/CONTRIBUTORS.md index f17b71190c..6ff2e55f4e 100644 --- a/zephyr/tests/frontend/casperjs/CONTRIBUTORS.md +++ b/zephyr/tests/frontend/casperjs/CONTRIBUTORS.md @@ -3,44 +3,51 @@ You can check out the [contribution graphs on github](https://github.com/n1k0/casperjs/graphs/contributors). ``` -$ git shortlog -s -n - 689 Nicolas Perriault - 14 oncletom - 14 Brikou CARRE - 8 hannyu - 6 Chris Lorenzo - 4 pborreli - 4 nrabinowitz - 3 Andrew Childs - 3 Solomon White - 3 reina.sweet - 2 Reina Sweet - 2 Jason Funk - 2 Michael Geers - 2 Julien Moulin - 2 Donovan Hutchinson - 2 Clochix - 1 Marcel Duran - 1 Mathieu Agopian - 1 Mehdi Kabab - 1 Mikko Peltonen - 1 Pascal Borreli - 1 Rafael - 1 Rafael Garcia - 1 Raphaël Benitte - 1 Andrew de Andrade - 1 Tim Bunce - 1 Victor Yap - 1 alfetopito - 1 Christophe Benz - 1 jean-philippe serafin - 1 Chris Winters - 1 Ben Lowery - 1 Jan Pochyla - 1 Harrison Reiser - 1 Julian Gruber - 1 Justine Tunney - 1 KaroDidi - 1 Leandro Boscariol - 1 Maisons du monde +$ git shortlog -s -n | cut -c8- +Nicolas Perriault +Brikou CARRE +oncletom +hannyu +Chris Lorenzo +Victor Yap +nrabinowitz +pborreli +Rob Barreca +Andrew Childs +Solomon White +reina.sweet +Dave Lee +Reina Sweet +Elmar Langholz +Jason Funk +Donovan Hutchinson +Julien Moulin +Michael Geers +Jan Schaumann +Clochix +Raphaël Benitte +Tim Bunce +alfetopito +jean-philippe serafin +snkashis +Andrew de Andrade +Ben Lowery +Chris Winters +Christophe Benz +Harrison Reiser +Jan Pochyla +Jan-Martin Fruehwacht +Julian Gruber +Justin Slattery +Justine Tunney +KaroDidi +Leandro Boscariol +Maisons du monde +Marcel Duran +Mathieu Agopian +Mehdi Kabab +Mikko Peltonen +Pascal Borreli +Rafael +Rafael Garcia ``` diff --git a/zephyr/tests/frontend/casperjs/batchbin/casperjs.bat b/zephyr/tests/frontend/casperjs/batchbin/casperjs.bat index 6f47552d18..dd86a98f26 100644 --- a/zephyr/tests/frontend/casperjs/batchbin/casperjs.bat +++ b/zephyr/tests/frontend/casperjs/batchbin/casperjs.bat @@ -1,22 +1,5 @@ @ECHO OFF -set CASPER_PATH=%~dp0..\ -set CASPER_BIN=%CASPER_PATH%bin\ - -set PHANTOMJS_NATIVE_ARGS=(--cookies-file --config --debug --disk-cache --ignore-ssl-errors --load-images --load-plugins --local-storage-path --local-storage-quota --local-to-remote-url-access --max-disk-cache-size --output-encoding --proxy --proxy-auth --proxy-type --remote-debugger-port --remote-debugger-autorun --script-encoding --web-security) - -set PHANTOM_ARGS= -set CASPER_ARGS= - -:Loop -if "%1"=="" goto Continue - set IS_PHANTOM_ARG=0 - for %%i in %PHANTOMJS_NATIVE_ARGS% do ( - if "%%i"=="%1" set IS_PHANTOM_ARG=1 - ) - if %IS_PHANTOM_ARG%==0 set CASPER_ARGS=%CASPER_ARGS% %1 - if %IS_PHANTOM_ARG%==1 set PHANTOM_ARGS=%PHANTOM_ARGS% %1 -shift -goto Loop -:Continue - -call phantomjs%PHANTOM_ARGS% %CASPER_BIN%bootstrap.js --casper-path=%CASPER_PATH% --cli%CASPER_ARGS% \ No newline at end of file +set CASPER_PATH=%~dp0.. +set CASPER_BIN=%CASPER_PATH%\bin\ +set ARGV=%* +call phantomjs "%CASPER_BIN%bootstrap.js" --casper-path="%CASPER_PATH%" --cli %ARGV% \ No newline at end of file diff --git a/zephyr/tests/frontend/casperjs/bin/bootstrap.js b/zephyr/tests/frontend/casperjs/bin/bootstrap.js index d2816942a6..2e40c6109b 100755 --- a/zephyr/tests/frontend/casperjs/bin/bootstrap.js +++ b/zephyr/tests/frontend/casperjs/bin/bootstrap.js @@ -33,14 +33,21 @@ if (!phantom) { console.error('CasperJS needs to be executed in a PhantomJS environment http://phantomjs.org/'); - phantom.exit(1); } -if (phantom.version.major === 1 && phantom.version.minor < 6) { - console.error('CasperJS needs at least PhantomJS v1.6.0 or later.'); +if (phantom.version.major === 1 && phantom.version.minor < 7) { + console.error('CasperJS needs at least PhantomJS v1.7 or later.'); phantom.exit(1); } else { - bootstrap(window); + try { + bootstrap(window); + } catch (e) { + console.error(e); + (e.stackArray || []).forEach(function(entry) { + console.error(' In ' + entry.sourceURL + ':' + entry.line); + }); + phantom.exit(1); + } } // Polyfills @@ -106,10 +113,13 @@ function patchRequire(require, requireDirs) { fileGuesses.push.apply(fileGuesses, [ testPath, testPath + '.js', + testPath + '.json', testPath + '.coffee', fs.pathJoin(testPath, 'index.js'), + fs.pathJoin(testPath, 'index.json'), fs.pathJoin(testPath, 'index.coffee'), fs.pathJoin(testPath, 'lib', fs.basename(testPath) + '.js'), + fs.pathJoin(testPath, 'lib', fs.basename(testPath) + '.json'), fs.pathJoin(testPath, 'lib', fs.basename(testPath) + '.coffee') ]); }); @@ -120,16 +130,25 @@ function patchRequire(require, requireDirs) { } } if (!file) { - throw new Error("CasperJS couldn't find module " + path); + throw new window.CasperError("CasperJS couldn't find module " + path); } if (file in requireCache) { return requireCache[file].exports; } + if (/\.json/i.test(file)) { + var parsed = JSON.parse(fs.read(file)); + requireCache[file] = parsed; + return parsed; + } var scriptCode = (function getScriptCode(file) { var scriptCode = fs.read(file); if (/\.coffee$/i.test(file)) { /*global CoffeeScript*/ - scriptCode = CoffeeScript.compile(scriptCode); + try { + scriptCode = CoffeeScript.compile(scriptCode); + } catch (e) { + throw new Error('Unable to compile coffeescript:' + e); + } } return scriptCode; })(file); @@ -137,8 +156,14 @@ function patchRequire(require, requireDirs) { try { fn(file, _require, module, module.exports); } catch (e) { - var error = new window.CasperError('__mod_error(' + path + '):: ' + e); + var error = new window.CasperError('__mod_error(' + path + ':' + e.line + '):: ' + e); error.file = file; + error.line = e.line; + error.stack = e.stack; + error.stackArray = JSON.parse(JSON.stringify(e.stackArray)); + if (error.stackArray.length > 0) { + error.stackArray[0].sourceURL = file; + } throw error; } requireCache[file] = module; @@ -150,9 +175,20 @@ function patchRequire(require, requireDirs) { function bootstrap(global) { "use strict"; - var phantomArgs = require('system').args; + /** + * Hooks in default phantomjs error handler to print a hint when a possible + * casperjs command misuse is detected. + * + */ + phantom.onError = function onPhantomError(msg, trace) { + phantom.defaultErrorHandler.apply(phantom, arguments); + if (msg.indexOf("ReferenceError: Can't find variable: casper") === 0) { + console.error('Hint: you may want to use the `casperjs test` command.'); + } + }; + /** * Loads and initialize the CasperJS environment. */ @@ -230,7 +266,7 @@ function bootstrap(global) { throw new global.CasperError('Cannot read package file contents: ' + e); } parts = pkg.version.trim().split("."); - if (parts < 3) { + if (parts.length < 3) { throw new global.CasperError("Invalid version number"); } patchPart = parts[2].split('-'); @@ -264,20 +300,21 @@ function bootstrap(global) { */ phantom.initCasperCli = function initCasperCli() { var fs = require("fs"); + var baseTestsPath = fs.pathJoin(phantom.casperPath, 'tests'); if (!!phantom.casperArgs.options.version) { console.log(phantom.casperVersion.toString()); - phantom.exit(0); + return phantom.exit(); } else if (phantom.casperArgs.get(0) === "test") { - phantom.casperScript = fs.absolute(fs.pathJoin(phantom.casperPath, 'tests', 'run.js')); + phantom.casperScript = fs.absolute(fs.pathJoin(baseTestsPath, 'run.js')); phantom.casperTest = true; phantom.casperArgs.drop("test"); } else if (phantom.casperArgs.get(0) === "selftest") { - phantom.casperScript = fs.absolute(fs.pathJoin(phantom.casperPath, 'tests', 'run.js')); - phantom.casperSelfTest = true; - phantom.casperArgs.options.includes = fs.pathJoin(phantom.casperPath, 'tests', 'selftest.js'); + phantom.casperScript = fs.absolute(fs.pathJoin(baseTestsPath, 'run.js')); + phantom.casperSelfTest = phantom.casperTest = true; + phantom.casperArgs.options.includes = fs.pathJoin(baseTestsPath, 'selftest.js'); if (phantom.casperArgs.args.length <= 1) { - phantom.casperArgs.args.push(fs.pathJoin(phantom.casperPath, 'tests', 'suites')); + phantom.casperArgs.args.push(fs.pathJoin(baseTestsPath, 'suites')); } phantom.casperArgs.drop("selftest"); } else if (phantom.casperArgs.args.length === 0 || !!phantom.casperArgs.options.help) { @@ -287,25 +324,30 @@ function bootstrap(global) { phantom.casperVersion.toString(), phantom.casperPath, phantomVersion)); console.log(fs.read(fs.pathJoin(phantom.casperPath, 'bin', 'usage.txt'))); - phantom.exit(0); + return phantom.exit(0); } - if (!phantom.casperScript) { phantom.casperScript = phantom.casperArgs.get(0); } - if (phantom.casperScript) { - if (!fs.isFile(phantom.casperScript)) { - console.error('Unable to open file: ' + phantom.casperScript); - phantom.exit(1); - } else { - // filter out the called script name from casper args - phantom.casperArgs.drop(phantom.casperScript); + if (!fs.isFile(phantom.casperScript)) { + console.error('Unable to open file: ' + phantom.casperScript); + return phantom.exit(1); + } - // passed casperjs script execution - phantom.injectJs(phantom.casperScript); - } + // filter out the called script name from casper args + phantom.casperArgs.drop(phantom.casperScript); + + // passed casperjs script execution + var injected = false; + try { + injected = phantom.injectJs(phantom.casperScript); + } catch (e) { + throw new global.CasperError('Error loading script ' + phantom.casperScript + ': ' + e); + } + if (!injected) { + throw new global.CasperError('Unable to load script ' + phantom.casperScript + '; check file syntax'); } }; diff --git a/zephyr/tests/frontend/casperjs/bin/casperjs b/zephyr/tests/frontend/casperjs/bin/casperjs index c22baf0f38..16c1be57ef 100755 --- a/zephyr/tests/frontend/casperjs/bin/casperjs +++ b/zephyr/tests/frontend/casperjs/bin/casperjs @@ -1,8 +1,17 @@ #!/usr/bin/env python +import json import os +import subprocess import sys +def test_cmd(cmd): + try: + return subprocess.check_output([__file__] + cmd.split(' ')) + except subprocess.CalledProcessError as err: + sys.stderr.write('FAIL: %s\n' % ' '.join(err.cmd)) + sys.stderr.write(' %s\n' % err.output) + sys.exit(1) def resolve(path): if os.path.islink(path): @@ -32,9 +41,25 @@ PHANTOMJS_NATIVE_ARGS = [ 'web-security', ] CASPER_ARGS = [] +CASPER_PATH = os.path.abspath(os.path.join(os.path.dirname(resolve(__file__)), '..')) PHANTOMJS_ARGS = [] +SYS_ARGS = sys.argv[1:] -for arg in sys.argv[1:]: +if len(SYS_ARGS) and SYS_ARGS[0] == '__selfcommandtest': + print('Starting Python executable tests...') + pkg_version = json.loads(open('package.json').read()).get('version') + scripts_path = os.path.join(CASPER_PATH, 'tests', 'commands') + assert(test_cmd('--help').find(pkg_version) > -1) + assert(test_cmd('--version').strip() == pkg_version) + assert(test_cmd(os.path.join(scripts_path, 'script.js')) == 'it works\n') + test_output = test_cmd('test --no-colors ' + os.path.join(scripts_path, 'mytest.js')) + assert('PASS ok1' in test_output) + assert('PASS ok2' in test_output) + assert('PASS ok3' in test_output) + print('Python executable tests passed.') + sys.exit(0) + +for arg in SYS_ARGS: found = False for native in PHANTOMJS_NATIVE_ARGS: if arg.startswith('--%s' % native): @@ -43,7 +68,6 @@ for arg in sys.argv[1:]: if not found: CASPER_ARGS.append(arg) -CASPER_PATH = os.path.abspath(os.path.join(os.path.dirname(resolve(__file__)), '..')) CASPER_COMMAND = os.environ.get('PHANTOMJS_EXECUTABLE', 'phantomjs').split(' ') CASPER_COMMAND.extend(PHANTOMJS_ARGS) CASPER_COMMAND.extend([ diff --git a/zephyr/tests/frontend/casperjs/bin/usage.txt b/zephyr/tests/frontend/casperjs/bin/usage.txt index cc94438ae7..3b8595d22f 100644 --- a/zephyr/tests/frontend/casperjs/bin/usage.txt +++ b/zephyr/tests/frontend/casperjs/bin/usage.txt @@ -1,10 +1,11 @@ Usage: casperjs [options] script.[js|coffee] [script argument [script argument ...]] casperjs [options] test [test path [test path ...]] + casperjs [options] selftest Options: --help Prints this help --version Prints out CasperJS version -Read the docs http://casperjs.org/ \ No newline at end of file +Read the docs http://casperjs.org/ diff --git a/zephyr/tests/frontend/casperjs/casperjs.gemspec b/zephyr/tests/frontend/casperjs/casperjs.gemspec index 301488431c..abe051e662 100644 --- a/zephyr/tests/frontend/casperjs/casperjs.gemspec +++ b/zephyr/tests/frontend/casperjs/casperjs.gemspec @@ -19,5 +19,5 @@ high-level functions, methods & syntaxic sugar for doing common tasks." s.bindir = "rubybin" s.executables = [ "casperjs" ] s.license = "MIT" - s.requirements = [ "PhantomJS v1.6" ] + s.requirements = [ "PhantomJS v1.7" ] end diff --git a/zephyr/tests/frontend/casperjs/modules/casper.js b/zephyr/tests/frontend/casperjs/modules/casper.js index 1855d08876..8555f024fa 100644 --- a/zephyr/tests/frontend/casperjs/modules/casper.js +++ b/zephyr/tests/frontend/casperjs/modules/casper.js @@ -35,6 +35,7 @@ var events = require('events'); var fs = require('fs'); var http = require('http'); var mouse = require('mouse'); +var pagestack = require('pagestack'); var qs = require('querystring'); var tester = require('tester'); var utils = require('utils'); @@ -75,7 +76,7 @@ exports.selectXPath = selectXPath; */ var Casper = function Casper(options) { "use strict"; - /*jshint maxstatements:30*/ + /*jshint maxstatements:40*/ // init & checks if (!(this instanceof Casper)) { return new Casper(options); @@ -110,6 +111,7 @@ var Casper = function Casper(options) { localToRemoteUrlAccessEnabled: true, userAgent: defaultUserAgent }, + remoteScripts: [], stepTimeout: null, timeout: null, verbose: false, @@ -138,6 +140,7 @@ var Casper = function Casper(options) { this.mouse = mouse.create(this); this.page = null; this.pendingWait = false; + this.popups = pagestack.create(); this.requestUrl = 'about:blank'; this.resources = []; this.result = { @@ -154,12 +157,14 @@ var Casper = function Casper(options) { this.initErrorHandler(); this.on('error', function(msg, backtrace) { + if (msg === this.test.SKIP_MESSAGE) { + return; + } var c = this.getColorizer(); var match = /^(.*): __mod_error(.*):: (.*)/.exec(msg); var notices = []; if (match && match.length === 4) { notices.push(' in module ' + match[2]); - notices.push(' NOTICE: errors within modules cannot be backtraced yet.'); msg = match[3]; } console.error(c.colorize(msg, 'RED_BAR', 80)); @@ -217,8 +222,8 @@ Casper.prototype.back = function back() { Casper.prototype.base64encode = function base64encode(url, method, data) { "use strict"; return this.evaluate(function _evaluate(url, method, data) { - return window.__utils__.getBase64(url, method, data); - }, { url: url, method: method, data: data }); + return __utils__.getBase64(url, method, data); + }, url, method, data); }; /** @@ -382,7 +387,14 @@ Casper.prototype.clear = function clear() { Casper.prototype.click = function click(selector) { "use strict"; this.checkStarted(); - return this.mouseEvent('click', selector); + var success = this.mouseEvent('click', selector); + this.evaluate(function(selector) { + var element = __utils__.findOne(selector); + if (element) { + element.focus(); + } + }, selector); + return success; }; /** @@ -402,6 +414,32 @@ Casper.prototype.clickLabel = function clickLabel(label, tag) { return this.click(selector); }; +/** + * Configures HTTP authentication parameters. Will try parsing auth credentials from + * the passed location first, then check for configured settings if any. + * + * @param String location Requested url + * @param Object settings Request settings + * @return Casper + */ +Casper.prototype.configureHttpAuth = function configureHttpAuth(location, settings) { + "use strict"; + var username, password, httpAuthMatch = location.match(/^https?:\/\/(.+):(.+)@/i); + this.checkStarted(); + if (httpAuthMatch) { + this.page.settings.userName = httpAuthMatch[1]; + this.page.settings.password = httpAuthMatch[2]; + } else if (utils.isObject(settings) && settings.username) { + this.page.settings.userName = settings.username; + this.page.settings.password = settings.password; + } else { + return; + } + this.emit('http.auth', username, password); + this.log("Setting HTTP authentication for user " + username, "info"); + return this; +}; + /** * Creates a step definition. * @@ -455,13 +493,13 @@ Casper.prototype.debugPage = function debugPage() { */ Casper.prototype.die = function die(message, status) { "use strict"; - this.checkStarted(); this.result.status = "error"; this.result.time = new Date().getTime() - this.startTime; if (!utils.isString(message) || !message.length) { message = "Suite explicitely interrupted without any message given."; } this.log(message, "error"); + this.echo(message, "ERROR"); this.emit('die', message, status); if (utils.isFunction(this.options.onDie)) { this.options.onDie.call(this, this, message, status); @@ -549,13 +587,7 @@ Casper.prototype.echo = function echo(text, style, pad) { * document.querySelector('#username').value = username; * document.querySelector('#password').value = password; * document.querySelector('#submit').click(); - * }, { - * username: 'Bazoonga', - * password: 'baz00nga' - * }) - * - * FIXME: waiting for a patch of PhantomJS to allow direct passing of - * arguments to the function. + * }, 'Bazoonga', 'baz00nga'); * * @param Function fn The function to be evaluated within current page DOM * @param Object context Object containing the parameters to inject into the function @@ -565,15 +597,27 @@ Casper.prototype.echo = function echo(text, style, pad) { Casper.prototype.evaluate = function evaluate(fn, context) { "use strict"; this.checkStarted(); + // preliminary checks + if (!utils.isFunction(fn) && !utils.isString(fn)) { // phantomjs allows functions defs as string + throw new CasperError("evaluate() only accepts functions or strings"); + } // ensure client utils are always injected this.injectClientUtils(); // function context - context = utils.isObject(context) ? context : {}; - // the way this works is kept for BC with older casperjs versions - var args = Object.keys(context).map(function(arg) { - return context[arg]; - }); - return this.page.evaluate.apply(this.page, [fn].concat(args)); + if (arguments.length === 1) { + return this.page.evaluate(fn); + } else if (arguments.length === 2) { + // check for closure signature if it matches context + if (utils.isObject(context) && eval(fn).length === Object.keys(context).length) { + context = utils.objectValues(context); + } else { + context = [context]; + } + } else { + // phantomjs-style signature + context = [].slice.call(arguments).slice(1); + } + return this.page.evaluate.apply(this.page, [fn].concat(context)); }; /** @@ -606,8 +650,8 @@ Casper.prototype.exists = function exists(selector) { "use strict"; this.checkStarted(); return this.evaluate(function _evaluate(selector) { - return window.__utils__.exists(selector); - }, { selector: selector }); + return __utils__.exists(selector); + }, selector); }; /** @@ -633,8 +677,8 @@ Casper.prototype.fetchText = function fetchText(selector) { "use strict"; this.checkStarted(); return this.evaluate(function _evaluate(selector) { - return window.__utils__.fetchText(selector); - }, { selector: selector }); + return __utils__.fetchText(selector); + }, selector); }; /** @@ -653,11 +697,8 @@ Casper.prototype.fill = function fill(selector, vals, submit) { } this.emit('fill', selector, vals, submit); var fillResults = this.evaluate(function _evaluate(selector, values) { - return window.__utils__.fill(selector, values); - }, { - selector: selector, - values: vals - }); + return __utils__.fill(selector, values); + }, selector, vals); if (!fillResults) { throw new CasperError("Unable to fill form"); } else if (fillResults.errors.length > 0) { @@ -681,17 +722,17 @@ Casper.prototype.fill = function fill(selector, vals, submit) { // Form submission? if (submit) { this.evaluate(function _evaluate(selector) { - var form = window.__utils__.findOne(selector); + var form = __utils__.findOne(selector); var method = (form.getAttribute('method') || "GET").toUpperCase(); var action = form.getAttribute('action') || "unknown"; - window.__utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info'); + __utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info'); if (typeof form.submit === "function") { form.submit(); } else { // http://www.spiration.co.uk/post/1232/Submit-is-not-a-function form.submit.click(); } - }, { selector: selector }); + }, selector); } }; @@ -732,7 +773,7 @@ Casper.prototype.getPageContent = function getPageContent() { this.checkStarted(); var contentType = utils.getPropertyPath(this, 'currentResponse.contentType'); if (!utils.isString(contentType)) { - return this.page.content; + return this.page.frameContent; } // for some reason webkit/qtwebkit will always enclose body contents within html tags var sanitizedHtml = this.evaluate(function checkHtml() { @@ -742,7 +783,7 @@ Casper.prototype.getPageContent = function getPageContent() { return __utils__.findOne('body pre').textContent.trim(); } }); - return sanitizedHtml ? sanitizedHtml : this.page.content; + return sanitizedHtml ? sanitizedHtml : this.page.frameContent; }; /** @@ -772,12 +813,13 @@ Casper.prototype.getCurrentUrl = function getCurrentUrl() { * @param String attribute The attribute name to lookup * @return String The requested DOM element attribute value */ -Casper.prototype.getElementAttribute = Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) { +Casper.prototype.getElementAttribute = +Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) { "use strict"; this.checkStarted(); return this.evaluate(function _evaluate(selector, attribute) { return document.querySelector(selector).getAttribute(attribute); - }, { selector: selector, attribute: attribute }); + }, selector, attribute); }; /** @@ -793,14 +835,31 @@ Casper.prototype.getElementBounds = function getElementBounds(selector) { throw new CasperError("No element matching selector found: " + selector); } var clipRect = this.evaluate(function _evaluate(selector) { - return window.__utils__.getElementBounds(selector); - }, { selector: selector }); + return __utils__.getElementBounds(selector); + }, selector); if (!utils.isClipRect(clipRect)) { throw new CasperError('Could not fetch boundaries for element matching selector: ' + selector); } return clipRect; }; +/** + * Retrieves information about the node matching the provided selector. + * + * @param String|Objects selector CSS3/XPath selector + * @return Object + */ +Casper.prototype.getElementInfo = function getElementInfo(selector) { + "use strict"; + this.checkStarted(); + if (!this.exists(selector)) { + throw new CasperError(f("Cannot get informations from %s: element not found.", selector)); + } + return this.evaluate(function(selector) { + return __utils__.getElementInfo(selector); + }, selector); +}; + /** * Retrieves boundaries for all the DOM elements matching the provided DOM CSS3/XPath selector. * @@ -814,8 +873,25 @@ Casper.prototype.getElementsBounds = function getElementBounds(selector) { throw new CasperError("No element matching selector found: " + selector); } return this.evaluate(function _evaluate(selector) { - return window.__utils__.getElementsBounds(selector); - }, { selector: selector }); + return __utils__.getElementsBounds(selector); + }, selector); +}; + +/** + * Retrieves a given form all of its field values. + * + * @param String selector A DOM CSS3/XPath selector + * @return Object + */ +Casper.prototype.getFormValues = function(selector) { + "use strict"; + this.checkStarted(); + if (!this.exists(selector)) { + throw new CasperError(f('Form matching selector "%s" not found', selector)); + } + return this.evaluate(function(selector) { + return __utils__.getFormValues(selector); + }, selector); }; /** @@ -833,19 +909,17 @@ Casper.prototype.getGlobal = function getGlobal(name) { result.value = JSON.stringify(window[name]); } catch (e) { var message = f("Unable to JSON encode window.%s: %s", name, e); - window.__utils__.log(message, "error"); + __utils__.log(message, "error"); result.error = message; } return result; - }, {'name': name}); - if (typeof result !== "object") { + }, name); + if (!utils.isObject(result)) { throw new CasperError(f('Could not retrieve global value for "%s"', name)); } else if ('error' in result) { throw new CasperError(result.error); } else if (utils.isString(result.value)) { return JSON.parse(result.value); - } else { - return undefined; } }; @@ -861,7 +935,7 @@ Casper.prototype.getHTML = function getHTML(selector, outer) { "use strict"; this.checkStarted(); if (!selector) { - return this.page.content; + return this.page.frameContent; } if (!this.exists(selector)) { throw new CasperError("No element matching selector found: " + selector); @@ -869,7 +943,7 @@ Casper.prototype.getHTML = function getHTML(selector, outer) { return this.evaluate(function getSelectorHTML(selector, outer) { var element = __utils__.findOne(selector); return outer ? element.outerHTML : element.innerHTML; - }, { selector: selector, outer: !!outer }); + }, selector, !!outer); }; /** @@ -892,6 +966,7 @@ Casper.prototype.getTitle = function getTitle() { */ Casper.prototype.handleReceivedResource = function(resource) { "use strict"; + /*jshint maxstatements:20*/ if (resource.stage !== "end") { return; } @@ -902,6 +977,7 @@ Casper.prototype.handleReceivedResource = function(resource) { this.currentHTTPStatus = null; this.currentResponse = undefined; if (utils.isHTTPResource(resource)) { + this.emit('page.resource.received', resource); this.currentResponse = resource; this.currentHTTPStatus = resource.status; this.emit('http.status.' + resource.status, resource); @@ -931,7 +1007,7 @@ Casper.prototype.initErrorHandler = function initErrorHandler() { }; /** - * Injects configured client scripts. + * Injects configured local client scripts. * * @return Casper */ @@ -965,7 +1041,7 @@ Casper.prototype.injectClientUtils = function injectClientUtils() { "use strict"; this.checkStarted(); var clientUtilsInjected = this.page.evaluate(function() { - return typeof window.__utils__ === "object"; + return typeof __utils__ === "object"; }); if (true === clientUtilsInjected) { return; @@ -984,6 +1060,32 @@ Casper.prototype.injectClientUtils = function injectClientUtils() { }.toString().replace('__options', JSON.stringify(this.options))); }; +/** + * Loads and include remote client scripts to current page. + * + * @return Casper + */ +Casper.prototype.includeRemoteScripts = function includeRemoteScripts() { + "use strict"; + var numScripts = this.options.remoteScripts.length, loaded = 0; + if (numScripts === 0) { + return this; + } + this.waitStart(); + this.options.remoteScripts.forEach(function(scriptUrl) { + this.log(f("Loading remote script: %s", scriptUrl), "debug"); + this.page.includeJs(scriptUrl, function() { + loaded++; + this.log(f("Remote script %s loaded", scriptUrl), "debug"); + if (loaded === numScripts) { + this.log("All remote scripts loaded.", "debug"); + this.waitDone(); + } + }.bind(this)); + }.bind(this)); + return this; +}; + /** * Logs a message. * @@ -1041,22 +1143,18 @@ Casper.prototype.mouseEvent = function mouseEvent(type, selector) { if (!this.exists(selector)) { throw new CasperError(f("Cannot dispatch %s event on nonexistent selector: %s", type, selector)); } - var eventSuccess = this.evaluate(function(type, selector) { + if (this.evaluate(function(type, selector) { return window.__utils__.mouseEvent(type, selector); - }, { - type: type, - selector: selector - }); - if (!eventSuccess) { - // fallback onto native QtWebKit mouse events - try { - this.mouse.processEvent(type, selector); - } catch (e) { - this.log(f("Couldn't emulate '%s' event on %s: %s", type, selector, e), "error"); - return false; - } + }, type, selector)) { + return true; } - return true; + // fallback onto native QtWebKit mouse events + try { + return this.mouse.processEvent(type, selector); + } catch (e) { + this.log(f("Couldn't emulate '%s' event on %s: %s", type, selector, e), "error"); + } + return false; }; /** @@ -1066,7 +1164,7 @@ Casper.prototype.mouseEvent = function mouseEvent(type, selector) { * * - String method: The HTTP method to use * - Object data: The data to use to perform the request, eg. {foo: 'bar'} - * - Array headers: An array of request headers, eg. [{'Cache-Control': 'max-age=0'}] + * - Object headers: Custom request headers object, eg. {'Cache-Control': 'max-age=0'} * * @param String location The url to open * @param Object settings The request settings (optional) @@ -1074,16 +1172,12 @@ Casper.prototype.mouseEvent = function mouseEvent(type, selector) { */ Casper.prototype.open = function open(location, settings) { "use strict"; + /*jshint maxstatements:30*/ + var baseCustomHeaders = this.page.customHeaders, + customHeaders = settings && settings.headers || {}; this.checkStarted(); - // settings validation - if (!settings) { - settings = { - method: "get" - }; - } - if (!utils.isObject(settings)) { - throw new CasperError("open(): request settings must be an Object"); - } + settings = utils.isObject(settings) ? settings : {}; + settings.method = settings.method || "get"; // http method // taken from https://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L302 var methods = ["get", "head", "put", "post", "delete"]; @@ -1101,28 +1195,21 @@ Casper.prototype.open = function open(location, settings) { // clean location location = utils.cleanUrl(location); // current request url + this.configureHttpAuth(location, settings); this.requestUrl = this.filter('open.location', location) || location; - // http auth - if (settings.username && settings.password) { - this.setHttpAuth(settings.username, settings.password); - } else { - var httpAuthMatch = location.match(/^https?:\/\/(.+):(.+)@/i); - if (httpAuthMatch) { - var httpAuth = { - username: httpAuthMatch[1], - password: httpAuthMatch[2] - }; - this.setHttpAuth(httpAuth.username, httpAuth.password); - } - } this.emit('open', this.requestUrl, settings); this.log(f('opening url: %s, HTTP %s', this.requestUrl, settings.method.toUpperCase()), "debug"); + // reset resources + this.resources = []; + // custom headers + this.page.customHeaders = utils.mergeObjects(utils.clone(baseCustomHeaders), customHeaders); + // perfom request this.page.openUrl(this.requestUrl, { operation: settings.method, - data: settings.data, - headers: settings.headers + data: settings.data }, this.page.settings); - this.resources = []; + // revert base custom headers + this.page.customHeaders = baseCustomHeaders; return this; }; @@ -1203,8 +1290,7 @@ Casper.prototype.run = function run(onComplete, time) { "use strict"; this.checkStarted(); if (!this.steps || this.steps.length < 1) { - this.log("No steps defined, aborting", "error"); - return this; + throw new CasperError('No steps defined, aborting'); } this.log(f("Running suite: %d step%s", this.steps.length, this.steps.length > 1 ? "s" : ""), "info"); this.emit('run.start'); @@ -1232,7 +1318,7 @@ Casper.prototype.runStep = function runStep(step) { if ((self.test.currentSuiteNum + "-" + self.step) === stepNum) { self.emit('step.timeout'); if (utils.isFunction(self.options.onStepTimeout)) { - self.options.onStepTimeout.call(self, self.options.onStepTimeout, stepNum); + self.options.onStepTimeout.call(self, self.options.stepTimeout, stepNum); } } clearInterval(stepTimeoutCheckInterval); @@ -1251,22 +1337,53 @@ Casper.prototype.runStep = function runStep(step) { }; /** - * Sets HTTP authentication parameters. + * Sends keys to given element. * - * @param String username The HTTP_AUTH_USER value - * @param String password The HTTP_AUTH_PW value + * @param String selector A DOM CSS3 compatible selector + * @param String keys A string representing the sequence of char codes to send + * @param Object options Options + * @return Casper + */ +Casper.prototype.sendKeys = function(selector, keys, options) { + "use strict"; + this.checkStarted(); + options = utils.mergeObjects({ + eventType: 'keypress' + }, options || {}); + var elemInfos = this.getElementInfo(selector), + tag = elemInfos.nodeName.toLowerCase(), + type = utils.getPropertyPath(elemInfos, 'attributes.type'), + supported = ["color", "date", "datetime", "datetime-local", "email", + "hidden", "month", "number", "password", "range", "search", + "tel", "text", "time", "url", "week"]; + var isTextInput = false; + if (tag === 'textarea' || (tag === 'input' && supported.indexOf(type) !== -1)) { + // clicking on the input element brings it focus + isTextInput = true; + this.click(selector); + } + this.page.sendEvent(options.eventType, keys); + if (isTextInput) { + // remove the focus + this.evaluate(function(selector) { + __utils__.findOne(selector).blur(); + }, selector); + } + return this; +}; + +/** + * Sets current WebPage instance the credentials for HTTP authentication. + * + * @param String username + * @param String password * @return Casper */ Casper.prototype.setHttpAuth = function setHttpAuth(username, password) { "use strict"; this.checkStarted(); - if (!utils.isString(username) || !utils.isString(password)) { - throw new CasperError("Both username and password must be strings"); - } this.page.settings.userName = username; this.page.settings.password = password; - this.emit('http.auth', username, password); - this.log("Setting HTTP authentication for user " + username, "info"); return this; }; @@ -1279,10 +1396,12 @@ Casper.prototype.setHttpAuth = function setHttpAuth(username, password) { */ Casper.prototype.start = function start(location, then) { "use strict"; + /*jshint maxstatements:30*/ this.emit('starting'); this.log('Starting...', "info"); this.startTime = new Date().getTime(); this.history = []; + this.popups = pagestack.create(); this.steps = []; this.step = 0; // Option checks @@ -1290,13 +1409,8 @@ Casper.prototype.start = function start(location, then) { this.log(f("Unknown log level '%d', defaulting to 'warning'", this.options.logLevel), "warning"); this.options.logLevel = "warning"; } - // WebPage if (!utils.isWebPage(this.page)) { - if (utils.isWebPage(this.options.page)) { - this.page = this.options.page; - } else { - this.page = createPage(this); - } + this.page = this.mainPage = utils.isWebPage(this.options.page) ? this.options.page : createPage(this); } this.page.settings = utils.mergeObjects(this.page.settings, this.options.pageSettings); if (utils.isClipRect(this.options.clipRect)) { @@ -1305,8 +1419,7 @@ Casper.prototype.start = function start(location, then) { if (utils.isObject(this.options.viewportSize)) { this.page.viewportSize = this.options.viewportSize; } - this.started = true; - this.emit('started'); + // timeout handling if (utils.isNumber(this.options.timeout) && this.options.timeout > 0) { this.log(f("Execution timeout set to %dms", this.options.timeout), "info"); setTimeout(function _check(self) { @@ -1316,10 +1429,12 @@ Casper.prototype.start = function start(location, then) { } }, this.options.timeout, this); } + this.started = true; + this.emit('started'); if (utils.isString(location) && location.length > 0) { return this.thenOpen(location, utils.isFunction(then) ? then : this.createStep(function _step() { this.log("start page is loaded", "debug"); - })); + }, {skipLog: true})); } return this; }; @@ -1409,8 +1524,9 @@ Casper.prototype.thenClick = function thenClick(selector, then, fallbackToHref) Casper.prototype.thenEvaluate = function thenEvaluate(fn, context) { "use strict"; this.checkStarted(); + var args = [fn].concat([].slice.call(arguments, 1)); return this.then(function _step() { - this.evaluate(fn, context); + this.evaluate.apply(this, args); }); }; @@ -1472,8 +1588,10 @@ Casper.prototype.toString = function toString() { */ Casper.prototype.userAgent = function userAgent(agent) { "use strict"; - this.checkStarted(); - this.options.pageSettings.userAgent = this.page.settings.userAgent = agent; + this.options.pageSettings.userAgent = agent; + if (this.started && this.page) { + this.page.settings.userAgent = agent; + } return this; }; @@ -1510,8 +1628,8 @@ Casper.prototype.visible = function visible(selector) { "use strict"; this.checkStarted(); return this.evaluate(function _evaluate(selector) { - return window.__utils__.visible(selector); - }, { selector: selector }); + return __utils__.visible(selector); + }, selector); }; /** @@ -1617,6 +1735,28 @@ Casper.prototype.waitFor = function waitFor(testFx, then, onTimeout, timeout) { }); }; +/** + * Waits for a popup page having its url matching the provided pattern to be opened + * and loaded. + * + * @param String|RegExp urlPattern The popup url pattern + * @param Function then The next step function (optional) + * @param Function onTimeout Function to call on operation timeout (optional) + * @param Number timeout Timeout in milliseconds (optional) + * @return Casper + */ +Casper.prototype.waitForPopup = function waitForPopup(urlPattern, then, onTimeout, timeout) { + "use strict"; + return this.waitFor(function() { + try { + this.popups.find(urlPattern); + return true; + } catch (e) { + return false; + } + }, then, onTimeout, timeout); +}; + /** * Waits until a given resource is loaded * @@ -1656,20 +1796,24 @@ Casper.prototype.waitForSelector = function waitForSelector(selector, then, onTi }; /** - * Waits until the page contains given HTML text. + * Waits until the page contains given HTML text or matches a given RegExp. * - * @param String text Text to wait for - * @param Function then The next step to perform (optional) - * @param Function onTimeout A callback function to call on timeout (optional) - * @param Number timeout The max amount of time to wait, in milliseconds (optional) + * @param String|RegExp pattern Text or RegExp to wait for + * @param Function then The next step to perform (optional) + * @param Function onTimeout A callback function to call on timeout (optional) + * @param Number timeout The max amount of time to wait, in milliseconds (optional) * @return Casper */ -Casper.prototype.waitForText = function(text, then, onTimeout, timeout) { +Casper.prototype.waitForText = function(pattern, then, onTimeout, timeout) { "use strict"; this.checkStarted(); timeout = timeout ? timeout : this.options.waitTimeout; return this.waitFor(function _check() { - return this.getPageContent().indexOf(text) !== -1; + var content = this.getPageContent(); + if (utils.isRegExp(pattern)) { + return pattern.test(content); + } + return content.indexOf(pattern) !== -1; }, then, onTimeout, timeout); }; @@ -1730,6 +1874,73 @@ Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, on }, then, onTimeout, timeout); }; +/** + * Makes the provided frame page as the currently active one. Note that the + * active page will be reverted when finished. + * + * @param String|Number frameInfo Target frame name or number + * @param Function then Next step function + * @return Casper + */ +Casper.prototype.withFrame = function withFrame(frameInfo, then) { + "use strict"; + this.then(function _step() { + if (utils.isNumber(frameInfo)) { + if (frameInfo > this.page.childFramesCount() - 1) { + throw new CasperError(f('Frame number "%d" is out of bounds.', frameInfo)); + } + } else if (this.page.childFramesName().indexOf(frameInfo) === -1) { + throw new CasperError(f('No frame named "%s" was found.', frameInfo)); + } + // make the frame page the currently active one + this.page.switchToChildFrame(frameInfo); + }); + try { + this.then(then); + } catch (e) { + // revert to main page on error + this.warn("Error while processing frame step: " + e); + this.page.switchToMainFrame(); + throw e; + } + return this.then(function _step() { + // revert to main page + this.page.switchToMainFrame(); + }); +}; + +/** + * Makes the provided frame page as the currently active one. Note that the + * active page will be reverted when finished. + * + * @param String|RegExp|WebPage popup Target frame page information + * @param Function then Next step function + * @return Casper + */ +Casper.prototype.withPopup = function withPopup(popupInfo, then) { + "use strict"; + this.then(function _step() { + var popupPage = this.popups.find(popupInfo); + if (!utils.isFunction(then)) { + throw new CasperError("withPopup() requires a step function."); + } + // make the popup page the currently active one + this.page = popupPage; + }); + try { + this.then(then); + } catch (e) { + // revert to main page on error + this.log("error while processing popup step: " + e, "error"); + this.page = this.mainPage; + throw e; + } + return this.then(function _step() { + // revert to main page + this.page = this.mainPage; + }); +}; + /** * Changes the current page zoom factor. * @@ -1771,6 +1982,7 @@ exports.Casper = Casper; * @return WebPage */ function createPage(casper) { + /*jshint maxstatements:20*/ "use strict"; var page = require('webpage').create(); page.onAlert = function onAlert(message) { @@ -1781,17 +1993,21 @@ function createPage(casper) { } }; page.onConfirm = function onConfirm(message) { - return casper.filter('page.confirm', message) || true; + if ('page.confirm' in casper._filters) { + return casper.filter('page.confirm', message); + } + return true; }; page.onConsoleMessage = function onConsoleMessage(msg) { // client utils casper console message - var consoleTest = /^\[casper\.echo\]\s?(.*)/.exec(msg); + var consoleTest = /^\[casper\.echo\]\s?([\s\S]*)/.exec(msg); if (consoleTest && consoleTest.length === 2) { casper.echo(consoleTest[1]); return; // don't trigger remote.message event for these } // client utils log messages - var logLevel = "info", logTest = /^\[casper:(\w+)\]\s?(.*)/.exec(msg); + var logLevel = "info", + logTest = /^\[casper:(\w+)\]\s?([\s\S]*)/m.exec(msg); if (logTest && logTest.length === 3) { logLevel = logTest[1]; msg = logTest[2]; @@ -1803,9 +2019,9 @@ function createPage(casper) { casper.emit('page.error', msg, trace); }; page.onInitialized = function onInitialized() { - casper.emit('page.initialized', this); + casper.emit('page.initialized', page); if (utils.isFunction(casper.options.onPageInitialized)) { - this.log("Post-configuring WebPage instance", "debug"); + casper.log("Post-configuring WebPage instance", "debug"); casper.options.onPageInitialized.call(casper, page); } }; @@ -1814,6 +2030,7 @@ function createPage(casper) { casper.emit('load.started'); }; page.onLoadFinished = function onLoadFinished(status) { + /*jshint maxstatements:20*/ if (status !== "success") { casper.emit('load.failed', { status: status, @@ -1831,7 +2048,10 @@ function createPage(casper) { casper.options.onLoadError.call(casper, casper, casper.requestUrl, status); } } + // local client scripts casper.injectClientScripts(); + // remote client scripts + casper.includeRemoteScripts(); // Client-side utils injection casper.injectClientUtils(); // history @@ -1847,6 +2067,17 @@ function createPage(casper) { } casper.emit('navigation.requested', url, navigationType, navigationLocked, isMainFrame); }; + page.onPageCreated = function onPageCreated(popupPage) { + casper.emit('popup.created', popupPage); + popupPage.onLoadFinished = function onLoadFinished() { + casper.popups.push(popupPage); + casper.emit('popup.loaded', popupPage); + }; + popupPage.onClosing = function onClosing(closedPopup) { + casper.popups.clean(closedPopup); + casper.emit('popup.closed', closedPopup); + }; + }; page.onPrompt = function onPrompt(message, value) { return casper.filter('page.prompt', message, value); }; @@ -1860,6 +2091,9 @@ function createPage(casper) { }; page.onResourceRequested = function onResourceRequested(request) { casper.emit('resource.requested', request); + if (request.url === casper.requestUrl) { + casper.emit('page.resource.requested', request); + } if (utils.isFunction(casper.options.onResourceRequested)) { casper.options.onResourceRequested.call(casper, casper, request); } diff --git a/zephyr/tests/frontend/casperjs/modules/clientutils.js b/zephyr/tests/frontend/casperjs/modules/clientutils.js index 2582186373..45cf3df81c 100644 --- a/zephyr/tests/frontend/casperjs/modules/clientutils.js +++ b/zephyr/tests/frontend/casperjs/modules/clientutils.js @@ -41,6 +41,7 @@ * Casper client-side helpers. */ exports.ClientUtils = function ClientUtils(options) { + /*jshint maxstatements:40*/ // private members var BASE64_ENCODE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var BASE64_DECODE_CHARS = new Array( @@ -200,6 +201,7 @@ * @return Object An object containing setting result for each field, including file uploads */ this.fill = function fill(form, vals) { + /*jshint maxcomplexity:8*/ var out = { errors: [], fields: [], @@ -260,7 +262,7 @@ try { var pSelector = this.processSelector(selector); if (pSelector.type === 'xpath') { - return this.getElementsByXPath(pSelector.path); + return this.getElementsByXPath(pSelector.path, scope); } else { return scope.querySelectorAll(pSelector.path); } @@ -281,7 +283,7 @@ try { var pSelector = this.processSelector(selector); if (pSelector.type === 'xpath') { - return this.getElementByXPath(pSelector.path); + return this.getElementByXPath(pSelector.path, scope); } else { return scope.querySelector(pSelector.path); } @@ -390,14 +392,43 @@ } }; + /** + * Retrieves information about the node matching the provided selector. + * + * @param String|Object selector CSS3/XPath selector + * @return Object + */ + this.getElementInfo = function getElementInfo(selector) { + var element = this.findOne(selector); + var bounds = this.getElementBounds(selector); + var attributes = {}; + [].forEach.call(element.attributes, function(attr) { + attributes[attr.name.toLowerCase()] = attr.value; + }); + return { + nodeName: element.nodeName.toLowerCase(), + attributes: attributes, + tag: element.outerHTML, + html: element.innerHTML, + text: element.innerText, + x: bounds.left, + y: bounds.top, + width: bounds.width, + height: bounds.height, + visible: this.visible(selector) + }; + }; + /** * Retrieves a single DOM element matching a given XPath expression. * - * @param String expression The XPath expression + * @param String expression The XPath expression + * @param HTMLElement|null scope Element to search child elements within * @return HTMLElement or null */ - this.getElementByXPath = function getElementByXPath(expression) { - var a = document.evaluate(expression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + this.getElementByXPath = function getElementByXPath(expression, scope) { + scope = scope || this.options.scope; + var a = document.evaluate(expression, scope, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (a.snapshotLength > 0) { return a.snapshotItem(0); } @@ -406,12 +437,14 @@ /** * Retrieves all DOM elements matching a given XPath expression. * - * @param String expression The XPath expression + * @param String expression The XPath expression + * @param HTMLElement|null scope Element to search child elements within * @return Array */ - this.getElementsByXPath = function getElementsByXPath(expression) { + this.getElementsByXPath = function getElementsByXPath(expression, scope) { + scope = scope || this.options.scope; var nodes = []; - var a = document.evaluate(expression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + var a = document.evaluate(expression, scope, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for (var i = 0; i < a.snapshotLength; i++) { nodes.push(a.snapshotItem(i)); } @@ -445,7 +478,7 @@ if (type === 'radio') { var value; [].forEach.call(inputs, function(radio) { - value = radio.checked ? radio.value : undefined; + value = radio.checked ? radio.value : value; }); return value; } else if (type === 'checkbox') { @@ -466,6 +499,25 @@ } }; + /** + * Retrieves a given form all of its field values. + * + * @param String selector A DOM CSS3/XPath selector + * @return Object + */ + this.getFormValues = function getFormValues(selector) { + var form = this.findOne(selector); + var values = {}; + var self = this; + [].forEach.call(form.elements, function(element) { + var name = element.getAttribute('name'); + if (name && !values[name]) { + values[name] = self.getFieldValue(name); + } + }); + return values; + }; + /** * Logs a message. Will format the message a way CasperJS will be able * to log phantomjs side. @@ -569,24 +621,21 @@ * Performs an AJAX request. * * @param String url Url. - * @param String method HTTP method. + * @param String method HTTP method (default: GET). * @param Object data Request parameters. * @param Boolean async Asynchroneous request? (default: false) * @return String Response text. */ this.sendAJAX = function sendAJAX(url, method, data, async) { - var xhr = new XMLHttpRequest(), dataString = ""; - if (typeof method !== "string" || ["GET", "POST"].indexOf(method.toUpperCase()) === -1) { - method = "GET"; - } else { - method = method.toUpperCase(); - } + var xhr = new XMLHttpRequest(), + dataString = "", + dataList = []; + method = method && method.toUpperCase() || "GET"; xhr.open(method, url, !!async); - this.log("getBinary(): Using HTTP method: '" + method + "'", "debug"); + this.log("sendAJAX(): Using HTTP method: '" + method + "'", "debug"); xhr.overrideMimeType("text/plain; charset=x-user-defined"); if (method === "POST") { if (typeof data === "object") { - var dataList = []; for (var k in data) { dataList.push(encodeURIComponent(k) + "=" + encodeURIComponent(data[k].toString())); } @@ -595,7 +644,7 @@ } else if (typeof data === "string") { dataString = data; } - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); } xhr.send(method === "POST" ? dataString : null); return xhr.responseText; @@ -609,6 +658,7 @@ * @param mixed value The field value to set */ this.setField = function setField(field, value) { + /*jshint maxcomplexity:99 */ var logValue, fields, out; value = logValue = (value || ""); if (field instanceof NodeList) { @@ -694,10 +744,14 @@ out = 'Unsupported field type: ' + nodeName; break; } - // firing the `change` event - var changeEvent = document.createEvent("HTMLEvents"); - changeEvent.initEvent('change', true, true); - field.dispatchEvent(changeEvent); + + // firing the `change` and `input` events + ['change', 'input'].forEach(function(name) { + var event = document.createEvent("HTMLEvents"); + event.initEvent(name, true, true); + field.dispatchEvent(event); + }); + // blur the field try { field.blur(); diff --git a/zephyr/tests/frontend/casperjs/modules/colorizer.js b/zephyr/tests/frontend/casperjs/modules/colorizer.js index 99d587dcc6..8a7babd25a 100644 --- a/zephyr/tests/frontend/casperjs/modules/colorizer.js +++ b/zephyr/tests/frontend/casperjs/modules/colorizer.js @@ -64,7 +64,8 @@ var Colorizer = function Colorizer() { 'WARNING': { fg: 'red', bold: true }, 'GREEN_BAR': { fg: 'white', bg: 'green', bold: true }, 'RED_BAR': { fg: 'white', bg: 'red', bold: true }, - 'INFO_BAR': { bg: 'cyan', fg: 'white', bold: true } + 'INFO_BAR': { bg: 'cyan', fg: 'white', bold: true }, + 'WARN_BAR': { bg: 'yellow', fg: 'white', bold: true } }; /** diff --git a/zephyr/tests/frontend/casperjs/modules/events.js b/zephyr/tests/frontend/casperjs/modules/events.js index 8840957bc6..f0db39e4c1 100644 --- a/zephyr/tests/frontend/casperjs/modules/events.js +++ b/zephyr/tests/frontend/casperjs/modules/events.js @@ -19,6 +19,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +/*global CasperError*/ + var isArray = Array.isArray; function EventEmitter() { @@ -230,6 +232,17 @@ EventEmitter.prototype.filter = function() { return filter.apply(this, Array.prototype.splice.call(arguments, 1)); }; +EventEmitter.prototype.removeAllFilters = function(type) { + if (arguments.length === 0) { + this._filters = {}; + return this; + } + if (type && this._filters && this._filters[type]) { + this._filters[type] = null; + } + return this; +}; + EventEmitter.prototype.setFilter = function(type, filterFn) { if (!this._filters) { this._filters = {}; diff --git a/zephyr/tests/frontend/casperjs/modules/http.js b/zephyr/tests/frontend/casperjs/modules/http.js index 33030da321..1b072fe46f 100644 --- a/zephyr/tests/frontend/casperjs/modules/http.js +++ b/zephyr/tests/frontend/casperjs/modules/http.js @@ -62,6 +62,7 @@ responseHeaders.prototype.get = function get(name){ */ exports.augmentResponse = function(response) { "use strict"; + /*jshint proto:true*/ if (!utils.isHTTPResource(response)) { return; } diff --git a/zephyr/tests/frontend/casperjs/modules/injector.js b/zephyr/tests/frontend/casperjs/modules/injector.js deleted file mode 100644 index 43c004952c..0000000000 --- a/zephyr/tests/frontend/casperjs/modules/injector.js +++ /dev/null @@ -1,97 +0,0 @@ -/*! - * Casper is a navigation utility for PhantomJS. - * - * Documentation: http://casperjs.org/ - * Repository: http://github.com/n1k0/casperjs - * - * Copyright (c) 2011-2012 Nicolas Perriault - * - * Part of source code is Copyright Joyent, Inc. and other Node contributors. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - */ - -/*global CasperError console encodeURIComponent escape exports require*/ - -// WARNING: this module is deprecated since CasperJS 1.0.0-RC3 - -var utils = require('utils'); - -exports.create = function create(fn) { - "use strict"; - return new FunctionArgsInjector(fn); -}; - -/** - * Function argument injector. - * - * FIXME: use new Function() instead of eval() - */ -var FunctionArgsInjector = function FunctionArgsInjector(fn) { - "use strict"; - console.error('Warning: the injector module has been deprecated.'); - - if (!utils.isFunction(fn)) { - throw new CasperError("FunctionArgsInjector() can only process functions"); - } - this.fn = fn; - - this.extract = function extract(fn) { - var match = /^function\s?(\w+)?\s?\((.*)\)\s?\{([\s\S]*)\}/i.exec(fn.toString().trim()); - if (match && match.length > 1) { - var args = match[2].split(',').map(function _map(arg) { - return arg.replace(new RegExp(/\/\*+.*\*\//ig), "").trim(); - }).filter(function _filter(arg) { - return arg; - }) || []; - return { - name: match[1] ? match[1].trim() : null, - args: args, - body: match[3] ? match[3].trim() : '' - }; - } - }; - - this.process = function process(values) { - var fnObj = this.extract(this.fn); - if (!utils.isObject(fnObj)) { - throw new CasperError("Unable to process function " + this.fn.toString()); - } - var inject = this.getArgsInjectionString(fnObj.args, values); - var newFn = new Function([inject, fnObj.body].join('\n')); - newFn.name = fnObj.name || ''; - return newFn; - }; - - this.getArgsInjectionString = function getArgsInjectionString(args, values) { - values = typeof values === "object" ? values : {}; - var jsonValues = escape(encodeURIComponent(JSON.stringify(values))); - var inject = [ - 'var __casper_params__ = JSON.parse(decodeURIComponent(unescape(\'' + jsonValues + '\')));' - ]; - args.forEach(function _forEach(arg) { - if (arg in values) { - inject.push('var ' + arg + '=__casper_params__["' + arg + '"];'); - } - }); - return inject.join('\n') + '\n'; - }; -}; -exports.FunctionArgsInjector = FunctionArgsInjector; diff --git a/zephyr/tests/frontend/casperjs/modules/mouse.js b/zephyr/tests/frontend/casperjs/modules/mouse.js index 93ee2aeae4..8a011a86b7 100644 --- a/zephyr/tests/frontend/casperjs/modules/mouse.js +++ b/zephyr/tests/frontend/casperjs/modules/mouse.js @@ -43,11 +43,13 @@ var Mouse = function Mouse(casper) { throw new CasperError('Mouse() needs a Casper instance'); } - var slice = Array.prototype.slice; - - var nativeEvents = ['mouseup', 'mousedown', 'click', 'mousemove']; - var emulatedEvents = ['mouseover', 'mouseout']; - var supportedEvents = nativeEvents.concat(emulatedEvents); + var slice = Array.prototype.slice, + nativeEvents = ['mouseup', 'mousedown', 'click', 'mousemove']; + if (utils.gteVersion(phantom.version, '1.8.0')) { + nativeEvents.push('doubleclick'); + } + var emulatedEvents = ['mouseover', 'mouseout'], + supportedEvents = nativeEvents.concat(emulatedEvents); function computeCenter(selector) { var bounds = casper.getElementBounds(selector); @@ -72,8 +74,7 @@ var Mouse = function Mouse(casper) { throw new CasperError('Mouse.processEvent(): Too few arguments'); case 1: // selector - var selector = args[0]; - casper.page.sendEvent.apply(casper.page, [type].concat(computeCenter(selector))); + casper.page.sendEvent.apply(casper.page, [type].concat(computeCenter(args[0]))); break; case 2: // coordinates @@ -95,6 +96,10 @@ var Mouse = function Mouse(casper) { processEvent('click', arguments); }; + this.doubleclick = function doubleclick() { + processEvent('doubleclick', arguments); + }; + this.down = function down() { processEvent('mousedown', arguments); }; diff --git a/zephyr/tests/frontend/casperjs/modules/pagestack.js b/zephyr/tests/frontend/casperjs/modules/pagestack.js new file mode 100644 index 0000000000..3c7f7a381d --- /dev/null +++ b/zephyr/tests/frontend/casperjs/modules/pagestack.js @@ -0,0 +1,166 @@ +/*! + * Casper is a navigation utility for PhantomJS. + * + * Documentation: http://casperjs.org/ + * Repository: http://github.com/n1k0/casperjs + * + * Copyright (c) 2011-2012 Nicolas Perriault + * + * Part of source code is Copyright Joyent, Inc. and other Node contributors. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/*global CasperError console exports phantom require*/ + +var utils = require('utils'); +var f = utils.format; + +function create() { + "use strict"; + return new Stack(); +} +exports.create = create; + +/** + * Popups container. Implements Array prototype. + * + */ +var Stack = function Stack(){}; +exports.Stack = Stack; + +Stack.prototype = []; + +/** + * Cleans the stack from closed popup. + * + * @param WebPage closed Closed popup page instance + * @return Number New stack length + */ +Stack.prototype.clean = function clean(closed) { + "use strict"; + var closedIndex = -1; + this.forEach(function(popup, index) { + if (closed === popup) { + closedIndex = index; + } + }); + if (closedIndex > -1) { + this.splice(closedIndex, 1); + } + return this.length; +}; + +/** + * Finds a popup matching the provided information. Information can be: + * + * - RegExp: matching page url + * - String: strict page url value + * - WebPage: a direct WebPage instance + * + * @param Mixed popupInfo + * @return WebPage + */ +Stack.prototype.find = function find(popupInfo) { + "use strict"; + var popup, type = utils.betterTypeOf(popupInfo); + switch (type) { + case "regexp": + popup = this.findByRegExp(popupInfo); + break; + case "string": + popup = this.findByURL(popupInfo); + break; + case "qtruntimeobject": // WebPage + popup = popupInfo; + if (!utils.isWebPage(popup) || !this.some(function(popupPage) { + if (popupInfo.id && popupPage.id) { + return popupPage.id === popup.id; + } + return popupPage.url === popup.url; + })) { + throw new CasperError("Invalid or missing popup."); + } + break; + default: + throw new CasperError(f("Invalid popupInfo type: %s.", type)); + } + return popup; +}; + +/** + * Finds the first popup which url matches a given RegExp. + * + * @param RegExp regexp + * @return WebPage + */ +Stack.prototype.findByRegExp = function findByRegExp(regexp) { + "use strict"; + var popup = this.filter(function(popupPage) { + return regexp.test(popupPage.url); + })[0]; + if (!popup) { + throw new CasperError(f("Couldn't find popup with url matching pattern %s", regexp)); + } + return popup; +}; + +/** + * Finds the first popup matching a given url. + * + * @param String url The child WebPage url + * @return WebPage + */ +Stack.prototype.findByURL = function findByURL(string) { + "use strict"; + var popup = this.filter(function(popupPage) { + return popupPage.url.indexOf(string) !== -1; + })[0]; + if (!popup) { + throw new CasperError(f("Couldn't find popup with url containing '%s'", string)); + } + return popup; +}; + +/** + * Returns a human readable list of current active popup urls. + * + * @return Array Mapped stack. + */ +Stack.prototype.list = function list() { + "use strict"; + return this.map(function(popup) { + try { + return popup.url; + } catch (e) { + return ''; + } + }); +}; + +/** + * String representation of current instance. + * + * @return String + */ +Stack.prototype.toString = function toString() { + "use strict"; + return f("[Object Stack], having %d popup(s)" % this.length); +}; diff --git a/zephyr/tests/frontend/casperjs/modules/tester.js b/zephyr/tests/frontend/casperjs/modules/tester.js index 6b2dc16c62..5fd85c05b4 100755 --- a/zephyr/tests/frontend/casperjs/modules/tester.js +++ b/zephyr/tests/frontend/casperjs/modules/tester.js @@ -48,13 +48,20 @@ exports.create = function create(casper, options) { */ var Tester = function Tester(casper, options) { "use strict"; + /*jshint maxstatements:30*/ if (!utils.isCasperObject(casper)) { throw new CasperError("Tester needs a Casper instance"); } + var self = this; + this.casper = casper; + this.SKIP_MESSAGE = '__termination__'; + + this.aborted = false; + this.executed = 0; this.currentTestFile = null; this.currentSuiteNum = 0; this.exporter = require('xunit').create(); @@ -66,9 +73,11 @@ var Tester = function Tester(casper, options) { this.running = false; this.suites = []; this.options = utils.mergeObjects({ + failFast: false, // terminates a suite as soon as a test fails? failText: "FAIL", // text to use for a successful test passText: "PASS", // text to use for a failed test - pad: 80 // maximum number of chars for a result line + pad: 80 , // maximum number of chars for a result line + warnText: "WARN" // text to use for a dubious test }, options); // properties @@ -76,25 +85,39 @@ var Tester = function Tester(casper, options) { passed: 0, failed: 0, passes: [], - failures: [] + failures: [], + passesTime: [], + failuresTime: [] }; + // measuring test duration + this.currentTestStartTime = new Date(); + this.lastAssertTime = 0; + this.configure(); this.on('success', function onSuccess(success) { this.testResults.passes.push(success); - this.exporter.addSuccess(fs.absolute(success.file), success.message || success.standard); + var timeElapsed = new Date() - this.currentTestStartTime; + this.testResults.passesTime.push(timeElapsed - this.lastAssertTime); + this.exporter.addSuccess(fs.absolute(success.file), success.message || success.standard, timeElapsed - this.lastAssertTime); + this.lastAssertTime = timeElapsed; }); this.on('fail', function onFail(failure) { // export + var timeElapsed = new Date() - this.currentTestStartTime; + this.testResults.failuresTime.push(timeElapsed - this.lastAssertTime); this.exporter.addFailure( fs.absolute(failure.file), failure.message || failure.standard, failure.standard || "test failed", - failure.type || "unknown" + failure.type || "unknown", + (timeElapsed - this.lastAssertTime) ); + this.lastAssertTime = timeElapsed; this.testResults.failures.push(failure); + // special printing if (failure.type) { this.comment(' type: ' + failure.type); @@ -116,6 +139,33 @@ var Tester = function Tester(casper, options) { } } }); + + // casper events + this.casper.on('error', function onCasperError(msg, backtrace) { + if (!phantom.casperTest) { + return; + } + if (msg === self.SKIP_MESSAGE) { + this.warn(f('--fail-fast: aborted remaining tests in "%s"', self.currentTestFile)); + self.aborted = true; + return self.done(); + } + var line = 0; + if (!utils.isString(msg)) { + try { + line = backtrace[0].line; + } catch (e) {} + } + self.uncaughtError(msg, self.currentTestFile, line); + self.done(); + }); + + this.casper.on('step.error', function onStepError(e) { + if (e.message !== self.SKIP_MESSAGE) { + self.uncaughtError(e, self.currentTestFile); + } + self.done(); + }); }; // Tester class is an EventEmitter @@ -138,6 +188,7 @@ exports.Tester = Tester; */ Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject, message, context) { "use strict"; + this.executed++; return this.processAssertionResult(utils.mergeObjects({ success: subject === true, type: "assert", @@ -195,7 +246,7 @@ Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, m * * @param Function fn A function to be evaluated in remote DOM * @param String message Test description - * @param Object params Object containing the parameters to inject into the function (optional) + * @param Object params Object/Array containing the parameters to inject into the function (optional) * @return Object An assertion result object */ Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertEval(fn, message, params) { @@ -247,7 +298,7 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag "use strict"; var actual = this.casper.evaluate(function(inputName) { return __utils__.getFieldValue(inputName); - }, { inputName: inputName }); + }, inputName); return this.assert(this.testEquals(actual, expected), message, { type: 'assertField', standard: f('"%s" input field has the value "%s"', inputName, expected), @@ -267,7 +318,7 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag * @param String message Test description * @return Object An assertion result object */ -Tester.prototype.assertExists = Tester.prototype.assertExist = this.assertSelectorExists = Tester.prototype.assertSelectorExist = function assertExists(selector, message) { +Tester.prototype.assertExists = Tester.prototype.assertExist = Tester.prototype.assertSelectorExists = Tester.prototype.assertSelectorExist = function assertExists(selector, message) { "use strict"; return this.assert(this.casper.exists(selector), message, { type: "assertExists", @@ -327,6 +378,9 @@ Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) { */ Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertMatch(subject, pattern, message) { "use strict"; + if (utils.betterTypeOf(pattern) !== "regexp") { + throw new CasperError('Invalid regexp.'); + } return this.assert(pattern.test(subject), message, { type: "assertMatch", standard: "Subject matches the provided pattern", @@ -344,7 +398,7 @@ Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertM * @param String message Test description * @return Object An assertion result object */ -Tester.prototype.assertNot = function assertNot(condition, message) { +Tester.prototype.assertNot = Tester.prototype.assertFalse = function assertNot(condition, message) { "use strict"; return this.assert(!condition, message, { type: "assertNot", @@ -382,7 +436,7 @@ Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function * @param String message Test description * @return Object An assertion result object */ -Tester.prototype.assertRaises = Tester.prototype.assertRaise = this.assertThrows = function assertRaises(fn, args, message) { +Tester.prototype.assertRaises = Tester.prototype.assertRaise = Tester.prototype.assertThrows = function assertRaises(fn, args, message) { "use strict"; var context = { type: "assertRaises", @@ -418,6 +472,27 @@ Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = f }); }; +/** + * Asserts that given text doesn't exist in the document body. + * + * @param String text Text not to be found + * @param String message Test description + * @return Object An assertion result object + */ +Tester.prototype.assertTextDoesntExist = Tester.prototype.assertTextDoesntExist = function assertTextDoesntExist(text, message) { + "use strict"; + var textFound = (this.casper.evaluate(function _evaluate() { + return document.body.textContent || document.body.innerText; + }).indexOf(text) === -1); + return this.assert(textFound, message, { + type: "assertTextDoesntExists", + standard: "Text doesn't exist within the document body", + values: { + text: text + } + }); +}; + /** * Asserts that given text exists in the document body. * @@ -439,6 +514,44 @@ Tester.prototype.assertTextExists = Tester.prototype.assertTextExist = function }); }; +/** + * Asserts a subject is truthy. + * + * @param Mixed subject Test subject + * @param String message Test description + * @return Object An assertion result object + */ +Tester.prototype.assertTruthy = function assertTruthy(subject, message) { + "use strict"; + /*jshint eqeqeq:false*/ + return this.assert(utils.isTruthy(subject), message, { + type: "assertTruthy", + standard: "Subject is truthy", + values: { + subject: subject + } + }); +}; + +/** + * Asserts a subject is falsy. + * + * @param Mixed subject Test subject + * @param String message Test description + * @return Object An assertion result object + */ +Tester.prototype.assertFalsy = function assertFalsy(subject, message) { + "use strict"; + /*jshint eqeqeq:false*/ + return this.assert(utils.isFalsy(subject), message, { + type: "assertFalsy", + standard: "Subject is falsy", + values: { + subject: subject + } + }); +}; + /** * Asserts that given text exists in the provided selector. * @@ -447,11 +560,11 @@ Tester.prototype.assertTextExists = Tester.prototype.assertTextExist = function * @param String message Test description * @return Object An assertion result object */ -Tester.prototype.assertSelectorHasText = function assertSelectorHasText(selector, text, message) { +Tester.prototype.assertSelectorHasText = Tester.prototype.assertSelectorContains = function assertSelectorHasText(selector, text, message) { "use strict"; var textFound = this.casper.fetchText(selector).indexOf(text) !== -1; return this.assert(textFound, message, { - type: "assertTextInSelector", + type: "assertSelectorHasText", standard: f('Found "%s" within the selector "%s"', text, selector), values: { selector: selector, @@ -468,11 +581,11 @@ Tester.prototype.assertSelectorHasText = function assertSelectorHasText(selector * @param String message Test description * @return Object An assertion result object */ -Tester.prototype.assertSelectorDoesntHaveText = function assertSelectorDoesntHaveText(selector, text, message) { +Tester.prototype.assertSelectorDoesntHaveText = Tester.prototype.assertSelectorDoesntContain = function assertSelectorDoesntHaveText(selector, text, message) { "use strict"; var textFound = this.casper.fetchText(selector).indexOf(text) === -1; return this.assert(textFound, message, { - type: "assertNoTextInSelector", + type: "assertSelectorDoesntHaveText", standard: f('Did not find "%s" within the selector "%s"', text, selector), values: { selector: selector, @@ -510,6 +623,9 @@ Tester.prototype.assertTitle = function assertTitle(expected, message) { */ Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = function assertTitleMatch(pattern, message) { "use strict"; + if (utils.betterTypeOf(pattern) !== "regexp") { + throw new CasperError('Invalid regexp.'); + } var currentTitle = this.casper.getTitle(); return this.assert(pattern.test(currentTitle), message, { type: "assertTitle", @@ -601,6 +717,19 @@ Tester.prototype.bar = function bar(text, style) { this.casper.echo(text, style, this.options.pad); }; +/** + * Retrieves the sum of all durations of the tests which were + * executed in the current suite + * + * @return Number duration of all tests executed until now (in the current suite) + */ +Tester.prototype.calculateSuiteDuration = function calculateSuiteDuration() { + "use strict"; + return this.testResults.passesTime.concat(this.testResults.failuresTime).reduce(function add(a, b) { + return a + b; + }, 0); +}; + /** * Render a colorized output. Basically a proxy method for * Casper.Colorizer#colorize() @@ -635,7 +764,7 @@ Tester.prototype.configure = function configure() { // specific timeout callbacks this.casper.options.onStepTimeout = function test_onStepTimeout(timeout, step) { - tester.fail(f("Step timeout occured at step %d (%dms)", step, timeout)); + tester.fail(f("Step timeout occured at step %s (%dms)", step, timeout)); }; this.casper.options.onTimeout = function test_onTimeout(timeout) { @@ -645,29 +774,19 @@ Tester.prototype.configure = function configure() { this.casper.options.onWaitTimeout = function test_onWaitTimeout(timeout) { tester.fail(f("Wait timeout occured (%dms)", timeout)); }; - - // events - this.casper.on('error', function(msg, backtrace) { - var line = 0; - try { - line = backtrace[0].line; - } catch (e) {} - tester.uncaughtError(msg, tester.currentTestFile, line); - tester.done(); - }); - - this.casper.on('step.error', function onStepError(e) { - tester.uncaughtError(e, tester.currentTestFile); - tester.done(); - }); }; /** * Declares the current test suite done. * + * @param Number planned Number of planned tests */ -Tester.prototype.done = function done() { +Tester.prototype.done = function done(planned) { "use strict"; + if (planned > 0 && planned !== this.executed) { + this.fail(f('%s: %d tests planned, %d tests executed', + this.currentTestFile, planned, this.executed)); + } this.emit('test.done'); this.running = false; }; @@ -784,6 +903,27 @@ Tester.prototype.getPasses = function getPasses() { }; }; +/** + * Retrieves the array where all the durations of failed tests are stored + * + * @return Array durations of failed tests + */ +Tester.prototype.getFailuresTime = function getFailuresTime() { + "use strict"; + return this.testResults.failuresTime; +} + +/** + * Retrieves the array where all the durations of passed tests are stored + * + * @return Array durations of passed tests + */ +Tester.prototype.getPassesTime = function getPassesTime() { + "use strict"; + return this.testResults.passesTime; +} + + /** * Writes an info-style formatted message to stdout. * @@ -816,21 +956,23 @@ Tester.prototype.pass = function pass(message) { */ Tester.prototype.processAssertionResult = function processAssertionResult(result) { "use strict"; - var eventName, style, status; - if (result.success === true) { - eventName = 'success'; - style = 'INFO'; + var eventName= 'success', + message = result.message || result.standard, + style = 'INFO', status = this.options.passText; - this.testResults.passed++; - } else { + if (!result.success) { eventName = 'fail'; style = 'RED_BAR'; status = this.options.failText; this.testResults.failed++; + } else { + this.testResults.passed++; } - var message = result.message || result.standard; this.casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' ')); this.emit(eventName, result); + if (this.options.failFast && !result.success) { + throw this.SKIP_MESSAGE; + } return result; }; @@ -852,7 +994,7 @@ Tester.prototype.renderFailureDetails = function renderFailureDetails(failures) message = failure.message; this.casper.echo(f('In %s:%s', failure.file, line)); this.casper.echo(f(' %s: %s', type, message || failure.standard || "(no message was entered)"), "COMMENT"); - }); + }.bind(this)); }; /** @@ -867,8 +1009,8 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { var total = this.testResults.passed + this.testResults.failed, statusText, style, result; var exitStatus = ~~(status || (this.testResults.failed > 0 ? 1 : 0)); if (total === 0) { - statusText = this.options.failText; - style = 'RED_BAR'; + statusText = this.options.warnText; + style = 'WARN_BAR'; result = f("%s Looks like you didn't run any test.", statusText); } else { if (this.testResults.failed > 0) { @@ -878,8 +1020,9 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { statusText = this.options.passText; style = 'GREEN_BAR'; } - result = f('%s %s tests executed, %d passed, %d failed.', - statusText, total, this.testResults.passed, this.testResults.failed); + result = f('%s %s tests executed in %ss, %d passed, %d failed.', + statusText, total, utils.ms2seconds(this.calculateSuiteDuration()), + this.testResults.passed, this.testResults.failed); } this.casper.echo(result, style, this.options.pad); if (this.testResults.failed > 0) { @@ -931,16 +1074,23 @@ Tester.prototype.runSuites = function runSuites() { this.casper.exit(1); } self.currentSuiteNum = 0; + self.currentTestStartTime = new Date(); + self.lastAssertTime = 0; var interval = setInterval(function _check(self) { if (self.running) { return; } - if (self.currentSuiteNum === testFiles.length) { + if (self.currentSuiteNum === testFiles.length || self.aborted) { self.emit('tests.complete'); clearInterval(interval); + self.exporter.setSuiteDuration(self.calculateSuiteDuration()); + self.aborted = false; } else { self.runTest(testFiles[self.currentSuiteNum]); + self.exporter.setSuiteDuration(self.calculateSuiteDuration()); self.currentSuiteNum++; + self.passesTime = []; + self.failuresTime = []; } }, 100, this); }; @@ -953,6 +1103,7 @@ Tester.prototype.runTest = function runTest(testFile) { "use strict"; this.bar(f('Test file: %s', testFile), 'INFO_BAR'); this.running = true; // this.running is set back to false with done() + this.executed = 0; this.exec(testFile); }; diff --git a/zephyr/tests/frontend/casperjs/modules/utils.js b/zephyr/tests/frontend/casperjs/modules/utils.js index 5e0d0f9d57..2016ebeb3a 100644 --- a/zephyr/tests/frontend/casperjs/modules/utils.js +++ b/zephyr/tests/frontend/casperjs/modules/utils.js @@ -34,16 +34,26 @@ * Provides a better typeof operator equivalent, able to retrieve the array * type. * + * CAVEAT: this function does not necessarilly map to classical js "type" names, + * notably a `null` will map to "null" instead of "object". + * * @param mixed input * @return String * @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ */ function betterTypeOf(input) { "use strict"; - try { - return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase(); - } catch (e) { - return typeof input; + switch (input) { + case undefined: + return 'undefined'; + case null: + return 'null'; + default: + try { + return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase(); + } catch (e) { + return typeof input; + } } } exports.betterTypeOf = betterTypeOf; @@ -69,6 +79,18 @@ function cleanUrl(url) { } exports.cleanUrl = cleanUrl; +/** + * Clones an object. + * + * @param Mixed o + * @return Mixed + */ +function clone(o) { + "use strict"; + return JSON.parse(JSON.stringify(o)); +} +exports.clone = clone; + /** * Dumps a JSON representation of passed value to the console. Used for * debugging purpose only. @@ -264,6 +286,18 @@ function isClipRect(value) { } exports.isClipRect = isClipRect; +/** + * Checks that the subject is falsy. + * + * @param Mixed subject Test subject + * @return Boolean + */ +function isFalsy(subject) { + "use strict"; + /*jshint eqeqeq:false*/ + return !subject; +} +exports.isFalsy = isFalsy; /** * Checks if value is a javascript Function * @@ -337,6 +371,18 @@ function isObject(value) { } exports.isObject = isObject; +/** + * Checks if value is a RegExp + * + * @param mixed value + * @return Boolean + */ +function isRegExp(value) { + "use strict"; + return isType(value, "regexp"); +} +exports.isRegExp = isRegExp; + /** * Checks if value is a javascript String * @@ -349,6 +395,19 @@ function isString(value) { } exports.isString = isString; +/** + * Checks that the subject is truthy. + * + * @param Mixed subject Test subject + * @return Boolean + */ +function isTruthy(subject) { + "use strict"; + /*jshint eqeqeq:false*/ + return !!subject; +} +exports.isTruthy = isTruthy; + /** * Shorthands for checking if a value is of the given type. Can check for * arrays. @@ -433,20 +492,32 @@ exports.isWebPage = isWebPage; function mergeObjects(origin, add) { "use strict"; for (var p in add) { - try { - if (add[p].constructor === Object) { + if (add[p] && add[p].constructor === Object) { + if (origin[p] && origin[p].constructor === Object) { origin[p] = mergeObjects(origin[p], add[p]); } else { - origin[p] = add[p]; + origin[p] = clone(add[p]); } - } catch(e) { - origin[p] = add[p]; + } else { + origin[p] = add[p]; } } return origin; } exports.mergeObjects = mergeObjects; +/** + * Converts milliseconds to seconds and rounds the results to 3 digits accuracy. + * + * @param Number milliseconds + * @return Number seconds + */ +function ms2seconds(milliseconds) { + "use strict"; + return Math.round(milliseconds / 1000 * 1000) / 1000; +} +exports.ms2seconds = ms2seconds; + /** * Creates an (SG|X)ML node element. * @@ -456,7 +527,7 @@ exports.mergeObjects = mergeObjects; */ function node(name, attributes) { "use strict"; - var _node = document.createElement(name); + var _node = document.createElement(name); for (var attrName in attributes) { var value = attributes[attrName]; if (attributes.hasOwnProperty(attrName) && isString(attrName)) { @@ -467,6 +538,20 @@ function node(name, attributes) { } exports.node = node; +/** + * Maps an object to an array made from its values. + * + * @param Object obj + * @return Array + */ +function objectValues(obj) { + "use strict"; + return Object.keys(obj).map(function(arg) { + return obj[arg]; + }); +} +exports.objectValues = objectValues; + /** * Serializes a value using JSON. * @@ -506,3 +591,62 @@ function unique(array) { return r; } exports.unique = unique; + +/** + * Compare two version numbers represented as strings. + * + * @param String a Version a + * @param String b Version b + * @return Number + */ +function cmpVersion(a, b) { + "use strict"; + var i, cmp, len, re = /(\.0)+[^\.]*$/; + function versionToString(version) { + if (isObject(version)) { + try { + return [version.major, version.minor, version.patch].join('.'); + } catch (e) {} + } + return version; + } + a = versionToString(a); + b = versionToString(b); + a = (a + '').replace(re, '').split('.'); + b = (b + '').replace(re, '').split('.'); + len = Math.min(a.length, b.length); + for (i = 0; i < len; i++) { + cmp = parseInt(a[i], 10) - parseInt(b[i], 10); + if (cmp !== 0) { + return cmp; + } + } + return a.length - b.length; +} +exports.cmpVersion = cmpVersion; + +/** + * Checks if a version number string is greater or equals another. + * + * @param String a Version a + * @param String b Version b + * @return Boolean + */ +function gteVersion(a, b) { + "use strict"; + return cmpVersion(a, b) >= 0; +} +exports.gteVersion = gteVersion; + +/** + * Checks if a version number string is less than another. + * + * @param String a Version a + * @param String b Version b + * @return Boolean + */ +function ltVersion(a, b) { + "use strict"; + return cmpVersion(a, b) < 0; +} +exports.ltVersion = ltVersion; diff --git a/zephyr/tests/frontend/casperjs/modules/xunit.js b/zephyr/tests/frontend/casperjs/modules/xunit.js index 2b64b041cb..887a8fccf5 100644 --- a/zephyr/tests/frontend/casperjs/modules/xunit.js +++ b/zephyr/tests/frontend/casperjs/modules/xunit.js @@ -45,7 +45,7 @@ var fs = require('fs'); function generateClassName(classname) { "use strict"; classname = classname.replace(phantom.casperPath, "").trim(); - var script = classname || phantom.casperScript; + var script = classname || phantom.casperScript || ""; if (script.indexOf(fs.workingDirectory) === 0) { script = script.substring(fs.workingDirectory.length + 1); } @@ -55,6 +55,12 @@ function generateClassName(classname) { if (~script.indexOf('.')) { script = script.substring(0, script.lastIndexOf('.')); } + + // If we have trimmed our string down to nothing, default to script name + if (!script && phantom.casperScript) { + script = phantom.casperScript; + } + return script || "unknown"; } @@ -86,13 +92,18 @@ exports.XUnitExporter = XUnitExporter; * * @param String classname * @param String name + * @param Number duration Test duration in milliseconds */ -XUnitExporter.prototype.addSuccess = function addSuccess(classname, name) { +XUnitExporter.prototype.addSuccess = function addSuccess(classname, name, duration) { "use strict"; - this._xml.appendChild(utils.node('testcase', { + var snode = utils.node('testcase', { classname: generateClassName(classname), - name: name - })); + name: name + }); + if (duration !== undefined) { + snode.setAttribute('time', utils.ms2seconds(duration)); + } + this._xml.appendChild(snode); }; /** @@ -102,13 +113,17 @@ XUnitExporter.prototype.addSuccess = function addSuccess(classname, name) { * @param String name * @param String message * @param String type + * @param Number duration Test duration in milliseconds */ -XUnitExporter.prototype.addFailure = function addFailure(classname, name, message, type) { +XUnitExporter.prototype.addFailure = function addFailure(classname, name, message, type, duration) { "use strict"; var fnode = utils.node('testcase', { classname: generateClassName(classname), name: name }); + if (duration !== undefined) { + fnode.setAttribute('time', utils.ms2seconds(duration)); + } var failure = utils.node('failure', { type: type || "unknown" }); @@ -117,6 +132,18 @@ XUnitExporter.prototype.addFailure = function addFailure(classname, name, messag this._xml.appendChild(fnode); }; +/** + * Adds test suite duration + * + * @param Number duration Test duration in milliseconds + */ +XUnitExporter.prototype.setSuiteDuration = function setSuiteDuration(duration) { + "use strict"; + if (!isNaN(duration)) { + this._xml.setAttribute("time", utils.ms2seconds(duration)); + } +}; + /** * Retrieves generated XML object - actually an HTMLElement. * diff --git a/zephyr/tests/frontend/casperjs/package.json b/zephyr/tests/frontend/casperjs/package.json index 0818708464..3f1d923a97 100644 --- a/zephyr/tests/frontend/casperjs/package.json +++ b/zephyr/tests/frontend/casperjs/package.json @@ -1,7 +1,7 @@ { "name": "casperjs", "description": "Navigation scripting & testing utility for PhantomJS", - "version": "1.0.0-RC4", + "version": "1.0.2", "keywords": [ "phantomjs", "javascript" @@ -14,7 +14,7 @@ } ], "dependencies": { - "http://www.phantomjs.org/": "1.6" + "http://www.phantomjs.org/": "1.7" }, "bugs": { "url": "https://github.com/n1k0/casperjs/issues" diff --git a/zephyr/tests/frontend/casperjs/rpm/casperjs.spec b/zephyr/tests/frontend/casperjs/rpm/casperjs.spec new file mode 100644 index 0000000000..0e004884e5 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/rpm/casperjs.spec @@ -0,0 +1,203 @@ +%define name casperjs +%define version 1.0.0 +%define release 1_1 +%define prefix /usr + +%define mybuilddir %{_builddir}/%{name}-%{version}-root + +Summary: open source navigation scripting & testing utility written in Javascript +Name: %{name} +Version: %{version} +License: BSD +Release: %{release} +Packager: Jan Schaumann +Group: Utilities/Misc +Source: %{name}-%{version}.tar.gz +BuildRoot: /tmp/%{name}-%{version}-root + +Requires: phantomjs + +%description +CasperJS is an open source navigation scripting & testing utility written +in Javascript and based on PhantomJS. It eases the process of defining a +full navigation scenario and provides useful high-level functions, methods +& syntactic sugar for doing common tasks + +%prep +%setup -q + +%setup +mkdir -p %{mybuilddir}%{prefix}/bin +mkdir -p %{mybuilddir}%{prefix}/share/%{name}/bin +mkdir -p %{mybuilddir}%{prefix}/share/%{name}/modules +mkdir -p %{mybuilddir}%{prefix}/share/%{name}/samples +mkdir -p %{mybuilddir}%{prefix}/share/%{name}/tests + +%install +cp bin/%{name} %{mybuilddir}%{prefix}/share/%{name}/bin/ +ln -s %{prefix}/share/%{name}/bin/%{name} %{mybuilddir}%{prefix}/bin/%{name} +cp bin/bootstrap.js %{mybuilddir}%{prefix}/share/%{name}/bin/ +# Yes, this tool needs this file in the 'bin' directory. +cp bin/usage.txt %{mybuilddir}%{prefix}/share/%{name}/bin/ +cp CHANGELOG.md %{mybuilddir}%{prefix}/share/%{name}/ +cp CONTRIBUTING.md %{mybuilddir}%{prefix}/share/%{name}/ +cp CONTRIBUTORS.md %{mybuilddir}%{prefix}/share/%{name}/ +cp LICENSE.md %{mybuilddir}%{prefix}/share/%{name}/ +cp README.md %{mybuilddir}%{prefix}/share/%{name}/ +cp package.json %{mybuilddir}%{prefix}/share/%{name}/ +cp -R modules/* %{mybuilddir}%{prefix}/share/%{name}/modules/ +cp -R samples/* %{mybuilddir}%{prefix}/share/%{name}/samples/ +cp -R tests/* %{mybuilddir}%{prefix}/share/%{name}/tests/ + +%files +%defattr(0444,root,root) +%attr(0555,root,root)%{prefix}/bin/%{name} +%attr(0555,root,root)%{prefix}/share/%{name}/bin/%{name} +%attr(0555,root,root)%{prefix}/share/%{name}/bin/bootstrap.js +%{prefix}/share/%{name}/bin/usage.txt +%{prefix}/share/%{name}/CHANGELOG.md +%{prefix}/share/%{name}/CONTRIBUTING.md +%{prefix}/share/%{name}/CONTRIBUTORS.md +%{prefix}/share/%{name}/LICENSE.md +%{prefix}/share/%{name}/README.md +%{prefix}/share/%{name}/package.json +%{prefix}/share/%{name}/modules/casper.js +%{prefix}/share/%{name}/modules/cli.js +%{prefix}/share/%{name}/modules/clientutils.js +%{prefix}/share/%{name}/modules/colorizer.js +%{prefix}/share/%{name}/modules/events.js +%{prefix}/share/%{name}/modules/http.js +%{prefix}/share/%{name}/modules/mouse.js +%{prefix}/share/%{name}/modules/querystring.js +%{prefix}/share/%{name}/modules/tester.js +%{prefix}/share/%{name}/modules/utils.js +%{prefix}/share/%{name}/modules/vendors/coffee-script.js +%{prefix}/share/%{name}/modules/xunit.js +%{prefix}/share/%{name}/samples/bbcshots.coffee +%{prefix}/share/%{name}/samples/bbcshots.js +%{prefix}/share/%{name}/samples/cliplay.coffee +%{prefix}/share/%{name}/samples/cliplay.js +%{prefix}/share/%{name}/samples/customevents.coffee +%{prefix}/share/%{name}/samples/customevents.js +%{prefix}/share/%{name}/samples/customlogging.coffee +%{prefix}/share/%{name}/samples/customlogging.js +%{prefix}/share/%{name}/samples/download.coffee +%{prefix}/share/%{name}/samples/download.js +%{prefix}/share/%{name}/samples/dynamic.coffee +%{prefix}/share/%{name}/samples/dynamic.js +%{prefix}/share/%{name}/samples/each.coffee +%{prefix}/share/%{name}/samples/each.js +%{prefix}/share/%{name}/samples/events.coffee +%{prefix}/share/%{name}/samples/events.js +%{prefix}/share/%{name}/samples/extends.coffee +%{prefix}/share/%{name}/samples/extends.js +%{prefix}/share/%{name}/samples/googlelinks.coffee +%{prefix}/share/%{name}/samples/googlelinks.js +%{prefix}/share/%{name}/samples/googlematch.coffee +%{prefix}/share/%{name}/samples/googlematch.js +%{prefix}/share/%{name}/samples/googlepagination.coffee +%{prefix}/share/%{name}/samples/googlepagination.js +%{prefix}/share/%{name}/samples/googletesting.coffee +%{prefix}/share/%{name}/samples/googletesting.js +%{prefix}/share/%{name}/samples/logcolor.coffee +%{prefix}/share/%{name}/samples/logcolor.js +%{prefix}/share/%{name}/samples/metaextract.coffee +%{prefix}/share/%{name}/samples/metaextract.js +%{prefix}/share/%{name}/samples/multirun.coffee +%{prefix}/share/%{name}/samples/multirun.js +%{prefix}/share/%{name}/samples/screenshot.coffee +%{prefix}/share/%{name}/samples/screenshot.js +%{prefix}/share/%{name}/samples/statushandlers.coffee +%{prefix}/share/%{name}/samples/statushandlers.js +%{prefix}/share/%{name}/samples/steptimeout.coffee +%{prefix}/share/%{name}/samples/steptimeout.js +%{prefix}/share/%{name}/samples/timeout.coffee +%{prefix}/share/%{name}/samples/timeout.js +%{prefix}/share/%{name}/tests/site/field-array.html +%{prefix}/share/%{name}/tests/site/images/phantom.png +%{prefix}/share/%{name}/tests/site/result.html +%{prefix}/share/%{name}/tests/site/multiple-forms.html +%{prefix}/share/%{name}/tests/site/global.html +%{prefix}/share/%{name}/tests/site/elementattribute.html +%{prefix}/share/%{name}/tests/site/urls.html +%{prefix}/share/%{name}/tests/site/mouse-events.html +%{prefix}/share/%{name}/tests/site/index.html +%{prefix}/share/%{name}/tests/site/click.html +%{prefix}/share/%{name}/tests/site/page1.html +%{prefix}/share/%{name}/tests/site/prompt.html +%{prefix}/share/%{name}/tests/site/error.html +%{prefix}/share/%{name}/tests/site/dummy.js +%{prefix}/share/%{name}/tests/site/page2.html +%{prefix}/share/%{name}/tests/site/alert.html +%{prefix}/share/%{name}/tests/site/form.html +%{prefix}/share/%{name}/tests/site/confirm.html +%{prefix}/share/%{name}/tests/site/resources.html +%{prefix}/share/%{name}/tests/site/test.html +%{prefix}/share/%{name}/tests/site/page3.html +%{prefix}/share/%{name}/tests/site/visible.html +%{prefix}/share/%{name}/tests/site/waitFor.html +%{prefix}/share/%{name}/tests/sample_modules/csmodule.coffee +%{prefix}/share/%{name}/tests/sample_modules/jsmodule.js +%{prefix}/share/%{name}/tests/testdir/03_a.js +%{prefix}/share/%{name}/tests/testdir/02_b/abc.js +%{prefix}/share/%{name}/tests/testdir/04/02_do.js +%{prefix}/share/%{name}/tests/testdir/04/01_init.js +%{prefix}/share/%{name}/tests/testdir/01_a/abc.js +%{prefix}/share/%{name}/tests/testdir/01_a/def.js +%{prefix}/share/%{name}/tests/testdir/03_b.js +%{prefix}/share/%{name}/tests/suites/casper/capture.js +%{prefix}/share/%{name}/tests/suites/casper/prompt.js +%{prefix}/share/%{name}/tests/suites/casper/resources.coffee +%{prefix}/share/%{name}/tests/suites/casper/auth.js +%{prefix}/share/%{name}/tests/suites/casper/alert.js +%{prefix}/share/%{name}/tests/suites/casper/wait.js +%{prefix}/share/%{name}/tests/suites/casper/flow.coffee +%{prefix}/share/%{name}/tests/suites/casper/events.js +%{prefix}/share/%{name}/tests/suites/casper/evaluate.js +%{prefix}/share/%{name}/tests/suites/casper/logging.js +%{prefix}/share/%{name}/tests/suites/casper/xpath.js +%{prefix}/share/%{name}/tests/suites/casper/elementattribute.js +%{prefix}/share/%{name}/tests/suites/casper/viewport.js +%{prefix}/share/%{name}/tests/suites/casper/.casper +%{prefix}/share/%{name}/tests/suites/casper/steps.js +%{prefix}/share/%{name}/tests/suites/casper/exists.js +%{prefix}/share/%{name}/tests/suites/casper/click.js +%{prefix}/share/%{name}/tests/suites/casper/mouseevents.js +%{prefix}/share/%{name}/tests/suites/casper/fetchtext.js +%{prefix}/share/%{name}/tests/suites/casper/urls.js +%{prefix}/share/%{name}/tests/suites/casper/open.js +%{prefix}/share/%{name}/tests/suites/casper/agent.js +%{prefix}/share/%{name}/tests/suites/casper/formfill.js +%{prefix}/share/%{name}/tests/suites/casper/request.js +%{prefix}/share/%{name}/tests/suites/casper/confirm.js +%{prefix}/share/%{name}/tests/suites/casper/history.js +%{prefix}/share/%{name}/tests/suites/casper/debug.js +%{prefix}/share/%{name}/tests/suites/casper/global.js +%{prefix}/share/%{name}/tests/suites/casper/encode.js +%{prefix}/share/%{name}/tests/suites/casper/onerror.js +%{prefix}/share/%{name}/tests/suites/casper/start.js +%{prefix}/share/%{name}/tests/suites/casper/hooks.js +%{prefix}/share/%{name}/tests/suites/casper/headers.js +%{prefix}/share/%{name}/tests/suites/casper/visible.js +%{prefix}/share/%{name}/tests/suites/coffee.coffee +%{prefix}/share/%{name}/tests/suites/require.js +%{prefix}/share/%{name}/tests/suites/cli.js +%{prefix}/share/%{name}/tests/suites/fs.js +%{prefix}/share/%{name}/tests/suites/.casper +%{prefix}/share/%{name}/tests/suites/tester.js +%{prefix}/share/%{name}/tests/suites/clientutils.js +%{prefix}/share/%{name}/tests/suites/http_status.js +%{prefix}/share/%{name}/tests/suites/xunit.js +%{prefix}/share/%{name}/tests/suites/utils.js +%{prefix}/share/%{name}/tests/selftest.js +%{prefix}/share/%{name}/tests/run.js + +%changelog +* Mon Dec 24 2012 Nicolas Perriault +- removed 'injector.js' module + +* Mon Dec 10 2012 Jan Schaumann +- include 'tests' + +* Mon Nov 26 2012 Jan Schaumann +- first rpm version diff --git a/zephyr/tests/frontend/casperjs/rpm/mkrpm.sh b/zephyr/tests/frontend/casperjs/rpm/mkrpm.sh new file mode 100755 index 0000000000..a27add2f0f --- /dev/null +++ b/zephyr/tests/frontend/casperjs/rpm/mkrpm.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# A silly little helper script to build the RPM. +set -e + +name=${1:?"Usage: build "} +name=${name%.spec} +topdir=$(mktemp -d) +version=$(awk '/define version/ { print $NF }' ${name}.spec) +builddir=${TMPDIR:-/tmp}/${name}-${version} +sourcedir="${topdir}/SOURCES" +buildroot="${topdir}/BUILD/${name}-${version}-root" +mkdir -p ${topdir}/RPMS ${topdir}/SRPMS ${topdir}/SOURCES ${topdir}/BUILD +mkdir -p ${buildroot} ${builddir} +echo "=> Copying sources..." +( cd .. && tar cf - ./[A-Z]* ./package.json ./bin ./samples ./tests ./modules | tar xf - -C ${builddir} ) +echo "=> Creating source tarball under ${sourcedir}..." +( cd ${builddir}/.. && tar zcf ${sourcedir}/${name}-${version}.tar.gz ${name}-${version} ) +echo "=> Building RPM..." +#rpmbuild --define "_topdir ${topdir}" --buildroot ${buildroot} --clean -bb ${name}.spec +rpm=$(rpmbuild --define "_topdir ${topdir}" --buildroot ${buildroot} --clean -bb ${name}.spec 2>/dev/null | \ + awk '/\/RPMS\// { print $2; }') +cp ${rpm} ${TMPDIR:-/tmp}/ +rm -fr ${topdir} +echo ${TMPDIR:-/tmp}/${rpm##*/} diff --git a/zephyr/tests/frontend/casperjs/rubybin/casperjs b/zephyr/tests/frontend/casperjs/rubybin/casperjs index c7cbe34d8c..8f242f6802 100755 --- a/zephyr/tests/frontend/casperjs/rubybin/casperjs +++ b/zephyr/tests/frontend/casperjs/rubybin/casperjs @@ -59,4 +59,4 @@ if system(CASPER_COMMAND.join(" ")).nil? puts "Fatal: Did you install phantomjs?" end -exit $?.exitstatus +exit $?.exitstatus || 1 diff --git a/zephyr/tests/frontend/casperjs/samples/bbcshots.coffee b/zephyr/tests/frontend/casperjs/samples/bbcshots.coffee index 7a7eba03ca..ed33e0dbc8 100644 --- a/zephyr/tests/frontend/casperjs/samples/bbcshots.coffee +++ b/zephyr/tests/frontend/casperjs/samples/bbcshots.coffee @@ -11,7 +11,7 @@ images = [] casper.hide = (selector) -> @evaluate (selector) -> document.querySelector(selector).style.display = "none" - , selector: selector + , selector casper.start "http://www.bbc.co.uk/", -> nbLinks = @evaluate -> @@ -21,15 +21,16 @@ casper.start "http://www.bbc.co.uk/", -> @hide ".nav_left" @hide ".nav_right" @mouse.move "#promo2_carousel" - @waitUntilVisible ".autoplay.nav_pause", -> - @echo "Moving over pause button" - @mouse.move ".autoplay.nav_pause" - @click ".autoplay.nav_pause" - @echo "Clicked on pause button" - @waitUntilVisible ".autoplay.nav_play", -> - @echo "Carousel has been paused" - # hide play button - @hide ".autoplay" + +casper.waitUntilVisible ".autoplay.nav_pause", -> + @echo "Moving over pause button" + @mouse.move ".autoplay.nav_pause" + @click ".autoplay.nav_pause" + @echo "Clicked on pause button" + @waitUntilVisible ".autoplay.nav_play", -> + @echo "Carousel has been paused" + # hide play button + @hide ".autoplay" # Capture carrousel area next = -> diff --git a/zephyr/tests/frontend/casperjs/samples/bbcshots.js b/zephyr/tests/frontend/casperjs/samples/bbcshots.js index dafe6ca3b8..d9b135f437 100644 --- a/zephyr/tests/frontend/casperjs/samples/bbcshots.js +++ b/zephyr/tests/frontend/casperjs/samples/bbcshots.js @@ -1,7 +1,9 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * Create a mosaic image from all headline photos on BBC homepage */ - var casper = require("casper").create(); var nbLinks = 0; var currentLink = 1; @@ -12,9 +14,7 @@ var buildPage, next; casper.hide = function(selector) { this.evaluate(function(selector) { document.querySelector(selector).style.display = "none"; - }, { - selector: selector - }); + }, selector); }; casper.start("http://www.bbc.co.uk/", function() { @@ -26,16 +26,17 @@ casper.start("http://www.bbc.co.uk/", function() { this.hide(".nav_left"); this.hide(".nav_right"); this.mouse.move("#promo2_carousel"); - this.waitUntilVisible(".autoplay.nav_pause", function() { - this.echo("Moving over pause button"); - this.mouse.move(".autoplay.nav_pause"); - this.click(".autoplay.nav_pause"); - this.echo("Clicked on pause button"); - this.waitUntilVisible(".autoplay.nav_play", function() { - this.echo("Carousel has been paused"); - // hide play button - this.hide(".autoplay"); - }); +}); + +casper.waitUntilVisible(".autoplay.nav_pause", function() { + this.echo("Moving over pause button"); + this.mouse.move(".autoplay.nav_pause"); + this.click(".autoplay.nav_pause"); + this.echo("Clicked on pause button"); + this.waitUntilVisible(".autoplay.nav_play", function() { + this.echo("Carousel has been paused"); + // hide play button + this.hide(".autoplay"); }); }); diff --git a/zephyr/tests/frontend/casperjs/samples/cliplay.js b/zephyr/tests/frontend/casperjs/samples/cliplay.js index d2557e2a29..758e211853 100644 --- a/zephyr/tests/frontend/casperjs/samples/cliplay.js +++ b/zephyr/tests/frontend/casperjs/samples/cliplay.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create(); var dump = require("utils").dump; diff --git a/zephyr/tests/frontend/casperjs/samples/customevents.js b/zephyr/tests/frontend/casperjs/samples/customevents.js index 57252a5e0c..3e87e30939 100644 --- a/zephyr/tests/frontend/casperjs/samples/customevents.js +++ b/zephyr/tests/frontend/casperjs/samples/customevents.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create(); // listening to a custom event diff --git a/zephyr/tests/frontend/casperjs/samples/customlogging.coffee b/zephyr/tests/frontend/casperjs/samples/customlogging.coffee index dbc907ba36..c60296f758 100644 --- a/zephyr/tests/frontend/casperjs/samples/customlogging.coffee +++ b/zephyr/tests/frontend/casperjs/samples/customlogging.coffee @@ -2,29 +2,27 @@ A basic custom logging implementation. The idea is to (extremely) verbosely log every received resource. ### - casper = require("casper").create - ### - Every time a resource is received, a new log entry is added to the stack - at the 'verbose' level. - - @param Object resource A phantomjs resource object - ### - onResourceReceived: (self, resource) -> - infos = [] - props = [ - "url" - "status" - "statusText" - "redirectURL" - "bodySize" - ] - infos.push resource[prop] for prop in props - infos.push "[#{header.name}: #{header.value}]" for header in resource.headers - @log infos.join(", "), "verbose" verbose: true # we want to see the log printed out to the console logLevel: "verbose" # of course we want to see logs to our new level :) +### +Every time a resource is received, a new log entry is added to the stack +at the 'verbose' level. +### +casper.on 'resource.received', (resource) -> + infos = [] + props = [ + "url" + "status" + "statusText" + "redirectURL" + "bodySize" + ] + infos.push resource[prop] for prop in props + infos.push "[#{header.name}: #{header.value}]" for header in resource.headers + @log infos.join(", "), "verbose" + # add a new 'verbose' logging level at the lowest priority casper.logLevels = ["verbose"].concat casper.logLevels diff --git a/zephyr/tests/frontend/casperjs/samples/customlogging.js b/zephyr/tests/frontend/casperjs/samples/customlogging.js index 73bfc5004e..cbb64e045e 100644 --- a/zephyr/tests/frontend/casperjs/samples/customlogging.js +++ b/zephyr/tests/frontend/casperjs/samples/customlogging.js @@ -1,38 +1,37 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * A basic custom logging implementation. The idea is to (extremely) verbosely * log every received resource. */ - var casper = require("casper").create({ - /* - Every time a resource is received, a new log entry is added to the stack at - the 'verbose' level. - */ - onResourceReceived: function(self, resource) { - var header, infos, prop, props, _i, _j, _len, _len1, _ref; - infos = []; - props = [ - "url", - "status", - "statusText", - "redirectURL", - "bodySize" - ]; - for (_i = 0, _len = props.length; _i < _len; _i++) { - prop = props[_i]; - infos.push(resource[prop]); - } - _ref = resource.headers; - for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { - header = _ref[_j]; - infos.push("[" + header.name + ": " + header.value + "]"); - } - this.log(infos.join(", "), "verbose"); - }, verbose: true, logLevel: "verbose" }); +/** + * Every time a resource is received, a new log entry is added to the stack at + * the 'verbose' level. + */ +casper.on('resource.received', function(resource) { + var infos = []; + var props = [ + "url", + "status", + "statusText", + "redirectURL", + "bodySize" + ]; + props.forEach(function(prop) { + infos.push(resource[prop]); + }); + resource.headers.forEach(function(header) { + infos.push("[" + header.name + ": " + header.value + "]"); + }); + this.log(infos.join(", "), "verbose"); +}); + // add a new 'verbose' logging level at the lowest priority casper.logLevels = ["verbose"].concat(casper.logLevels); diff --git a/zephyr/tests/frontend/casperjs/samples/download.js b/zephyr/tests/frontend/casperjs/samples/download.js index 8e92278788..62adeb6f37 100644 --- a/zephyr/tests/frontend/casperjs/samples/download.js +++ b/zephyr/tests/frontend/casperjs/samples/download.js @@ -1,4 +1,7 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * download the google logo image onto the local filesystem */ diff --git a/zephyr/tests/frontend/casperjs/samples/dynamic.js b/zephyr/tests/frontend/casperjs/samples/dynamic.js index ff5cb0edea..4c4bb5a77b 100644 --- a/zephyr/tests/frontend/casperjs/samples/dynamic.js +++ b/zephyr/tests/frontend/casperjs/samples/dynamic.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create({ verbose: true }); diff --git a/zephyr/tests/frontend/casperjs/samples/each.js b/zephyr/tests/frontend/casperjs/samples/each.js index e484862400..5c57ec055e 100644 --- a/zephyr/tests/frontend/casperjs/samples/each.js +++ b/zephyr/tests/frontend/casperjs/samples/each.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create(); var links = [ diff --git a/zephyr/tests/frontend/casperjs/samples/events.js b/zephyr/tests/frontend/casperjs/samples/events.js index 755f6826cf..9defeec056 100644 --- a/zephyr/tests/frontend/casperjs/samples/events.js +++ b/zephyr/tests/frontend/casperjs/samples/events.js @@ -1,7 +1,9 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * This script will add a custom HTTP status code handler, here for 404 pages. */ - var casper = require("casper").create(); casper.on("http.status.200", function(resource) { diff --git a/zephyr/tests/frontend/casperjs/samples/extends.js b/zephyr/tests/frontend/casperjs/samples/extends.js index 7463634f64..276062e034 100644 --- a/zephyr/tests/frontend/casperjs/samples/extends.js +++ b/zephyr/tests/frontend/casperjs/samples/extends.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create({ loadImages: false, logLevel: "debug", @@ -34,4 +37,4 @@ Object.keys(links).forEach(function(url) { fantomas.run(function() { this.renderJSON(links); this.exit(); -}); \ No newline at end of file +}); diff --git a/zephyr/tests/frontend/casperjs/samples/googlelinks.js b/zephyr/tests/frontend/casperjs/samples/googlelinks.js index d2f72741a0..54c9fe24cc 100644 --- a/zephyr/tests/frontend/casperjs/samples/googlelinks.js +++ b/zephyr/tests/frontend/casperjs/samples/googlelinks.js @@ -1,3 +1,5 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ var links = []; var casper = require("casper").create(); diff --git a/zephyr/tests/frontend/casperjs/samples/googlematch.coffee b/zephyr/tests/frontend/casperjs/samples/googlematch.coffee index 6841a3f894..70a7f4e431 100644 --- a/zephyr/tests/frontend/casperjs/samples/googlematch.coffee +++ b/zephyr/tests/frontend/casperjs/samples/googlematch.coffee @@ -14,7 +14,7 @@ casper = require("casper").create verbose: true casper.fetchScore = -> @evaluate -> - result = document.querySelector('#resultStats').innerText + result = __utils__.findOne('#resultStats').innerText parseInt /Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, '') terms = casper.cli.args # terms are passed through command-line arguments diff --git a/zephyr/tests/frontend/casperjs/samples/googlematch.js b/zephyr/tests/frontend/casperjs/samples/googlematch.js index b92cbdf947..e32be75fad 100644 --- a/zephyr/tests/frontend/casperjs/samples/googlematch.js +++ b/zephyr/tests/frontend/casperjs/samples/googlematch.js @@ -1,4 +1,7 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * Takes provided terms passed as arguments and query google for the number of * estimated results each have. * @@ -16,7 +19,7 @@ var casper = require("casper").create({ casper.fetchScore = function() { return this.evaluate(function() { - var result = document.querySelector('#resultStats').innerText; + var result = __utils__.findOne('#resultStats').innerText; return parseInt(/Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, ''), 10); }); }; diff --git a/zephyr/tests/frontend/casperjs/samples/googlepagination.js b/zephyr/tests/frontend/casperjs/samples/googlepagination.js index f427d92360..737ac76cb7 100644 --- a/zephyr/tests/frontend/casperjs/samples/googlepagination.js +++ b/zephyr/tests/frontend/casperjs/samples/googlepagination.js @@ -1,4 +1,7 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * Capture multiple pages of google search results * * Usage: $ casperjs googlepagination.coffee my search terms diff --git a/zephyr/tests/frontend/casperjs/samples/googletesting.js b/zephyr/tests/frontend/casperjs/samples/googletesting.js index 6a9b3026fd..4cf07673ea 100644 --- a/zephyr/tests/frontend/casperjs/samples/googletesting.js +++ b/zephyr/tests/frontend/casperjs/samples/googletesting.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create({ logLevel: "debug" }); diff --git a/zephyr/tests/frontend/casperjs/samples/logcolor.js b/zephyr/tests/frontend/casperjs/samples/logcolor.js index 370e2be6cb..d4d969376a 100644 --- a/zephyr/tests/frontend/casperjs/samples/logcolor.js +++ b/zephyr/tests/frontend/casperjs/samples/logcolor.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create({ verbose: true, logLevel: "debug" diff --git a/zephyr/tests/frontend/casperjs/samples/metaextract.js b/zephyr/tests/frontend/casperjs/samples/metaextract.js index 319c6d2dae..c83a6f6bbf 100644 --- a/zephyr/tests/frontend/casperjs/samples/metaextract.js +++ b/zephyr/tests/frontend/casperjs/samples/metaextract.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create(); var url = casper.cli.get(0); var metas = []; diff --git a/zephyr/tests/frontend/casperjs/samples/multirun.js b/zephyr/tests/frontend/casperjs/samples/multirun.js index 28ac0d1d8d..edaf2ffb0d 100644 --- a/zephyr/tests/frontend/casperjs/samples/multirun.js +++ b/zephyr/tests/frontend/casperjs/samples/multirun.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var casper = require("casper").create({ verbose: true }); diff --git a/zephyr/tests/frontend/casperjs/samples/screenshot.coffee b/zephyr/tests/frontend/casperjs/samples/screenshot.coffee index b5dc69249c..19bc57e8ad 100644 --- a/zephyr/tests/frontend/casperjs/samples/screenshot.coffee +++ b/zephyr/tests/frontend/casperjs/samples/screenshot.coffee @@ -16,8 +16,8 @@ if not twitterAccount or not filename or not /\.(png|jpg|pdf)$/i.test filename .echo("Usage: $ casperjs screenshot.coffee ") .exit(1) -casper.start "https://twitter.com/#!/#{twitterAccount}", -> - @waitForSelector ".tweet-row", (-> +casper.start "https://twitter.com/#{twitterAccount}", -> + @waitForSelector ".stream-container", (-> @captureSelector filename, "html" @echo "Saved screenshot of #{@getCurrentUrl()} to #{filename}" ), (-> diff --git a/zephyr/tests/frontend/casperjs/samples/screenshot.js b/zephyr/tests/frontend/casperjs/samples/screenshot.js index d92631f5d7..f764fbd5b9 100644 --- a/zephyr/tests/frontend/casperjs/samples/screenshot.js +++ b/zephyr/tests/frontend/casperjs/samples/screenshot.js @@ -1,6 +1,9 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * This script will capture a screenshot of a twitter account page - * Usage: $ casperjs screenshot.coffee + * Usage: $ casperjs screenshot.js */ var casper = require("casper").create({ @@ -15,13 +18,13 @@ var filename = casper.cli.get(1); if (!twitterAccount || !filename || !/\.(png|jpg|pdf)$/i.test(filename)) { casper - .echo("Usage: $ casperjs screenshot.coffee ") + .echo("Usage: $ casperjs screenshot.js ") .exit(1) ; } -casper.start("https://twitter.com/#!/" + twitterAccount, function() { - this.waitForSelector(".tweet-row", (function() { +casper.start("https://twitter.com/" + twitterAccount, function() { + this.waitForSelector(".stream-container", (function() { this.captureSelector(filename, "html"); this.echo("Saved screenshot of " + (this.getCurrentUrl()) + " to " + filename); }), (function() { diff --git a/zephyr/tests/frontend/casperjs/samples/statushandlers.js b/zephyr/tests/frontend/casperjs/samples/statushandlers.js index cfdde27417..36b2b67541 100644 --- a/zephyr/tests/frontend/casperjs/samples/statushandlers.js +++ b/zephyr/tests/frontend/casperjs/samples/statushandlers.js @@ -1,4 +1,7 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * This script will add a custom HTTP status code handler, here for 404 pages. */ diff --git a/zephyr/tests/frontend/casperjs/samples/steptimeout.js b/zephyr/tests/frontend/casperjs/samples/steptimeout.js index afbb607020..0448d1b811 100644 --- a/zephyr/tests/frontend/casperjs/samples/steptimeout.js +++ b/zephyr/tests/frontend/casperjs/samples/steptimeout.js @@ -1,3 +1,6 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + var failed = []; var start = null; var links = [ diff --git a/zephyr/tests/frontend/casperjs/samples/timeout.js b/zephyr/tests/frontend/casperjs/samples/timeout.js index 40a14648ed..b5faab3744 100644 --- a/zephyr/tests/frontend/casperjs/samples/timeout.js +++ b/zephyr/tests/frontend/casperjs/samples/timeout.js @@ -1,4 +1,7 @@ -/* +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** * Just a silly game. * * $ casperjs samples/timeout.js 500 diff --git a/zephyr/tests/frontend/casperjs/samples/translate.coffee b/zephyr/tests/frontend/casperjs/samples/translate.coffee new file mode 100644 index 0000000000..72e560147b --- /dev/null +++ b/zephyr/tests/frontend/casperjs/samples/translate.coffee @@ -0,0 +1,23 @@ +### +Translation using the Google Translate Service. + +Usage: + +$ casperjs translate.coffee --target=fr "hello world" +bonjour tout le monde +### +system = require("system") +casper = require("casper").create() +format = require("utils").format +source = casper.cli.get("source") or "auto" +target = casper.cli.get("target") +text = casper.cli.get(0) +result = undefined + +casper.warn("The --target option is mandatory.").exit 1 unless target + +casper.start(format("http://translate.google.com/#%s/%s/%s", source, target, text), -> + @fill "form#gt-form", text: text +).waitForSelector "span.hps", -> @echo @fetchText("#result_box") + +casper.run() diff --git a/zephyr/tests/frontend/casperjs/samples/translate.js b/zephyr/tests/frontend/casperjs/samples/translate.js new file mode 100644 index 0000000000..5a0cb6e547 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/samples/translate.js @@ -0,0 +1,30 @@ +/*jshint strict:false*/ +/*global CasperError console phantom require*/ + +/** + * Translation using the Google Translate Service. + * + * Usage: + * + * $ casperjs translate.js --target=fr "hello world" + * bonjour tout le monde + */ +var system = require('system'), + casper = require('casper').create(), + format = require('utils').format, + source = casper.cli.get('source') || 'auto', + target = casper.cli.get('target'), + text = casper.cli.get(0), + result; + +if (!target) { + casper.warn('The --target option is mandatory.').exit(1); +} + +casper.start(format('http://translate.google.com/#%s/%s/%s', source, target, text), function() { + this.fill('form#gt-form', {text: text}); +}).waitForSelector('span.hps', function() { + this.echo(this.fetchText("#result_box")); +}); + +casper.run(); diff --git a/zephyr/tests/frontend/casperjs/tests/commands/mytest.js b/zephyr/tests/frontend/casperjs/tests/commands/mytest.js new file mode 100644 index 0000000000..7643e9cfb0 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/commands/mytest.js @@ -0,0 +1,14 @@ +/*jshint strict:false*/ +/*global CasperError casper console phantom require*/ +casper.start('about:blank', function() { + this.test.pass('ok1'); +}); + +casper.then(function() { + this.test.pass('ok2'); +}); + +casper.run(function() { + this.test.pass('ok3'); + this.test.done(); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/commands/script.js b/zephyr/tests/frontend/casperjs/tests/commands/script.js new file mode 100644 index 0000000000..56200cac07 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/commands/script.js @@ -0,0 +1,3 @@ +var casper = require('casper').create(); +casper.echo('it works'); +casper.exit(); diff --git a/zephyr/tests/frontend/casperjs/tests/run.js b/zephyr/tests/frontend/casperjs/tests/run.js index ce024e5773..6b3a75d4ef 100644 --- a/zephyr/tests/frontend/casperjs/tests/run.js +++ b/zephyr/tests/frontend/casperjs/tests/run.js @@ -1,4 +1,4 @@ -/*global phantom*/ +/*global phantom CasperError*/ if (!phantom.casperLoaded) { console.log('This script must be invoked using the casperjs executable'); @@ -21,10 +21,8 @@ function checkSelfTest(tests) { var isCasperTest = false; tests.forEach(function(test) { var testDir = fs.absolute(fs.dirname(test)); - if (fs.isDirectory(testDir)) { - if (fs.exists(fs.pathJoin(testDir, '.casper'))) { - isCasperTest = true; - } + if (fs.isDirectory(testDir) && fs.exists(fs.pathJoin(testDir, '.casper'))) { + isCasperTest = true; } }); return isCasperTest; @@ -52,52 +50,72 @@ function checkIncludeFile(include) { return absInclude; } -// parse some options from cli -casper.options.verbose = casper.cli.get('direct') || false; -casper.options.logLevel = casper.cli.get('log-level') || "error"; -if (casper.cli.get('no-colors') === true) { - var cls = 'Dummy'; - casper.options.colorizerType = cls; - casper.colorizer = colorizer.create(cls); -} - -// test paths are passed as args -if (casper.cli.args.length) { - tests = casper.cli.args.filter(function(path) { - "use strict"; - return fs.isFile(path) || fs.isDirectory(path); - }); -} else { - casper.echo('No test path passed, exiting.', 'RED_BAR', 80); - casper.exit(1); -} - -// check for casper selftests -if (!phantom.casperSelfTest && checkSelfTest(tests)) { - casper.warn('To run casper self tests, use the `selftest` command.'); - casper.exit(1); -} - -// includes handling -this.loadIncludes.forEach(function(include){ +function checkArgs() { "use strict"; - var container; - if (casper.cli.has(include)) { - container = casper.cli.get(include).split(',').map(function(file) { - return checkIncludeFile(file); - }).filter(function(file) { - return utils.isString(file); - }); - - casper.test.loadIncludes[include] = utils.unique(container); + // parse some options from cli + casper.options.verbose = casper.cli.get('direct') || false; + casper.options.logLevel = casper.cli.get('log-level') || "error"; + if (casper.cli.get('no-colors') === true) { + var cls = 'Dummy'; + casper.options.colorizerType = cls; + casper.colorizer = colorizer.create(cls); } -}); + casper.test.options.failFast = casper.cli.get('fail-fast') || false; -// test suites completion listener -casper.test.on('tests.complete', function() { + // test paths are passed as args + if (casper.cli.args.length) { + tests = casper.cli.args.filter(function(path) { + if (fs.isFile(path) || fs.isDirectory(path)) { + return true; + } + throw new CasperError(f("Invalid test path: %s", path)); + }); + } else { + casper.echo('No test path passed, exiting.', 'RED_BAR', 80); + casper.exit(1); + } + + // check for casper selftests + if (!phantom.casperSelfTest && checkSelfTest(tests)) { + casper.warn('To run casper self tests, use the `selftest` command.'); + casper.exit(1); + } +} + +function initRunner() { "use strict"; - this.renderResults(true, undefined, casper.cli.get('xunit') || undefined); -}); + // includes handling + loadIncludes.forEach(function(include){ + var container; + if (casper.cli.has(include)) { + container = casper.cli.get(include).split(',').map(function(file) { + return checkIncludeFile(file); + }).filter(function(file) { + return utils.isString(file); + }); + casper.test.loadIncludes[include] = utils.unique(container); + } + }); -// run all the suites -casper.test.runSuites.apply(casper.test, tests); + // test suites completion listener + casper.test.on('tests.complete', function() { + this.renderResults(true, undefined, casper.cli.get('xunit') || undefined); + if (this.options.failFast && this.testResults.failures.length > 0) { + casper.warn('Test suite failed fast, all tests may not have been executed.'); + } + }); +} + +var error; +try { + checkArgs(); +} catch (e) { + error = true; + casper.warn(e); + casper.exit(1); +} + +if (!error) { + initRunner(); + casper.test.runSuites.apply(casper.test, tests); +} diff --git a/zephyr/tests/frontend/casperjs/tests/sample_modules/config.json b/zephyr/tests/frontend/casperjs/tests/sample_modules/config.json new file mode 100644 index 0000000000..bb663e7083 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/sample_modules/config.json @@ -0,0 +1 @@ +{"ok": true} diff --git a/zephyr/tests/frontend/casperjs/tests/sample_modules/csmodule.coffee b/zephyr/tests/frontend/casperjs/tests/sample_modules/csmodule.coffee index ee0c5f1097..38949818a6 100644 --- a/zephyr/tests/frontend/casperjs/tests/sample_modules/csmodule.coffee +++ b/zephyr/tests/frontend/casperjs/tests/sample_modules/csmodule.coffee @@ -1,5 +1 @@ -try - exports.ok = true -catch e - casper.test.fail('error in coffeescript module code: ' + e) - casper.test.done() +exports.ok = true diff --git a/zephyr/tests/frontend/casperjs/tests/sample_modules/jsmodule.js b/zephyr/tests/frontend/casperjs/tests/sample_modules/jsmodule.js index 0a1bdaa3ab..e12f1fe5b5 100644 --- a/zephyr/tests/frontend/casperjs/tests/sample_modules/jsmodule.js +++ b/zephyr/tests/frontend/casperjs/tests/sample_modules/jsmodule.js @@ -1,7 +1 @@ -/*global casper*/ -try { - exports.ok = true; -} catch (e) { - casper.test.fail('error in js module code' + e); - casper.test.done() -} +exports.ok = true; diff --git a/zephyr/tests/frontend/casperjs/tests/selftest.js b/zephyr/tests/frontend/casperjs/tests/selftest.js index 147d0428ec..08011d669b 100644 --- a/zephyr/tests/frontend/casperjs/tests/selftest.js +++ b/zephyr/tests/frontend/casperjs/tests/selftest.js @@ -28,7 +28,11 @@ service = server.listen(testServerPort, function(request, response) { console.log(utils.format('Test server url not found: %s (file: %s)', request.url, pageFile), "warning"); response.write("404 - NOT FOUND"); } else { - response.statusCode = 200; + var headers = {}; + if (/js$/.test(pageFile)) { + headers['Content-Type'] = "application/javascript"; + } + response.writeHead(200, headers); response.write(fs.read(pageFile)); } response.close(); diff --git a/zephyr/tests/frontend/casperjs/tests/site/click.html b/zephyr/tests/frontend/casperjs/tests/site/click.html index ffcb8f564b..2422309a27 100644 --- a/zephyr/tests/frontend/casperjs/tests/site/click.html +++ b/zephyr/tests/frontend/casperjs/tests/site/click.html @@ -16,10 +16,11 @@ test2: false, test3: false, test4: false, - testdown: [], - testup: [], - testmove: [], - testclick: [] + testdown: [], + testup: [], + testmove: [], + testclick: [], + testdoubleclick: [] }; document.querySelector('#test4').onclick = function(event) { results.test4 = true; @@ -34,6 +35,9 @@ window.onmousemove = function(event) { results.testmove = [event.x, event.y]; }; + window.ondblclick = function(event) { + results.testdoubleclick = [event.x, event.y]; + }; })(window); diff --git a/zephyr/tests/frontend/casperjs/tests/site/field-array.html b/zephyr/tests/frontend/casperjs/tests/site/field-array.html new file mode 100644 index 0000000000..0d64d96186 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/field-array.html @@ -0,0 +1,14 @@ + + + + + CasperJS test form + + +
+ + + +
+ + diff --git a/zephyr/tests/frontend/casperjs/tests/site/frame1.html b/zephyr/tests/frontend/casperjs/tests/site/frame1.html new file mode 100644 index 0000000000..0452449e54 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/frame1.html @@ -0,0 +1,10 @@ + + + + + CasperJS frame 1 + + +

This is frame 1.

+ + diff --git a/zephyr/tests/frontend/casperjs/tests/site/frame2.html b/zephyr/tests/frontend/casperjs/tests/site/frame2.html new file mode 100644 index 0000000000..0d3429887a --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/frame2.html @@ -0,0 +1,11 @@ + + + + + CasperJS frame 2 + + +

This is frame 2.

+

frame 3

+ + diff --git a/zephyr/tests/frontend/casperjs/tests/site/frame3.html b/zephyr/tests/frontend/casperjs/tests/site/frame3.html new file mode 100644 index 0000000000..874999ce34 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/frame3.html @@ -0,0 +1,11 @@ + + + + + CasperJS frame 3 + + +

This is frame 3.

+

frame 2

+ + diff --git a/zephyr/tests/frontend/casperjs/tests/site/frames.html b/zephyr/tests/frontend/casperjs/tests/site/frames.html new file mode 100644 index 0000000000..3c75ec33d9 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/frames.html @@ -0,0 +1,12 @@ + + + + + CasperJS test frames + + + + + + + diff --git a/zephyr/tests/frontend/casperjs/tests/site/global.html b/zephyr/tests/frontend/casperjs/tests/site/global.html index 1ec80555ec..7a4714a6e5 100644 --- a/zephyr/tests/frontend/casperjs/tests/site/global.html +++ b/zephyr/tests/frontend/casperjs/tests/site/global.html @@ -4,6 +4,11 @@ - \ No newline at end of file + diff --git a/zephyr/tests/frontend/casperjs/tests/site/includes/include1.js b/zephyr/tests/frontend/casperjs/tests/site/includes/include1.js new file mode 100644 index 0000000000..cdb9688c10 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/includes/include1.js @@ -0,0 +1,6 @@ +(function() { + var elem = document.createElement('div'); + elem.setAttribute('id', 'include1'); + elem.appendChild(document.createTextNode('include1')); + document.querySelector('body').appendChild(elem); +})(); diff --git a/zephyr/tests/frontend/casperjs/tests/site/includes/include2.js b/zephyr/tests/frontend/casperjs/tests/site/includes/include2.js new file mode 100644 index 0000000000..f7ad9fc83f --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/includes/include2.js @@ -0,0 +1,6 @@ +(function() { + var elem = document.createElement('div'); + elem.setAttribute('id', 'include2'); + elem.appendChild(document.createTextNode('include2')); + document.querySelector('body').appendChild(elem); +})(); diff --git a/zephyr/tests/frontend/casperjs/tests/site/popup.html b/zephyr/tests/frontend/casperjs/tests/site/popup.html new file mode 100644 index 0000000000..ecdc6eb7c4 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/site/popup.html @@ -0,0 +1,19 @@ + + + + + CasperJS test popup + + close + + new window + + + diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/agent.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/agent.js index 8de41ac1bd..fcbcdb27e0 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/agent.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/agent.js @@ -22,5 +22,5 @@ casper.thenOpen('tests/site/index.html'); casper.run(function() { this.removeListener('resource.requested', fetchUA); - this.test.done(); + this.test.done(3); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/alert.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/alert.js new file mode 100644 index 0000000000..64ea183437 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/alert.js @@ -0,0 +1,14 @@ +/*global casper*/ +/*jshint strict:false*/ + +var ok = false; + +casper.on('remote.alert', function(message) { + ok = message === 'plop'; +}); + +casper.start('tests/site/alert.html').run(function() { + this.test.assert(ok, 'alert event has been intercepted'); + this.removeAllListeners('remote.alert'); + this.test.done(1); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/auth.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/auth.js new file mode 100644 index 0000000000..eeb07ba88a --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/auth.js @@ -0,0 +1,24 @@ +/*global casper*/ +/*jshint strict:false maxstatements:99*/ + +casper.start('tests/site/index.html'); + +casper.configureHttpAuth('http://localhost/'); +casper.test.assertEquals(casper.page.settings.userName, undefined); +casper.test.assertEquals(casper.page.settings.password, undefined); + +casper.configureHttpAuth('http://niko:plop@localhost/'); +casper.test.assertEquals(casper.page.settings.userName, 'niko'); +casper.test.assertEquals(casper.page.settings.password, 'plop'); + +casper.configureHttpAuth('http://localhost/', {username: 'john', password: 'doe'}); +casper.test.assertEquals(casper.page.settings.userName, 'john'); +casper.test.assertEquals(casper.page.settings.password, 'doe'); + +casper.configureHttpAuth('http://niko:plop@localhost/', {username: 'john', password: 'doe'}); +casper.test.assertEquals(casper.page.settings.userName, 'niko'); +casper.test.assertEquals(casper.page.settings.password, 'plop'); + +casper.run(function() { + this.test.done(8); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/capture.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/capture.js index 3310e2d38b..c8d4b645c8 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/capture.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/capture.js @@ -13,21 +13,19 @@ casper.start('tests/site/index.html', function() { this.test.assert(fs.isFile(testFile), 'Casper.capture() captured a screenshot'); }); -if (phantom.version.major === 1 && phantom.version.minor >= 6) { - casper.thenOpen('tests/site/index.html', function() { - this.test.comment('Casper.captureBase64()'); - this.test.assert(this.captureBase64('png').length > 0, - 'Casper.captureBase64() rendered a page capture as base64'); - this.test.assert(this.captureBase64('png', 'ul').length > 0, - 'Casper.captureBase64() rendered a capture from a selector as base64'); - this.test.assert(this.captureBase64('png', {top: 0, left: 0, width: 30, height: 30}).length > 0, - 'Casper.captureBase64() rendered a capture from a clipRect as base64'); - }); -} +casper.thenOpen('tests/site/index.html', function() { + this.test.comment('Casper.captureBase64()'); + this.test.assert(this.captureBase64('png').length > 0, + 'Casper.captureBase64() rendered a page capture as base64'); + this.test.assert(this.captureBase64('png', 'ul').length > 0, + 'Casper.captureBase64() rendered a capture from a selector as base64'); + this.test.assert(this.captureBase64('png', {top: 0, left: 0, width: 30, height: 30}).length > 0, + 'Casper.captureBase64() rendered a capture from a clipRect as base64'); +}); casper.run(function() { try { fs.remove(testFile); } catch(e) {} - this.test.done(); + this.test.done(4); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/click.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/click.js index f595c7f491..21ca196881 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/click.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/click.js @@ -1,5 +1,7 @@ /*global casper*/ -/*jshint strict:false*/ +/*jshint strict:false maxstatements: 99*/ +var utils = require('utils'); + casper.start('tests/site/index.html', function() { this.click('a[href="test.html"]'); }); @@ -56,8 +58,26 @@ casper.then(function() { this.mouse.move(200, 100); results = this.getGlobal('results'); this.test.assertEquals(results.testmove, [200, 100], 'Mouse.move() has moved to the specified position'); + + if (utils.gteVersion(phantom.version, '1.8.0')) { + this.test.comment('Mouse.doubleclick()'); + this.mouse.doubleclick(200, 100); + results = this.getGlobal('results'); + this.test.assertEquals(results.testdoubleclick, [200, 100], + 'Mouse.doubleclick() double-clicked the specified position'); + } else { + this.test.pass("Mouse.doubleclick() requires PhantomJS >= 1.8"); + } +}); + +// element focus on click +casper.then(function() { + this.page.content = '
' + this.click('form input[name=foo]') + this.page.sendEvent('keypress', 'bar'); + this.test.assertEquals(this.getFormValues('form')['foo'], 'bar', 'Casper.click() sets the focus on clicked element'); }); casper.run(function() { - this.test.done(); + this.test.done(23); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/confirm.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/confirm.js index de73fe8e28..de09bbf7ea 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/confirm.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/confirm.js @@ -1,23 +1,29 @@ /*global casper*/ /*jshint strict:false*/ -// skip this test for phantom versions < 1.5 -if (phantom.version.major === 1 && phantom.version.minor < 6) { - casper.test.comment('Skipped tests, PhantomJS 1.6 required'); - casper.test.done(); -} else { - var received; +var received; +casper.setFilter('page.confirm', function(message) { + received = message; + return true; +}); + +casper.start('tests/site/confirm.html', function() { + this.test.assert(this.getGlobal('confirmed'), 'confirmation dialog accepted'); +}); + +casper.then(function() { + //remove the page.confirm event filter so we can add a new one + casper.removeAllFilters('page.confirm') casper.setFilter('page.confirm', function(message) { - received = message; - return true; + return false; }); +}); - casper.start('tests/site/confirm.html', function() { - this.test.assert(this.getGlobal('confirmed'), 'confirmation received'); - }); +casper.thenOpen('/tests/site/confirm.html', function() { + this.test.assertNot(this.getGlobal('confirmed'), 'confirmation dialog canceled'); +}); - casper.run(function() { - this.test.assertEquals(received, 'are you sure?', 'confirmation message is ok'); - this.test.done(); - }); -} +casper.run(function() { + this.test.assertEquals(received, 'are you sure?', 'confirmation message is ok'); + this.test.done(3); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/debug.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/debug.js index 9cd16e768d..1e11bf627c 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/debug.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/debug.js @@ -6,5 +6,5 @@ casper.start('tests/site/index.html', function() { }); casper.run(function() { - casper.test.done(); + casper.test.done(2); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/elementattribute.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/elementattribute.js index 73f67b9d3e..f92be4bb31 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/elementattribute.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/elementattribute.js @@ -6,5 +6,5 @@ casper.start('tests/site/elementattribute.html', function() { }); casper.run(function() { - this.test.done(); + this.test.done(1); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/encode.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/encode.js index f60419ae63..23f67a5b9f 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/encode.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/encode.js @@ -20,5 +20,5 @@ casper.start('file://' + phantom.casperPath + '/tests/site/index.html', function }); casper.run(function() { - this.test.done(); + this.test.done(2); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/evaluate.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/evaluate.js index 55adde98fe..a2073eb165 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/evaluate.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/evaluate.js @@ -1,5 +1,5 @@ /*global casper*/ -/*jshint strict:false*/ +/*jshint strict:false maxparams:99*/ casper.test.comment('Casper.evaluate()'); casper.start(); @@ -32,4 +32,56 @@ casper.test.assertEquals(result.toString(), ['boolean', 'boolean', 'number', 'number', 'string', 'object', 'object', 'function'].toString(), 'Casper.evaluate() handles passed argument context correcly'); -casper.test.done(); +// no context +casper.test.assertEquals(casper.evaluate(function() { + return 42; +}), 42, 'Casper.evaluate() handles evaluation with no context passed'); + +// object context (previous casperjs versions compatibility mode) +casper.test.assertEquals(casper.evaluate(function(a) { + return [a]; +}, {a: "foo"}), ["foo"], 'Casper.evaluate() accepts an object as arguments context'); +casper.test.assertEquals(casper.evaluate(function(a, b) { + return [a, b]; +}, {a: "foo", b: "bar"}), ["foo", "bar"], 'Casper.evaluate() accepts an object as arguments context'); +casper.test.assertEquals(casper.evaluate(function(a, b, c) { + return [a, b, c]; +}, {a: "foo", b: "bar", c: "baz"}), ["foo", "bar", "baz"], 'Casper.evaluate() accepts an object as arguments context'); + +// array context +casper.test.assertEquals(casper.evaluate(function(a) { + return [a]; +}, ["foo"]), ["foo"], 'Casper.evaluate() accepts an array as arguments context'); +casper.test.assertEquals(casper.evaluate(function(a, b) { + return [a, b]; +}, ["foo", "bar"]), ["foo", "bar"], 'Casper.evaluate() accepts an array as arguments context'); +casper.test.assertEquals(casper.evaluate(function(a, b, c) { + return [a, b, c]; +}, ["foo", "bar", "baz"]), ["foo", "bar", "baz"], 'Casper.evaluate() accepts an array as arguments context'); + +// natural arguments context (phantomjs equivalent) +casper.test.assertEquals(casper.evaluate(function(a) { + return [a]; +}, "foo"), ["foo"], 'Casper.evaluate() accepts natural arguments context'); +casper.test.assertEquals(casper.evaluate(function(a, b) { + return [a, b]; +}, "foo", "bar"), ["foo", "bar"], 'Casper.evaluate() accepts natural arguments context'); +casper.test.assertEquals(casper.evaluate(function(a, b, c) { + return [a, b, c]; +}, "foo", "bar", "baz"), ["foo", "bar", "baz"], 'Casper.evaluate() accepts natural arguments context'); + +casper.start().thenEvaluate(function(a, b) { + window.a = a + window.b = b; +}, "foo", "bar"); + +casper.then(function() { + this.test.comment('Casper.thenEvaluate()'); + this.test.assertEquals(this.getGlobal('a'), "foo", "Casper.thenEvaluate() sets args"); + this.test.assertEquals(this.getGlobal('b'), "bar", + "Casper.thenEvaluate() sets args the same way evaluate() does"); +}); + +casper.run(function() { + this.test.done(13); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/events.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/events.js index ec3f784a5f..bec43252b3 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/events.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/events.js @@ -37,4 +37,4 @@ casper.test.assertEquals(casper.foo, 42, "filter() applies the correct context") delete casper.foo; -casper.test.done(); +casper.test.done(5); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/exists.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/exists.js index 9839b70e7e..aa141ce69d 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/exists.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/exists.js @@ -7,5 +7,5 @@ casper.start('tests/site/index.html', function() { }); casper.run(function() { - this.test.done(); + this.test.done(1); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/fetchtext.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/fetchtext.js index ffd072ac94..59a0a9552a 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/fetchtext.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/fetchtext.js @@ -7,5 +7,5 @@ casper.start('tests/site/index.html', function() { }); casper.run(function() { - this.test.done(); + this.test.done(1); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/flow.coffee b/zephyr/tests/frontend/casperjs/tests/suites/casper/flow.coffee index 9413b8ec63..959b1e9af8 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/flow.coffee +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/flow.coffee @@ -35,4 +35,4 @@ casper.then -> casper.then -> @test.assertEquals ++step, 13, "last step" -casper.run(-> @test.done()) +casper.run(-> @test.done(13)) diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/formfill.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/formfill.js index c2cc8cf18f..e30197e460 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/formfill.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/formfill.js @@ -41,6 +41,23 @@ casper.start('tests/site/form.html', function() { !document.querySelector('input[name="checklist[]"][value="2"]').checked && document.querySelector('input[name="checklist[]"][value="3"]').checked); }, true, 'Casper.fill() can fill a list of checkboxes'); + +}); + +casper.then(function() { + this.test.comment('Casper.getFormValues()'); + this.test.assertEquals(this.getFormValues('form'), { + "check": true, + "checklist[]": ["1", "3"], + "choice": "no", + "content": "Am watching thou", + "email": "chuck@norris.com", + "file": "C:\\fakepath\\README.md", + "password": "chuck", + "submit": "submit", + "topic": "bar" + }, 'Casper.getFormValues() retrieves filled values'); + this.test.comment('submitting form'); this.click('input[type="submit"]'); }); @@ -54,6 +71,33 @@ casper.then(function() { this.test.assertUrlMatch(/topic=bar/, 'Casper.fill() select field was submitted'); }); +casper.thenOpen('tests/site/form.html', function() { + this.fill('form[action="result.html"]', { + email: 'chuck@norris.com', + password: 'chuck', + content: 'Am watching thou', + check: true, + choice: 'yes', + topic: 'bar', + file: phantom.libraryPath + '/README.md', + 'checklist[]': ['1', '3'] + }); +}); + +casper.then(function() { + this.test.assertEquals(this.getFormValues('form'), { + "check": true, + "checklist[]": ["1", "3"], + "choice": "yes", + "content": "Am watching thou", + "email": "chuck@norris.com", + "file": "C:\\fakepath\\README.md", + "password": "chuck", + "submit": "submit", + "topic": "bar" + }, 'Casper.getFormValues() correctly retrieves values from radio inputs regardless of order'); +}); + casper.thenOpen('tests/site/form.html', function() { this.test.comment('Unexistent fields'); this.test.assertRaises(this.fill, ['form[action="result.html"]', { @@ -67,12 +111,21 @@ casper.thenOpen('tests/site/multiple-forms.html', function() { this.fill('form[name="f2"]', { yo: "ok" }, true); -}); - -casper.then(function() { +}).then(function() { this.test.assertUrlMatch(/\?f=f2&yo=ok$/, 'Casper.fill() handles multiple forms'); }); -casper.run(function() { - this.test.done(); +// issue #267: array syntax field names +casper.thenOpen('tests/site/field-array.html', function() { + this.test.comment('Field arrays'); + this.fill('form', { + 'foo[bar]': "bar", + 'foo[baz]': "baz" + }, true); +}).then(function() { + this.test.assertUrlMatch('?foo[bar]=bar&foo[baz]=baz', 'Casper.fill() handles array syntax field names'); +}); + +casper.run(function() { + this.test.done(20); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/frames.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/frames.js new file mode 100644 index 0000000000..0b0c909252 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/frames.js @@ -0,0 +1,43 @@ +/*global casper __utils__*/ +/*jshint strict:false*/ +casper.start('tests/site/frames.html'); + +casper.withFrame('frame1', function() { + this.test.assertTitle('CasperJS frame 1'); + this.test.assertExists("#f1"); + this.test.assertDoesntExist("#f2"); + this.test.assertEval(function() { + return '__utils__' in window && 'getBinary' in __utils__; + }, '__utils__ object is available in child frame'); + this.test.assertMatches(this.page.frameContent, /This is frame 1/); + this.test.assertMatches(this.getHTML(), /This is frame 1/); +}); + +casper.withFrame('frame2', function() { + this.test.assertTitle('CasperJS frame 2'); + this.test.assertExists("#f2"); + this.test.assertDoesntExist("#f1"); + this.test.assertEval(function() { + return '__utils__' in window && 'getBinary' in __utils__; + }, '__utils__ object is available in other child frame'); + this.clickLabel('frame 3'); +}); + +casper.withFrame('frame2', function() { + this.test.assertTitle('CasperJS frame 3'); +}); + +casper.withFrame(0, function() { + this.test.assertTitle('CasperJS frame 1'); + this.test.assertExists("#f1"); + this.test.assertDoesntExist("#f2"); +}); + +casper.withFrame(1, function() { + this.test.assertTitle('CasperJS frame 3'); +}); + +casper.run(function() { + this.test.assertTitle('CasperJS test frames'); + this.test.done(16); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/global.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/global.js index 016dee1e50..5e64bbd897 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/global.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/global.js @@ -2,10 +2,14 @@ /*jshint strict:false*/ casper.start('tests/site/global.html', function() { this.test.comment('Casper.getGlobal()'); - this.test.assertEquals(this.getGlobal('myGlobal'), 'awesome string', 'Casper.getGlobal() can retrieve a remote global variable'); - this.test.assertRaises(this.getGlobal, ['myUnencodableGlobal'], 'Casper.getGlobal() does not fail trying to encode an unencodable global'); + this.test.assertEquals(this.getGlobal('myGlobal'), 'awesome string', + 'Casper.getGlobal() can retrieve a remote global variable'); + this.test.assertEquals(this.getGlobal('myObject').foo.bar, 'baz', + 'Casper.getGlobal() can retrieves a serializable object'); + this.test.assertRaises(this.getGlobal, ['myUnencodableGlobal'], + 'Casper.getGlobal() does not fail trying to encode an unserializable global'); }); casper.run(function() { - this.test.done(); + this.test.done(3); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/headers.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/headers.js index 3dc725b68f..434d2a470b 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/headers.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/headers.js @@ -37,5 +37,5 @@ casper.thenOpen('http://localhost:8090/', function thenLocalhost(response) { casper.run(function() { server.close(); - this.test.done(); + this.test.done(4); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/history.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/history.js index 4a9b4926c4..4406bdf988 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/history.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/history.js @@ -19,5 +19,5 @@ casper.then(function() { casper.run(function() { this.test.assert(this.history.length > 0, 'Casper.history contains urls'); this.test.assertMatch(this.history[0], /tests\/site\/page1\.html$/, 'Casper.history has the correct first url'); - this.test.done(); + this.test.done(4); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/hooks.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/hooks.js index 19442547a3..4cff45ae84 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/hooks.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/hooks.js @@ -39,5 +39,5 @@ casper.then(function() { casper.run(function() { this.options.onAlert = null; - this.test.done(); + this.test.done(5); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/keys.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/keys.js new file mode 100644 index 0000000000..eebb2a4445 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/keys.js @@ -0,0 +1,15 @@ +/*jshint strict:false*/ +/*global CasperError casper console phantom require*/ +casper.start('tests/site/form.html', function() { + this.sendKeys('input[name="email"]', 'duke@nuk.em'); + this.sendKeys('textarea', "Damn, I’m looking good."); + var values = this.getFormValues('form'); + this.test.assertEquals(values['email'], 'duke@nuk.em', + 'Casper.sendKeys() sends keys to given input'); + this.test.assertEquals(values['content'], "Damn, I’m looking good.", + 'Casper.sendKeys() sends keys to given textarea'); +}); + +casper.run(function() { + this.test.done(2); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/location.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/location.js new file mode 100644 index 0000000000..cb86ce1336 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/location.js @@ -0,0 +1,23 @@ +/*jshint strict:false*/ +/*global CasperError casper console phantom require*/ +var utils = require('utils') + +if (utils.ltVersion(phantom.version, '1.8.0')) { + // https://github.com/n1k0/casperjs/issues/101 + casper.warn('document.location is broken under phantomjs < 1.8'); + casper.test.done(); +} else { + casper.start('tests/site/index.html', function() { + this.evaluate(function() { + document.location = '/tests/site/form.html'; + }); + }); + + casper.then(function() { + this.test.assertUrlMatches(/form\.html$/); + }); + + casper.run(function() { + this.test.done(); + }); +} diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/logging.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/logging.js index 66dd29e934..a77ee285d3 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/logging.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/logging.js @@ -36,5 +36,5 @@ casper.then(function() { casper.run(function() { this.test.assertEquals(this.result.log.length, 3, 'Casper.log() logged messages'); - this.test.done(); + this.test.done(4); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/mouseevents.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/mouseevents.js index 24b9dbf096..9aaeedc73e 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/mouseevents.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/mouseevents.js @@ -25,5 +25,5 @@ casper.then(function() { }); casper.run(function() { - this.test.done(); + this.test.done(16); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/onerror.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/onerror.js index 3e87db7fdc..4e675e328d 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/onerror.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/onerror.js @@ -17,5 +17,5 @@ casper.thenOpen('tests/site/error.html', function() { }); casper.run(function() { - this.test.done(); + this.test.done(2); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/open.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/open.js index 08fc4cfd77..6e4bc7ddcd 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/open.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/open.js @@ -48,7 +48,7 @@ var t = casper.test, current = 0, tests = [ username: 'bob', password: 'sinclar' }, "Casper.thenOpen() used the expected HTTP auth settings"); - }, + } ]; casper.start(); @@ -131,5 +131,5 @@ casper.thenOpen('tests/site/index.html', { casper.run(function() { this.removeAllListeners('open'); - t.done(); + t.done(16); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/popup.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/popup.js new file mode 100644 index 0000000000..d03a345bfc --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/popup.js @@ -0,0 +1,86 @@ +/*jshint strict:false*/ +/*global CasperError casper console phantom require*/ +var utils = require('utils'); +var x = require('casper').selectXPath; + +casper.on('popup.created', function(popup) { + this.test.pass('"popup.created" event is fired'); + this.test.assert(utils.isWebPage(popup), + '"popup.created" event callback get a popup page instance'); +}); + +casper.on('popup.loaded', function(popup) { + this.test.pass('"popup.loaded" event is fired'); + this.test.assertEquals(popup.evaluate(function() { + return document.title; + }), 'CasperJS test index', + '"popup.loaded" is triggered when popup content is actually loaded'); +}); + +casper.on('popup.closed', function(popup) { + this.test.assertEquals(this.popups.length, 0, '"popup.closed" event is fired'); +}); + +casper.start('tests/site/popup.html'); + +casper.waitForPopup('index.html', function() { + this.test.pass('Casper.waitForPopup() waits for a popup being created'); + this.test.assertEquals(this.popups.length, 1, 'A popup has been added'); + this.test.assert(utils.isWebPage(this.popups[0]), 'A popup is a WebPage'); +}); + +casper.withPopup('index.html', function() { + this.test.assertUrlMatches(/index\.html$/, + 'Casper.withPopup() switched to popup as current active one'); + this.test.assertEval(function() { + return '__utils__' in window; + }, 'Casper.withPopup() has client utils injected'); + this.test.assertExists('h1', + 'Casper.withPopup() can perform assertions on the DOM'); + this.test.assertExists(x('//h1'), + 'Casper.withPopup() can perform assertions on the DOM using XPath'); +}); + +casper.then(function() { + this.test.assertUrlMatches(/popup\.html$/, + 'Casper.withPopup() has reverted to main page after using the popup'); +}); + +casper.thenClick('.close', function() { + this.test.assertEquals(this.popups.length, 0, 'Popup is removed when closed'); +}); + +casper.thenOpen('tests/site/popup.html'); + +casper.waitForPopup(/index\.html$/, function() { + this.test.pass('Casper.waitForPopup() waits for a popup being created'); +}); + +casper.withPopup(/index\.html$/, function() { + this.test.assertTitle('CasperJS test index', + 'Casper.withPopup() can use a regexp to identify popup'); +}); + +casper.thenClick('.close', function() { + this.test.assertUrlMatches(/popup\.html$/, + 'Casper.withPopup() has reverted to main page after using the popup'); + this.test.assertEquals(this.popups.length, 0, 'Popup is removed when closed'); + this.removeAllListeners('popup.created'); + this.removeAllListeners('popup.loaded'); + this.removeAllListeners('popup.closed'); +}); + +casper.thenClick('a[target="_blank"]'); + +casper.waitForPopup('form.html', function() { + this.test.pass('Casper.waitForPopup() waits when clicked on a link with target=_blank'); +}); + +casper.withPopup('form.html', function() { + this.test.assertTitle('CasperJS test form'); +}); + +casper.run(function() { + // removes event listeners as they've now been tested already + this.test.done(25); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/prompt.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/prompt.js index 7660777bbf..3336567860 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/prompt.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/prompt.js @@ -1,19 +1,13 @@ /*global casper*/ /*jshint strict:false*/ -// skip this test for phantom versions < 1.5 -if (phantom.version.major === 1 && phantom.version.minor < 6) { - casper.test.comment('Skipped tests, PhantomJS 1.6 required'); - casper.test.done(); -} else { - casper.setFilter('page.prompt', function(message, value) { - return 'Chuck ' + value; - }); +casper.setFilter('page.prompt', function(message, value) { + return 'Chuck ' + value; +}); - casper.start('tests/site/prompt.html', function() { - this.test.assertEquals(this.getGlobal('name'), 'Chuck Norris', 'prompted value has been received'); - }); +casper.start('tests/site/prompt.html', function() { + this.test.assertEquals(this.getGlobal('name'), 'Chuck Norris', 'prompted value has been received'); +}); - casper.run(function() { - this.test.done(); - }); -} +casper.run(function() { + this.test.done(1); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/request.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/request.js new file mode 100644 index 0000000000..4d31fcfe48 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/request.js @@ -0,0 +1,36 @@ +/*global casper*/ +/*jshint strict:false*/ +function testHeader(header) { + return header.name === 'Accept' && header.value === 'application/json'; +} + +var t = casper.test, current = 0, tests = [ + function(request) { + t.assertNot(request.headers.some(testHeader), "Casper.open() sets no custom header by default"); + }, + function(request) { + t.assert(request.headers.some(testHeader), "Casper.open() can set a custom header"); + }, + function(request) { + t.assertNot(request.headers.some(testHeader), "Casper.open() custom headers option is not persistent"); + } +]; + +casper.on('page.resource.requested', function(request) { + tests[current++](request); +}); + +casper.start(); + +casper.thenOpen('tests/site/index.html'); +casper.thenOpen('tests/site/index.html', { + headers: { + Accept: 'application/json' + } +}); +casper.thenOpen('tests/site/index.html'); + +casper.run(function() { + this.removeAllListeners('page.resource.requested'); + t.done(3); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/resources.coffee b/zephyr/tests/frontend/casperjs/tests/suites/casper/resources.coffee index 3432ebb33d..829239643e 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/resources.coffee +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/resources.coffee @@ -21,4 +21,4 @@ casper.start "tests/site/resources.html", -> onTimeout = -> @test.fail "waitForResource timeout occured" @waitForResource "dummy.js", onTime, onTimeout -casper.run(-> @test.done()) +casper.run(-> @test.done(5)) diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/scripts.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/scripts.js new file mode 100644 index 0000000000..e025121615 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/scripts.js @@ -0,0 +1,32 @@ +/*global casper*/ +/*jshint strict:false*/ +casper.options.remoteScripts = [ + 'includes/include1.js', // local includes are actually served + 'includes/include2.js', // through the local test webserver + 'http://code.jquery.com/jquery-1.8.3.min.js' +]; + +casper.start('tests/site/index.html', function() { + this.test.assertSelectorHasText('#include1', 'include1', + 'Casper.includeRemoteScripts() includes a first remote script on start'); + this.test.assertSelectorHasText('#include2', 'include2', + 'Casper.includeRemoteScripts() includes a second remote script on start'); + this.test.assertEval(function() { + return 'jQuery' in window; + }, 'Casper.includeRemoteScripts() includes a really remote file on first step'); +}); + +casper.thenOpen('tests/site/form.html', function() { + this.test.assertSelectorHasText('#include1', 'include1', + 'Casper.includeRemoteScripts() includes a first remote script on second step'); + this.test.assertSelectorHasText('#include2', 'include2', + 'Casper.includeRemoteScripts() includes a second remote script on second step'); + this.test.assertEval(function() { + return 'jQuery' in window; + }, 'Casper.includeRemoteScripts() includes a really remote file on second step'); +}); + +casper.run(function() { + this.options.remoteScripts = []; + this.test.done(6); +}); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/start.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/start.js index aa3ba02325..fa5b588221 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/start.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/start.js @@ -13,5 +13,5 @@ casper.start('tests/site/index.html', function() { casper.test.assert(casper.started, 'Casper.start() started'); casper.run(function() { - this.test.done(); + this.test.done(4); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/steps.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/steps.js index 14fab001be..c3c90f4edb 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/steps.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/steps.js @@ -30,5 +30,5 @@ casper.each([1, 2, 3], function(self, item, i) { }); casper.run(function() { - this.test.done(); + this.test.done(8); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/urls.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/urls.js index 880f0125a8..f37724bba8 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/urls.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/urls.js @@ -17,6 +17,5 @@ casper.then(function() { casper.run(function() { this.test.assertHttpStatus(200); this.test.assertUrlMatches('Forlì', 'Casper.getCurrentUrl() retrieves a decoded URL'); - this.test.done(); + this.test.done(6); }); - diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/viewport.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/viewport.js index 513366bb03..0fece1a60e 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/viewport.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/viewport.js @@ -10,4 +10,4 @@ casper.test.assertEquals(casper.page.viewportSize.width, 1337, 'Casper.viewport( casper.test.assertEquals(casper.page.viewportSize.height, 999, 'Casper.viewport() can change the height of page viewport'); casper.test.assertRaises(casper.viewport, ['a', 'b'], 'Casper.viewport() validates viewport size data'); -casper.test.done(); +casper.test.done(3); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/visible.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/visible.js index d673eea3b5..7079e84050 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/visible.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/visible.js @@ -15,5 +15,5 @@ casper.start('tests/site/visible.html', function() { }); casper.run(function() { - this.test.done(); + this.test.done(4); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/wait.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/wait.js index 74850e963a..00f5a209ab 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/wait.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/wait.js @@ -9,18 +9,18 @@ casper.start('tests/site/index.html', function() { casper.wait(1000, function() { this.test.comment('Casper.wait()'); this.test.assert(new Date().getTime() - waitStart > 1000, 'Casper.wait() can wait for a given amount of time'); - // Casper.waitFor() - casper.thenOpen('tests/site/waitFor.html', function() { - this.test.comment('Casper.waitFor()'); - this.waitFor(function() { - return this.evaluate(function() { - return document.querySelectorAll('li').length === 4; - }); - }, function() { - this.test.pass('Casper.waitFor() can wait for something to happen'); - }, function() { - this.test.fail('Casper.waitFor() can wait for something to happen'); +}); + +casper.thenOpen('tests/site/waitFor.html', function() { + this.test.comment('Casper.waitFor()'); + this.waitFor(function() { + return this.evaluate(function() { + return document.querySelectorAll('li').length === 4; }); + }, function() { + this.test.pass('Casper.waitFor() can wait for something to happen'); + }, function() { + this.test.fail('Casper.waitFor() can wait for something to happen'); }); }); @@ -32,6 +32,14 @@ casper.thenOpen('tests/site/waitFor.html').waitForText('
  • four
  • ', function this.test.fail('Casper.waitForText() can wait for text'); }); -casper.run(function() { - this.test.done(); +casper.thenOpen('tests/site/waitFor.html').waitForText(/four/i, function() { + this.test.comment('Casper.waitForText()'); + this.test.pass('Casper.waitForText() can wait for regexp'); +}, function() { + this.test.comment('Casper.waitForText()'); + this.test.fail('Casper.waitForText() can wait for regexp'); +}); + +casper.run(function() { + this.test.done(4); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/casper/xpath.js b/zephyr/tests/frontend/casperjs/tests/suites/casper/xpath.js index 4a85ace684..66a8eb5f15 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/casper/xpath.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/casper/xpath.js @@ -30,5 +30,5 @@ casper.thenClick(x('/html/body/a[2]'), function() { }); casper.run(function() { - this.test.done(); + this.test.done(6); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/cli.js b/zephyr/tests/frontend/casperjs/tests/suites/cli.js index 0a10073926..7370bd3e11 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/cli.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/cli.js @@ -1,5 +1,5 @@ /*global casper*/ -/*jshint strict:false*/ +/*jshint strict:false maxstatements:99*/ var cli = require('cli'), t = casper.test; t.comment('parse(), get(), has()'); @@ -124,4 +124,4 @@ t.comment('parse(), get(), has()'); }, 'drop() did not affect other raw options'); })(cli.parse(['foo & bar', 'baz & boz', '--universe=42', '--lap=13.37', '--chucknorris', '--oops=false'])); -t.done(); +t.done(76); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/clientutils.js b/zephyr/tests/frontend/casperjs/tests/suites/clientutils.js index cce9227e6f..e1df22c99f 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/clientutils.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/clientutils.js @@ -52,6 +52,8 @@ function fakeDocument(html) { // scoped var scope = clientutils.findOne('ul'); casper.test.assertType(clientutils.findAll('li', scope), 'nodelist', 'ClientUtils.findAll() can find matching DOM elements within a given scope'); + casper.test.assertEquals(clientutils.findAll('li', scope).length, 2, 'ClientUtils.findAll() can find matching DOM elements within a given scope'); + casper.test.assertType(clientutils.findAll(x('//li'), scope), 'array', 'ClientUtils.findAll() can find matching DOM elements using XPath within a given scope'); fakeDocument(null); })(casper); @@ -63,8 +65,8 @@ function fakeDocument(html) { casper.test.assertNot(clientutils.findOne('ol'), 'ClientUtils.findOne() can find a matching DOM element'); // scoped var scope = clientutils.findOne('ul'); - casper.test.assertType(clientutils.findAll('li', scope), 'nodelist', 'ClientUtils.findAll() can find matching DOM elements within a given scope'); - casper.test.assertEquals(clientutils.findAll('li', scope).length, 2, 'ClientUtils.findAll() can find matching DOM elements within a given scope'); + casper.test.assertType(clientutils.findOne('li', scope), 'htmllielement', 'ClientUtils.findOne() can find a matching DOM element within a given scope'); + casper.test.assertType(clientutils.findOne(x('//li'), scope), 'htmllielement', 'ClientUtils.findOne() can find a matching DOM element using XPath within a given scope'); fakeDocument(null); })(casper); @@ -95,6 +97,7 @@ function fakeDocument(html) { // getElementsBounds casper.start(); casper.then(function() { + this.test.comment('Casper.getElementsBounds()'); var html = '
    '; html += '
    '; html += '
    '; @@ -108,6 +111,28 @@ function fakeDocument(html) { }); })(casper); +(function(casper) { + // element information + casper.test.comment('ClientUtils.getElementInfo()'); + casper.page.content = 'paf'; + var info = casper.getElementInfo('a.plip'); + casper.test.assertEquals(info.nodeName, 'a', 'ClientUtils.getElementInfo() retrieves element name'); + casper.test.assertEquals(info.attributes, { + 'href': 'plop', + 'class': 'plip plup' + }, 'ClientUtils.getElementInfo() retrieves element attributes'); + casper.test.assertEquals(info.html, 'paf', 'ClientUtils.getElementInfo() retrieves element html content'); + casper.test.assertEquals(info.text, 'paf', 'ClientUtils.getElementInfo() retrieves element text'); + casper.test.assert(info.x > 0, 'ClientUtils.getElementInfo() retrieves element x pos'); + casper.test.assert(info.y > 0, 'ClientUtils.getElementInfo() retrieves element y pos'); + casper.test.assert(info.width > 0, 'ClientUtils.getElementInfo() retrieves element width'); + casper.test.assert(info.height > 0, 'ClientUtils.getElementInfo() retrieves element height'); + casper.test.assert(info.visible, 'ClientUtils.getElementInfo() retrieves element visibility'); + casper.test.assertEquals(info.tag, 'paf', + 'ClientUtils.getElementInfo() retrieves element whole tag contents'); + +})(casper); + casper.run(function() { - this.test.done(); + this.test.done(40); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/coffee.coffee b/zephyr/tests/frontend/casperjs/tests/suites/coffee.coffee index fb03260acd..dcdb12e604 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/coffee.coffee +++ b/zephyr/tests/frontend/casperjs/tests/suites/coffee.coffee @@ -16,4 +16,4 @@ casper.then -> casper.run -> @test.assertEquals steps, 3, "Casper.options.onStepComplete() is called on step complete" @options.onStepComplete = null - @test.done() + @test.done(4) diff --git a/zephyr/tests/frontend/casperjs/tests/suites/fs.js b/zephyr/tests/frontend/casperjs/tests/suites/fs.js index 3086a9a9bb..67bca6ae76 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/fs.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/fs.js @@ -35,4 +35,4 @@ var fs = require('fs'), t = casper.test; } })(); -t.done(); +t.done(14); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/http_status.js b/zephyr/tests/frontend/casperjs/tests/suites/http_status.js index a45b05b775..4a0764eba5 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/http_status.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/http_status.js @@ -27,7 +27,7 @@ var codes = [100, 101, 102, 118, 200, 201, 202, 203, 204, 205, 206, 207, 210, 414, 415, 416, 417, 418, 422, 423, 424, 425, 426, 449, 450, 500, 501, 502, 503, 504, 505, 507, 509]; -casper.thenOpen('http://google.com').each(codes, function(self, code) { +casper.each(codes, function(self, code) { if (code === 100) { // HTTP 100 is CONTINUE, so don't expect a terminated response return; @@ -40,5 +40,5 @@ casper.thenOpen('http://google.com').each(codes, function(self, code) { casper.run(function() { server.close(); - this.test.done(); + this.test.done(109); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/injector.js b/zephyr/tests/frontend/casperjs/tests/suites/injector.js deleted file mode 100644 index 246bd358da..0000000000 --- a/zephyr/tests/frontend/casperjs/tests/suites/injector.js +++ /dev/null @@ -1,66 +0,0 @@ -/*global casper*/ -/*jshint strict:false*/ -var t = casper.test; -var createInjector = function(fn, values) { - return require('injector').create(fn, values); -}; -var testFn = function(a, b) { return a + b; }; -var injector = createInjector(testFn); -var extract = injector.extract(testFn); - -t.comment('FunctionArgsInjector.extract()'); -t.assertType(extract, "object", 'FunctionArgsInjector.extract() returns an object'); -t.assertEquals(extract.name, null, 'FunctionArgsInjector.extract() process function name as expected'); -t.assertEquals(extract.body, 'return a + b;', 'FunctionArgsInjector.extract() process function body as expected'); -t.assertEquals(extract.args, ['a', 'b'], 'FunctionArgsInjector.extract() process function args as expected'); - -function Plop(foo, bar) { - return 'foo: ' + foo +', bar: ' + bar; -} -function Plip() { return 'plop'; } -function foo_bar(boz) {} -var gni = function ($bubu_bibi, __popo__) {}; -var gno = function ( arg1, /*plop*/ arg2 ) { }; -function issue129(term) { - // see issue #129 - return term; - // see issue #129 -} -t.assertEquals(injector.extract(Plop), { - name: 'Plop', - args: ['foo', 'bar'], - body: "return 'foo: ' + foo +', bar: ' + bar;" -}, 'FunctionArgsInjector.extract() handles named functions with arguments and body'); -t.assertEquals(injector.extract(Plip), { - name: 'Plip', - args: [], - body: "return 'plop';" -}, 'FunctionArgsInjector.extract() handles functions with no arguments'); -t.assertEquals(injector.extract(foo_bar), { - name: 'foo_bar', - args: ['boz'], - body: "" -}, 'FunctionArgsInjector.extract() handles functions with no body'); -t.assertEquals(injector.extract(gni), { - name: null, - args: ['$bubu_bibi', '__popo__'], - body: "" -}, 'FunctionArgsInjector.extract() handles anonymous functions with complex args passed'); -t.assertEquals(injector.extract(gno), { - name: null, - args: ['arg1', 'arg2'], - body: "" -}, 'FunctionArgsInjector.extract() handles can filter comments in function args'); - -t.comment('FunctionArgsInjector.process()'); -var processed; -eval('processed = ' + injector.process({ a: 1, b: 2 })); - -t.assertType(processed, "function", 'FunctionArgsInjector.process() processed a function'); -t.assertEquals(processed(), 3, 'FunctionArgsInjector.process() processed the function correctly'); - -// Issue #129 -var fnIssue129 = createInjector(issue129).process({term: 'fixed'}); -t.assertEquals(fnIssue129('fixed'), 'fixed', 'FunctionArgsInjector.process() has issue #129 fixed'); - -t.done(); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/popup.js b/zephyr/tests/frontend/casperjs/tests/suites/popup.js new file mode 100644 index 0000000000..afc11dd375 --- /dev/null +++ b/zephyr/tests/frontend/casperjs/tests/suites/popup.js @@ -0,0 +1,33 @@ +/*jshint strict:false*/ +/*global CasperError casper console phantom require*/ +var pagestack = require('pagestack'); +var utils = require('utils'); +var webpage = require('webpage'); +var t = casper.test; +var stack = pagestack.create(); + + +var page1 = webpage.create(); +page1.url = 'page1.html'; +stack.push(page1); +t.assertEquals(stack.length, 1); +t.assert(utils.isWebPage(stack[0])); +t.assertEquals(stack[0], page1); +t.assertEquals(stack.list().length, 1); +t.assertEquals(stack.list()[0], page1.url); + +var page2 = webpage.create(); +page2.url = 'page2.html'; +stack.push(page2); +t.assertEquals(stack.length, 2); +t.assert(utils.isWebPage(stack[1])); +t.assertEquals(stack[1], page2); +t.assertEquals(stack.list().length, 2); +t.assertEquals(stack.list()[1], page2.url); + +t.assertEquals(stack.clean(page1), 1); +t.assertEquals(stack[0], page2); +t.assertEquals(stack.list().length, 1); +t.assertEquals(stack.list()[0], page2.url); + +casper.test.done(); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/require.js b/zephyr/tests/frontend/casperjs/tests/suites/require.js index bc0db97077..2723bd68d8 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/require.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/require.js @@ -2,7 +2,7 @@ /*jshint strict:false*/ var fs = require('fs'); var modroot = fs.pathJoin(phantom.casperPath, 'tests', 'sample_modules'); -var jsmod, csmod; +var jsmod, csmod, config; casper.test.comment('Javascript module loading') try { @@ -20,4 +20,12 @@ try { casper.test.fail('require() patched version can load a coffeescript module'); } -casper.test.done(); +casper.test.comment('JSON module loading') +try { + config = require(fs.pathJoin(modroot, 'config.json')); + casper.test.assertTrue(config.ok, 'require() patched version can load a json module'); +} catch (e) { + casper.test.fail('require() patched version can load a json module'); +} + +casper.test.done(3); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/tester.js b/zephyr/tests/frontend/casperjs/tests/suites/tester.js index 2c7881c953..df79c9d4ee 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/tester.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/tester.js @@ -25,23 +25,39 @@ casper.thenOpen('tests/site/index.html', function() { t.comment('Tester.assertTextExists()'); t.assertTextExists('form', 'Tester.assertTextExists() checks that page body contains text'); + t.comment('Tester.assertTextExist()'); + t.assertTextExist('form', 'Tester.assertTextExist() checks that page body contains text [alias]'); + + t.comment('Tester.assertTextDoesntExist()'); + t.assertTextDoesntExist('blah', "Tester.assertTextDoesntExist() checks that page body doesn't contain provided text"); + t.comment('Tester.assertSelectorHasText()'); t.assertSelectorHasText('h1', 'Title', 'Tester.assertSelectorHasText() works as expected'); t.comment('Tester.assertSelectorDoesntHaveText()'); t.assertSelectorDoesntHaveText('h1', 'Subtitle', 'Tester.assertSelectorDoesntHaveText() works as expected'); -}); -casper.then(function() { t.comment('Tester.assert()'); t.assert(true, 'Tester.assert() works as expected'); + t.comment('Tester.assertTrue()'); + t.assertTrue(true, 'Tester.assertTrue() works as expected [alias]'); + + t.comment('Tester.assertTruthy()'); + t.assertTruthy('1', 'Tester.assertTruthy() works as expected'); + + t.comment('Tester.assertFalsy()'); + t.assertFalsy('', 'Tester.assertFalsy() works as expected'); + t.comment('Tester.assertNot()'); t.assertNot(false, 'Tester.assertNot() works as expected'); t.comment('Tester.assertEquals()'); t.assertEquals(true, true, 'Tester.assertEquals() works as expected'); + t.comment('Tester.assertEqual()'); + t.assertEqual(true, true, 'Tester.assertEqual() works as expected [alias]'); + t.comment('Tester.assertNotEquals()'); t.assertNotEquals(true, false, 'Tester.assertNotEquals() works as expected'); @@ -50,17 +66,39 @@ casper.then(function() { return true; }, 'Tester.assertEval() works as expected'); + t.comment('Tester.assertEvaluate()'); + t.assertEvaluate(function() { + return true; + }, 'Tester.assertEvaluate() works as expected [alias]'); + t.comment('Tester.assertEvalEquals()'); t.assertEvalEquals(function() { return 42; }, 42, 'Tester.assertEvalEquals() works as expected'); + t.comment('Tester.assertEvalEqual()'); + t.assertEvalEqual(function() { + return 42; + }, 42, 'Tester.assertEvalEqual() works as expected [alias]'); + t.comment('Tester.assertExists()'); t.assertExists('body', 'Tester.assertExists() works as expected'); + t.comment('Tester.assertExist()'); + t.assertExist('body', 'Tester.assertExist() works as expected [alias]'); + + t.comment('Tester.assertSelectorExists()'); + t.assertSelectorExists('body', 'Tester.assertSelectorExists() works as expected [alias]'); + + t.comment('Tester.assertSelectorExists()'); + t.assertSelectorExist('body', 'Tester.assertSelectorExist() works as expected [alias]'); + t.comment('Tester.assertDoesntExist()'); t.assertDoesntExist('foobar', 'Tester.assertDoesntExist() works as expected'); + t.comment('Tester.assertNotExist()'); + t.assertDoesntExist('foobar', 'Tester.assertNotExist() works as expected [alias]'); + t.comment('Tester.assertHttpStatus()'); // using file:// protocol, HTTP status is always null t.assertHttpStatus(200, 'Tester.assertHttpStatus() works as expected'); @@ -68,31 +106,56 @@ casper.then(function() { t.comment('Tester.assertMatch()'); t.assertMatch("the lazy dog", /lazy/, 'Tester.assertMatch() works as expected'); + t.comment('Tester.assertMatches()'); + t.assertMatches("the lazy dog", /lazy/, 'Tester.assertMatches() works as expected [alias]'); + t.comment('Tester.assertRaises()'); t.assertRaises(function() { throw new Error('plop'); }, [], 'Tester.assertRaises() works as expected'); + t.comment('Tester.assertRaise()'); + t.assertRaise(function() { + throw new Error('plop'); + }, [], 'Tester.assertRaise() works as expected [alias]'); + + t.comment('Tester.assertThrows()'); + t.assertThrows(function() { + throw new Error('plop'); + }, [], 'Tester.assertThrows() works as expected [alias]'); + t.comment('Tester.assertResourceExists()'); t.assertResourceExists(/index\.html/, 'Tester.assertResourceExists() works as expected'); + t.comment('Tester.assertResourceExist()'); + t.assertResourceExist(/index\.html/, 'Tester.assertResourceExist() works as expected [alias]'); + t.comment('Tester.assertTitle()'); t.assertTitle('CasperJS test index', 'Tester.assertTitle() works as expected'); t.comment('Tester.assertTitleMatch()'); t.assertTitleMatch(/test index/, 'Tester.assertTitleMatch() works as expected'); + t.comment('Tester.assertTitleMatches()'); + t.assertTitleMatches(/test index/, 'Tester.assertTitleMatches() works as expected [alias]'); + t.comment('Tester.assertType()'); t.assertType("plop", "string", "Tester.assertType() works as expected"); t.comment('Tester.assertUrlMatch()'); t.assertUrlMatch(/index\.html$/, "Tester.assertUrlMatch() works as expected"); + t.comment('Tester.assertUrlMatches()'); + t.assertUrlMatches(/index\.html$/, "Tester.assertUrlMatches() works as expected [alias]"); + t.comment('Tester.assertVisible()'); t.assertVisible('img', 'Tester.assertVisible() works as expected'); t.comment('Tester.assertNotVisible()'); t.assertNotVisible('p#hidden', 'Tester.assertNotVisible() works as expected'); + + t.comment('Tester.assertInvisible()'); + t.assertInvisible('p#hidden', 'Tester.assertInvisible() works as expected [alias]'); }); casper.thenOpen('tests/site/form.html', function() { @@ -147,6 +210,17 @@ casper.then(function() { t.assertEquals(t.getPasses().length, passCount + 1, "Tester.getPasses() works as expected"); }); -casper.run(function() { - t.done(); +casper.then(function() { + t.comment('Tester.calculateSuiteDuration()'); + function add(a, b) { + return a + b; + } + var passedTime = t.getPassesTime().reduce(add, 0), + failedTime = t.getFailuresTime().reduce(add, 0), + calculatedSum = t.calculateSuiteDuration(); + t.assertEquals(calculatedSum, passedTime + failedTime, "Tester.calculateSuiteDuration() works as expected") +}); + +casper.run(function() { + t.done(59); }); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/utils.js b/zephyr/tests/frontend/casperjs/tests/suites/utils.js index d6fbdae1fc..4c2b95e067 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/utils.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/utils.js @@ -4,6 +4,26 @@ var utils = require('utils'), t = casper.test, x = require('casper').selectXPath; +t.comment('betterTypeOf()'); +(function() { + var testCases = [ + {subject: 1, expected: 'number'}, + {subject: '1', expected: 'string'}, + {subject: {}, expected: 'object'}, + {subject: [], expected: 'array'}, + {subject: undefined, expected: 'undefined'}, + {subject: null, expected: 'null'}, + {subject: function(){}, expected: 'function'}, + {subject: window, expected: 'domwindow'}, + {subject: new Date(), expected: 'date'}, + {subject: new RegExp(), expected: 'regexp'} + ]; + testCases.forEach(function(testCase) { + t.assertEquals(utils.betterTypeOf(testCase.subject), testCase.expected, + require('utils').format('betterTypeOf() detects expected type "%s"', testCase.subject)); + }); +})(); + t.comment('cleanUrl()'); (function() { var testCases = { @@ -23,6 +43,14 @@ t.comment('cleanUrl()'); } })(); +t.comment('clone()'); +(function() { + var a = {a: 1, b: 2, c: [1, 2]}; + t.assertEquals(utils.clone(a), a); + var b = [1, 2, 3, a]; + t.assertEquals(utils.clone(b), b); +})(); + t.comment('equals()'); (function() { t.assert(utils.equals(null, null), 'equals() null equality'); @@ -225,6 +253,9 @@ t.comment('mergeObjects()'); { obj1: {}, obj2: {a: 1}, merged: {a: 1} }, + { + obj1: {}, obj2: {a: {b: 2}}, merged: {a: {b: 2}} + }, { obj1: {a: 1}, obj2: {}, merged: {a: 1} }, @@ -245,6 +276,18 @@ t.comment('mergeObjects()'); testCases.forEach(function(testCase) { t.assertEquals(utils.mergeObjects(testCase.obj1, testCase.obj2), testCase.merged, 'mergeObjects() can merge objects'); }); + var obj = {x: 1}; + var merged1 = utils.mergeObjects({}, {a: obj}); + var merged2 = utils.mergeObjects({a: {}}, {a: obj}); + merged1.a.x = 2; + merged2.a.x = 2; + t.assertEquals(obj.x, 1, 'mergeObjects() creates deep clones'); +})(); + +t.comment('objectValues()'); +(function() { + t.assertEquals(utils.objectValues({}), [], 'objectValues() can extract object values'); + t.assertEquals(utils.objectValues({a: 1, b: 2}), [1, 2], 'objectValues() can extract object values'); })(); t.comment('unique()'); @@ -272,4 +315,63 @@ t.comment('unique()'); }); })(); -t.done(); +t.comment('cmpVersion() tests'); +(function() { + t.assertEquals(utils.cmpVersion('1.0.0', '2.0.0'), -1, + 'cmpVersion() can compare version strings'); + t.assertEquals(utils.cmpVersion('1.0.0-DEV', '2.0.0-BOOM'), -1, + 'cmpVersion() can compare version strings'); + t.assertEquals(utils.cmpVersion('1.0.0', '1.1.0'), -1, + 'cmpVersion() can compare version strings'); + t.assertEquals(utils.cmpVersion('1.1.0', '1.0.0'), 1, + 'cmpVersion() can compare version strings'); + t.assertEquals(utils.cmpVersion('0.0.3', '0.0.4'), -1, + 'cmpVersion() can compare version strings'); + t.assertEquals(utils.cmpVersion('0.0.3', '1.0.3'), -1, + 'cmpVersion() can compare version strings'); + t.assertEquals(utils.cmpVersion('0.1', '1.0.3.8'), -1, + 'cmpVersion() can compare version strings'); + t.assertEquals(utils.cmpVersion({major: 1, minor: 2, patch: 3}, + {major: 1, minor: 2, patch: 4}), -1, + 'cmpVersion() can compare version objects'); + t.assertEquals(utils.cmpVersion({major: 2, minor: 0, patch: 3}, + {major: 1, minor: 0, patch: 4}), 1, + 'cmpVersion() can compare version objects'); + t.assertEquals(utils.cmpVersion({major: 0, minor: 0, patch: 3}, + {major: 1, minor: 0, patch: 3}), -1, + 'cmpVersion() can compare version objects'); + t.done(); +})(); + +t.comment('gteVersion() tests'); +(function() { + t.assert(utils.gteVersion('1.1.0', '1.0.0'), + 'gteVersion() checks for a greater or equal version'); + t.assertNot(utils.gteVersion('1.0.0', '1.1.0'), + 'gteVersion() checks for a greater or equal version'); + t.assert(utils.gteVersion({major: 1, minor: 1, patch: 0}, + {major: 1, minor: 0, patch: 0}), + 'gteVersion() checks for a greater or equal version'); + t.assertNot(utils.gteVersion({major: 1, minor: 0, patch: 0}, + {major: 1, minor: 1, patch: 0}), + 'gteVersion() checks for a greater or equal version'); + t.done(); +})(); + +t.comment('ltVersion() tests'); +(function() { + t.assert(utils.ltVersion('1.0.0', '1.1.0'), + 'ltVersion() checks for a lesser version'); + t.assertNot(utils.ltVersion('1.1.0', '1.0.0'), + 'ltVersion() checks for a lesser version'); + t.assert(utils.ltVersion({major: 1, minor: 0, patch: 0}, + {major: 1, minor: 1, patch: 0}), + 'ltVersion() checks for a lesser version'); + t.assertNot(utils.ltVersion({major: 1, minor: 1, patch: 0}, + {major: 1, minor: 0, patch: 0}), + 'ltVersion() checks for a lesser version'); + t.done(); +})(); + + +t.done(132); diff --git a/zephyr/tests/frontend/casperjs/tests/suites/xunit.js b/zephyr/tests/frontend/casperjs/tests/suites/xunit.js index fd8240828a..294adb7b46 100644 --- a/zephyr/tests/frontend/casperjs/tests/suites/xunit.js +++ b/zephyr/tests/frontend/casperjs/tests/suites/xunit.js @@ -4,15 +4,44 @@ casper.test.comment('phantom.Casper.XUnitExporter'); var xunit = require('xunit').create(); xunit.addSuccess('foo', 'bar'); -casper.test.assertMatch(xunit.getXML(), /wrong/, 'XUnitExporter.addFailure() adds a failed testcase'); +casper.test.assertMatch(xunit.getXML(), + /wrong/, + 'XUnitExporter.addFailure() adds a failed testcase'); // named classname xunit = require('xunit').create(); xunit.addSuccess(require('fs').workingDirectory + '/plop.js', 'It worked'); -casper.test.assertMatch(xunit.getXML(), /wrong/, + 'XUnitExporter.addFailure() adds a failed testcase with duration'); + +// named with time +xunit = require('xunit').create(); +casper.test.assertMatch(xunit.getXML(), + //, + 'XUnitExporter.create() created without time'); +xunit.setSuiteDuration(1024); +casper.test.assertMatch(xunit.getXML(), + //, + 'XUnitExporter.setSuiteDuration() sets time in seconds'); + +casper.test.done(8);