├── .gitignore ├── .npmignore ├── .travis.yml ├── MIT-LICENSE ├── README.md ├── bin └── mocha-phantomjs ├── package.json └── test ├── console-log.html ├── cookie.html ├── env.html ├── error.html ├── failing-async.html ├── failing.html ├── file.html ├── hooks ├── after-end.js └── before-start.js ├── lib ├── console-log.js ├── failing-async.js ├── failing.js ├── many.js ├── mixed.js ├── passing.js ├── screenshot.js └── slow.js ├── many.html ├── mixed.html ├── mocha-phantomjs.coffee ├── mocha-runner.html ├── mocha.opts ├── no-tests.html ├── passing.html ├── reporters ├── 3rd-party.js └── node-only.js ├── resource-errors.html ├── screenshot.html ├── slow.html ├── timeout.html ├── user-agent.html └── viewport.html /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | *.swp 3 | /tmp/* 4 | /node_modules 5 | test 6 | public 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 0.12 -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 20012 Ken Collins 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PhantomJS Runners for Mocha 2 | 3 | ## Deprecated 4 | 5 | Ariya Hidayat [archived phantomjs](https://github.com/ariya/phantomjs/issues/15344) on March 3rd, 2018. (See [this tweet](https://twitter.com/AriyaHidayat/status/970173001701367808) for more info). phantomjs served us all as great headless browser for years, but now with [Electron](https://electronjs.org/) and headless modes for both [Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/Firefox/Headless_mode), we have much better options. [mocha-chrome](https://github.com/shellscape/mocha-chrome) is a project inspired by `mocha-phantomjs`, so migration should be easy, and you will be running your tests on the same browser that 50% of your users actually use! I highly recommend it. 6 | 7 | I will accept pull requests still, but I won't be answering issues or doing feature work myself. 8 | 9 | ## Summary 10 | 11 | [Mocha](http://mochajs.org/) is a feature-rich JavaScript test framework running on node and the browser. Along with the [Chai](http://chaijs.com) assertion library they make an impressive combo. [PhantomJS](http://phantomjs.org) is a headless WebKit with a JavaScript API. 12 | 13 | Since 4.0, the phantomjs code now is in [mocha-phantomjs-core](https://github.com/nathanboktae/mocha-phantomjs-core). If you need full control over which phantomjs version to use and where to get it, including *PhantomJS 2.0* and SlimerJS, or want to use it more programatically like a build system plugin, please use that package directly. This project is a node.js CLI around it. 14 | 15 | [![Build Status](https://travis-ci.org/nathanboktae/mocha-phantomjs.svg?branch=master)](https://travis-ci.org/nathanboktae/mocha-phantomjs) 16 | 17 | ## Key Features 18 | 19 | ### Standard Out 20 | 21 | Finally, `process.stdout.write`, done right. Mocha is primarily written for node, hence it relies on writing to standard out without trailing newline characters. This behavior is critical for reporters like the dot reporter. We make up for PhantomJS's lack of stream support by both customizing `console.log` and creating a `process.stdout.write` function to the current PhantomJS process. This technique combined with a handful of fancy [ANSI cursor movement codes](http://web.mit.edu/gnu/doc/html/screen_10.html) allows PhantomJS to support Mocha's diverse reporter options. 22 | 23 | ### Exit Codes 24 | 25 | Proper exit status codes from PhantomJS using Mocha's failures count. So in standard UNIX fashion, a `0` code means success. This means you can use mocha-phantomjs on your CI server of choice. 26 | 27 | ### Mixed Mode Runs 28 | 29 | You can use your existing Mocha HTML file reporters side by side with mocha-phantomjs. This gives you the option to run your tests both in a browser or with PhantomJS, with no changes needed to your existing test setup. 30 | 31 | # Installation 32 | 33 | We distribute [mocha-phantomjs as an npm package](https://npmjs.org/package/mocha-phantomjs) that is easy to install. Once done, you will have a `mocha-phantomjs` binary. See the next usage section for docs or use the `-h` flag. 34 | 35 | # Usage 36 | 37 | ``` 38 | Usage: mocha-phantomjs [options] page 39 | 40 | Options: 41 | 42 | -h, --help output usage information 43 | -V, --version output the version number 44 | -R, --reporter specify the reporter to use 45 | -f, --file specify the file to dump reporter output 46 | -t, --timeout specify the test startup timeout to use 47 | -g, --grep only run tests matching 48 | -i, --invert invert --grep matches 49 | -b, --bail exit on the first test failure 50 | -A, --agent specify the user agent to use 51 | -c, --cookies phantomjs cookie object http://git.io/RmPxgA 52 | -h, --header = specify custom header 53 | -k, --hooks path to hooks module 54 | -s, --setting = specify specific phantom settings 55 | -v, --view x specify phantom viewport size 56 | -C, --no-color disable color escape codes 57 | -p, --path path to PhantomJS binary 58 | --ignore-resource-errors ignore resource errors 59 | 60 | Any other options are passed to phantomjs (see `phantomjs --help`) 61 | 62 | Examples: 63 | 64 | $ mocha-phantomjs -R dot /test/file.html 65 | $ mocha-phantomjs https://testserver.com/file.html --ignore-ssl-errors=true 66 | $ mocha-phantomjs -p ~/bin/phantomjs /test/file.html 67 | ``` 68 | 69 | Now as an node package, using `mocha-phantomjs` has never been easier. The page argument can be either a local or fully qualified path or a http or file URL. `--reporter` may be a built-in reporter or a path to your own reporter (see below). See [phantomjs WebPage settings](https://github.com/ariya/phantomjs/wiki/API-Reference-WebPage#wiki-webpage-settings) for options that may be supplied to the `--setting` argument. 70 | 71 | Since 4.0, you need no modifications to your test harness markup file to run. Here is an example `test.html`: 72 | 73 | ```html 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
82 | 83 | 84 | 88 | 89 | 90 | 93 | 94 | 95 | ``` 96 | 97 | # Screenshots 98 | 99 | Mocha-phantomjs supports creating screenshots from your test code. For example, you could write a function like below into your test code. 100 | 101 | ```javascript 102 | function takeScreenshot() { 103 | if (window.callPhantom) { 104 | var date = new Date() 105 | var filename = "screenshots/" + date.getTime() 106 | console.log("Taking screenshot " + filename) 107 | callPhantom({'screenshot': filename}) 108 | } 109 | } 110 | ``` 111 | 112 | If you want to generate a screenshot for each test failure you could add the following into your test code. 113 | 114 | ```javascript 115 | afterEach(function () { 116 | if (this.currentTest.state == 'failed') { 117 | takeScreenshot() 118 | } 119 | }) 120 | ``` 121 | 122 | # Supported Reporters 123 | 124 | `mocha-phantomjs` works by piping `Mocha.process.stdout` to PhantomJS's stdout. Any reporter that can work in the browser works with mocha-phantomjs. 125 | 126 | [Bundled](http://mochajs.org/#reporters) and tested reporters include: 127 | 128 | ```` 129 | spec (default) 130 | dot 131 | tap 132 | min 133 | nyan 134 | list 135 | doc 136 | teamcity 137 | json 138 | json-cov 139 | xunit 140 | progress 141 | landing 142 | markdown 143 | ```` 144 | 145 | When using the `dot` reporter, the PhantomJS process has no way of knowing anything about your console window's width. So we default the width to 75 columns. However, if you set the `COLUMNS` environment variable, it will pick that up and adjust to your current terminal width. For example, using the `$COLUMNS` variable like so. 146 | 147 | ``` 148 | env COLUMNS=$COLUMNS phantomjs mocha-phantomjs.coffee URL dot 149 | ``` 150 | 151 | ### Third Party Reporters 152 | 153 | Mocha has support for custom [3rd party reporters](https://github.com/mochajs/mocha/wiki/Third-party-reporters), and mocha-phantomjs does support 3rd party reporters, but keep in mind - *the reporter does not run in Node.js, but in the browser, and node modules can't be required.* You need to only use basic, vanilla JavaScript when using third party reporters. However, some things are available: 154 | 155 | - `require`: You can only require other reporters, like `require('./base')` to build off of the BaseReporter 156 | - `exports`, `module`: Export your reporter class as normal 157 | - `process`: use `process.stdout.write` preferrably to support the `--file` option over `console.log` (see #114) 158 | 159 | Also, no compilers are supported currently, so please provide JavaScript only for your reporters. 160 | 161 | # Testing 162 | 163 | Simple! Just clone the repo, then run `npm install` and the various node development dependencies will install to the `node_modules` directory of the project. If you have not done so, it is typically a good idea to add `/node_modules/.bin` to your `$PATH` so these modules bins are used. Now run `npm test` to start off the test suite. 164 | 165 | We also use Travis CI to run our tests too. The current build status: 166 | 167 | [![Build Status](https://secure.travis-ci.org/nathanboktae/mocha-phantomjs.png)](http://travis-ci.org/nathanboktae/mocha-phantomjs) 168 | 169 | 170 | # License 171 | 172 | Released under the MIT license. Copyright (c) 2015 Ken Collins, Nathan Black, and many generous GitHub Contributors. 173 | 174 | -------------------------------------------------------------------------------- /bin/mocha-phantomjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var program = require('commander'), 4 | spawn = require('child_process').spawn, 5 | fs = require('fs'), 6 | path = require('path'), 7 | exists = fs.existsSync || path.existsSync, 8 | fileExists = function (path) { 9 | return fs.existsSync(path) && !fs.statSync(path).isDirectory(); 10 | }, 11 | cwd = process.cwd(), 12 | cookies = [], 13 | headers = {}, 14 | settings = {}; 15 | 16 | function keyValue(val, store) { 17 | val = val.split('='); 18 | key = val.shift(); 19 | val = val.join('='); 20 | if (val === 'true') { 21 | val = true; 22 | } else if (val === 'false') { 23 | val = false; 24 | } 25 | store[key] = val; 26 | return val; 27 | } 28 | 29 | function cookiesParser(val) { 30 | val = JSON.parse(val); 31 | cookies.push(val); 32 | return val; 33 | } 34 | function header(val) { 35 | return keyValue(val, headers); 36 | } 37 | function setting(val) { 38 | return keyValue(val, settings); 39 | } 40 | function viewport(val) { 41 | val = val.split('x'); 42 | return { 43 | width: parseFloat(val[0]), 44 | height: parseFloat(val[1]) 45 | }; 46 | } 47 | function resolveHooks(val) { 48 | var absPath = path.resolve(process.cwd(), val); 49 | 50 | if (!exists(absPath)) { 51 | for (var i = 0; i < module.paths.length - 1; i++) { 52 | absPath = path.join(module.paths[i], val); 53 | if (exists(absPath)) return absPath; 54 | }; 55 | } 56 | 57 | return absPath; 58 | } 59 | 60 | program 61 | .allowUnknownOption() 62 | .version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version) 63 | .usage('[options] page') 64 | .option('-R, --reporter ', 'specify the reporter to use', 'spec') 65 | .option('-f, --file ', 'specify the file to dump reporter output') 66 | .option('-t, --timeout ', 'specify the test startup timeout to use', parseInt) 67 | .option('-g, --grep ', 'only run tests matching ') 68 | .option('-i, --invert', 'invert --grep matches') 69 | .option('-b, --bail', 'exit on the first test failure') 70 | .option('-A, --agent ', 'specify the user agent to use') 71 | .option('-c, --cookies ', 'phantomjs cookie object http://git.io/RmPxgA', cookiesParser) // http://git.io/RmPxgA 72 | .option('-h, --header =', 'specify custom header', header) 73 | .option('-k, --hooks ', 'path to hooks module', resolveHooks) 74 | .option('-s, --setting =', 'specify specific phantom settings', setting) 75 | .option('-v, --view x', 'specify phantom viewport size', viewport) 76 | .option('-C, --no-color', 'disable color escape codes') 77 | .option('-p, --path ', 'path to PhantomJS binary') 78 | .option('--ignore-resource-errors', 'ignore resource errors'); 79 | 80 | program.on('--help', function(){ 81 | console.log(' Any other options are passed to phantomjs (see `phantomjs --help`)'); 82 | console.log(''); 83 | console.log(' Examples:'); 84 | console.log(''); 85 | console.log(' $ mocha-phantomjs -R dot /test/file.html'); 86 | console.log(' $ mocha-phantomjs http://testserver.com/file.html'); 87 | console.log(' $ mocha-phantomjs -p ~/bin/phantomjs /test/file.html'); 88 | console.log(''); 89 | }); 90 | 91 | program.parse(process.argv); 92 | 93 | if (!program.args.length) { program.outputHelp(); process.exitCode = 1; return; }; 94 | if (program.agent) { settings.userAgent = program.agent; } 95 | 96 | var script = require.resolve('mocha-phantomjs-core/mocha-phantomjs-core.js'); 97 | var reporter = program.reporter; 98 | var page = function(){ 99 | var arg = program.args[0]; 100 | if (arg.match(/file:\/\//)) { return arg; }; 101 | if (arg.match(/http:\/\//)) { return arg; }; 102 | if (arg.match(/https:\/\//)) { return arg; }; 103 | if (exists(arg)) { return arg; }; 104 | if (exists(cwd+'/'+arg)) { return fs.realpathSync(cwd+'/'+arg); }; 105 | return arg; 106 | }(); 107 | var config = JSON.stringify({ 108 | hooks: program.hooks, 109 | timeout: program.timeout || 6000, 110 | cookies: cookies, 111 | headers: headers, 112 | settings: settings, 113 | viewportSize: program.view, 114 | useColors: program.color, 115 | bail: program.bail, 116 | file: program.file, 117 | grep: program.grep, 118 | invert: program.invert, 119 | ignoreResourceErrors: program.ignoreResourceErrors 120 | }); 121 | 122 | if (reporter) { 123 | if (fileExists(reporter)) { 124 | reporter = path.resolve(process.cwd(), reporter); 125 | } else if (fileExists(reporter + '.js')) { 126 | reporter = path.resolve(process.cwd(), reporter + '.js'); 127 | } 128 | } 129 | 130 | var phantomPath 131 | if (program.path) { 132 | phantomPath = path.resolve(program.path); 133 | if (!exists(phantomPath)) { 134 | console.error("PhantomJS does not exist at '" + program.path + "'"); 135 | process.exitCode = -2; 136 | return; 137 | } 138 | } else { 139 | phantomPath = require('phantomjs').path || '/usr/local/bin/phantomjs'; 140 | } 141 | 142 | function launchFailure(e) { 143 | console.error('An error occurred trying to launch phantomjs at "' + phantomPath + '": ' + e); 144 | if (!program.path) { 145 | console.error('You can specify an explicit path to phantomjs via the `-p` option.'); 146 | } 147 | process.exitCode = -2; 148 | } 149 | 150 | var unknown = program.parseOptions(process.argv).unknown.filter(function(u) { 151 | return u !== program.args[0] 152 | }) 153 | 154 | try { 155 | var phantomjs = spawn(phantomPath, unknown.concat([script, page, reporter, config])) 156 | phantomjs.stdout.pipe(process.stdout); 157 | phantomjs.stderr.pipe(process.stderr); 158 | phantomjs.on('exit', function(code){ 159 | if (code == null) { 160 | if (phantomjs.signalCode !== null) { 161 | console.log("phantomjs terminated with signal " + phantomjs.signalCode); 162 | } 163 | code = 1; 164 | } else if (code === 127) { 165 | console.log("Perhaps phantomjs is not installed?"); 166 | } 167 | 168 | process.exitCode = code; 169 | 170 | // Adds a final line break before exit for better cli experience. 171 | process.once('beforeExit', function () { 172 | process.stdout.write('\n'); 173 | }); 174 | }); 175 | phantomjs.on('error', launchFailure); 176 | } 177 | catch (e) { 178 | launchFailure(e); 179 | } 180 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha-phantomjs", 3 | "description": "Run mocha browser tests in phantomjs via the command line", 4 | "keywords": [ 5 | "phantomjs", 6 | "mocha", 7 | "test", 8 | "runner", 9 | "command line", 10 | "browser" 11 | ], 12 | "version": "4.1.0", 13 | "engines": { 14 | "node" : ">= 0.11.8" 15 | }, 16 | "author": "Nathan Black (http://nathanblack.org)", 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "http://github.com/nathanboktae/mocha-phantomjs/blob/master/MIT-LICENSE" 21 | } 22 | ], 23 | "contributors": [ 24 | "Jonathan Chapman (https://github.com/chafnan)", 25 | "Ken Collins (http://metaskills.net/)" 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "http://github.com/nathanboktae/mocha-phantomjs.git" 30 | }, 31 | "bugs": { 32 | "url": "http://github.com/nathanboktae/mocha-phantomjs/issues" 33 | }, 34 | "main": "./bin/mocha-phantomjs", 35 | "bin": { 36 | "mocha-phantomjs": "./bin/mocha-phantomjs" 37 | }, 38 | "scripts": { 39 | "test": "mocha --harmony --compilers coffee:coffee-script/register test/mocha-phantomjs.coffee -t 5000" 40 | }, 41 | "dependencies": { 42 | "phantomjs": "1.9.7-15", 43 | "mocha-phantomjs-core": "^1.1.0", 44 | "commander": "^2.8.1" 45 | }, 46 | "devDependencies": { 47 | "bluebird": "^2.9.25", 48 | "chai": "^2.3.0", 49 | "co-mocha": "^1.1.0", 50 | "coffee-script": "^1.9.2" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/console-log.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Passing 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/cookie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Cookie 5 | 6 | 7 | 8 | 9 |
10 | 11 | 15 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/env.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests env 5 | 6 | 7 | 8 | 9 |
10 | 11 | 15 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Cookie 5 | 6 | 7 | 8 | 9 |
10 | 11 | 15 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/failing-async.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Async Tests Failing 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/failing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Failing 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/file.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Reporter Output to File 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/hooks/after-end.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | afterEnd: function(reporter) { 3 | console.log('After end called!'); 4 | } 5 | }; -------------------------------------------------------------------------------- /test/hooks/before-start.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | beforeStart: function(reporter) { 3 | console.log('Before start called!'); 4 | } 5 | }; -------------------------------------------------------------------------------- /test/lib/console-log.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Tests Passing', function() { 4 | it('passes 1', function() { 5 | var o = {}; 6 | o['self'] = o; 7 | 8 | console.log(o); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/lib/failing-async.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Async Tests Failing', function() { 4 | it('passes 1', function() { 5 | expect(1).to.be.ok; 6 | }); 7 | it('passes 2', function() { 8 | expect(2).to.be.ok; 9 | }); 10 | it('passes 3', function() { 11 | expect(3).to.be.ok; 12 | }); 13 | 14 | it('fails 1', function(done) { 15 | setTimeout(function() { 16 | expect(false).to.be.true; 17 | done(); 18 | }, 0); 19 | }); 20 | 21 | it('fails 2', function(done) { 22 | setTimeout(function() { 23 | expect(false).to.be.true; 24 | done(); 25 | }, 0); 26 | }); 27 | 28 | it('fails 3', function(done) { 29 | setTimeout(function() { 30 | expect('false').to.equal('true'); 31 | done(); 32 | }, 0); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/lib/failing.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Tests Failing', function() { 4 | it('passes 1', function() { 5 | expect(1).to.be.ok; 6 | }); 7 | 8 | it('passes 2', function() { 9 | expect(2).to.be.ok; 10 | }); 11 | 12 | it('passes 3', function() { 13 | expect(3).to.be.ok; 14 | }); 15 | 16 | it('fails 1', function() { 17 | expect(false).to.be["true"]; 18 | }); 19 | 20 | it('fails 2', function() { 21 | expect(false).to.be["true"]; 22 | }); 23 | 24 | it('fails 3', function() { 25 | expect('false').to.equal('true'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/lib/many.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Many Tests', function() { 4 | for (var n = 1; n <= 500; n++) { 5 | if (n % 10 === 0) { 6 | xit('skips ' + n, function() { 7 | expect(false).to.be.true 8 | }) 9 | } else if (n % 100 === 2) { 10 | it('fails ' + n, function() { 11 | expect(false).to.be.true 12 | }) 13 | } else { 14 | it('passses ' + n, function() { 15 | expect(n).to.equal(n) 16 | }) 17 | } 18 | } 19 | }); -------------------------------------------------------------------------------- /test/lib/mixed.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Tests Mixed', function() { 4 | it('passes 1', function() { 5 | expect(1).to.be.ok; 6 | }); 7 | it('passes 2', function() { 8 | expect(2).to.be.ok; 9 | }); 10 | it('passes 3', function() { 11 | expect(3).to.be.ok; 12 | }); 13 | 14 | it('skips 1'); 15 | it('skips 2'); 16 | it('skips 3'); 17 | 18 | it('fails 1', function() { 19 | expect(false).to.be.true; 20 | }); 21 | it('fails 2', function() { 22 | expect(false).to.be.true; 23 | }); 24 | it('fails 3', function() { 25 | expect(false).to.be.true; 26 | }); 27 | it('passes 4', function() { 28 | expect(1).to.be.ok; 29 | }); 30 | it('passes 5', function() { 31 | expect(2).to.be.ok; 32 | }); 33 | it('passes 6', function() { 34 | expect(3).to.be.ok; 35 | }); 36 | it('fails 4', function() { 37 | expect(false).to.be.true; 38 | }); 39 | it('fails 5', function() { 40 | expect(false).to.be.true; 41 | }); 42 | it('fails 6', function() { 43 | expect(false).to.be.true; 44 | }); 45 | 46 | xit('skips 4', function() { 47 | expect(false).to.be.true; 48 | }); 49 | xit('skips 5', function() { 50 | expect(false).to.be.true; 51 | }); 52 | xit('skips 6', function() { 53 | expect(false).to.be.true; 54 | }); 55 | }); -------------------------------------------------------------------------------- /test/lib/passing.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Tests Passing', function() { 4 | it('passes 1', function() { 5 | expect(1).to.be.ok; 6 | }); 7 | it('passes 2', function() { 8 | expect(2).to.be.ok; 9 | }); 10 | it('passes 3', function() { 11 | expect(3).to.be.ok; 12 | }); 13 | 14 | xit('skips 1', function() { 15 | expect(false).to.be.true; 16 | }); 17 | xit('skips 2', function() { 18 | expect(false).to.be.true; 19 | }); 20 | xit('skips 3', function() { 21 | expect(false).to.be.true; 22 | }); 23 | }); -------------------------------------------------------------------------------- /test/lib/screenshot.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Screenshot', function() { 4 | it('takes screenshot', function() { 5 | if (window.callPhantom) { 6 | var date = new Date() 7 | var filename = "screenshot" 8 | console.log("Taking screenshot " + filename) 9 | callPhantom({'screenshot': filename}) 10 | } 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/lib/slow.js: -------------------------------------------------------------------------------- 1 | expect = (chai && chai.expect) || require('chai').expect; 2 | 3 | describe('Slow tests', function() { 4 | it('display test start events', function(done) { 5 | setTimeout((function() { 6 | done(); 7 | }), 1862); 8 | expect(true).to.be.true; 9 | }); 10 | 11 | it('and are correctly overwritten', function(done) { 12 | setTimeout((function() { 13 | expect(true).to.be.true; 14 | done(); 15 | }), 1538); 16 | }); 17 | 18 | it('with pass or fail output', function(done) { 19 | setTimeout((function() { 20 | done(); 21 | }), 1239); 22 | expect(true).to.be.true; 23 | }); 24 | }); -------------------------------------------------------------------------------- /test/many.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Many Tests 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/mixed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Mixed 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/mocha-phantomjs.coffee: -------------------------------------------------------------------------------- 1 | describe 'mocha-phantomjs', -> 2 | 3 | chai = require 'chai' 4 | expect = chai.expect 5 | should = chai.should() 6 | spawn = require('child_process').spawn 7 | url = require 'url' 8 | fs = require 'fs' 9 | 10 | fileURL = (file) -> 11 | fullPath = fs.realpathSync "#{process.cwd()}/test/#{file}.html" 12 | fullPath = fullPath.replace /\\/g, '\/' 13 | urlString = fullPath 14 | urlString = url.format { protocol: 'file', hostname: '', pathname: fullPath } if process.platform isnt 'win32' 15 | 16 | run = (args) -> 17 | new Promise (resolve, reject) -> 18 | stdout = '' 19 | stderr = '' 20 | spawnArgs = ["#{process.cwd()}/bin/mocha-phantomjs"].concat(args) 21 | mochaPhantomJS = spawn 'node', spawnArgs 22 | mochaPhantomJS.stdout.on 'data', (data) -> stdout = stdout.concat data.toString() 23 | mochaPhantomJS.stderr.on 'data', (data) -> stderr = stderr.concat data.toString() 24 | mochaPhantomJS.on 'exit', (code) -> 25 | resolve { code, stdout, stderr } 26 | mochaPhantomJS.on 'error', (err) -> reject err 27 | 28 | it 'returns a failure code and shows usage when no args are given', -> 29 | { code, stdout } = yield run [] 30 | code.should.equal 1 31 | stdout.should.match /Usage: mocha-phantomjs/ 32 | 33 | it 'returns a failure code and notifies of bad url when given one', -> 34 | { code, stderr } = yield run ['foo/bar.html'] 35 | code.should.equal 1 36 | stderr.should.match /failed to load the page/i 37 | stderr.should.match /check the url/i 38 | stderr.should.match /foo\/bar.html/i 39 | 40 | it 'returns a failure code and notifies of no such runner class', -> 41 | { code, stderr } = yield run ['-R', 'nonesuch', fileURL('passing')] 42 | code.should.equal 1 43 | stderr.should.match /Unable to open file 'nonesuch'/ 44 | 45 | it 'returns a failure code when mocha fails to run any tests', -> 46 | { code, stderr } = yield run [fileURL('no-tests')] 47 | code.should.equal 1 48 | stderr.should.match /mocha.run\(\) was called with no tests/ 49 | 50 | it 'returns a failure code when mocha is not started in a timely manner', -> 51 | { code, stderr } = yield run ['-t', 500, fileURL('timeout')] 52 | stderr.should.match /mocha.run\(\) was not called within 500ms of the page loading/ 53 | code.should.not.equal 0 54 | 55 | it 'returns a failure code when there is a page error', -> 56 | { code, stderr } = yield run [fileURL('error')] 57 | code.should.equal 1 58 | stderr.should.match /ReferenceError/ 59 | 60 | it 'does not fail when console.log is used with circular reference object', -> 61 | { code, stdout, stderr } = yield run [fileURL('console-log')] 62 | code.should.equal 0 63 | stderr.should.not.match /cannot serialize cyclic structures\./m 64 | stdout.should.not.match /cannot serialize cyclic structures\./m 65 | stdout.should.contain '[Circular]' 66 | 67 | it 'returns the mocha runner from run() and allows modification of it', -> 68 | { code, stdout } = yield run [fileURL('mocha-runner')] 69 | stdout.should.not.match /Failed via an Event/m 70 | code.should.equal 1 71 | 72 | it 'returns the mocha runner from run() and allows modification of it', -> 73 | { code, stdout } = yield run [fileURL('mocha-runner')] 74 | stdout.should.not.match /Failed via an Event/m 75 | code.should.equal 1 76 | 77 | it 'passes the arguments along to mocha.run', -> 78 | { stdout } = yield run [fileURL('mocha-runner')] 79 | stdout.should.match /Run callback fired/m 80 | 81 | it 'passes all unknown arguments to phantomjs', -> 82 | { stderr } = yield run ['--unknown=true', fileURL('mocha-runner')] 83 | stderr.should.match /Error: Unknown option: unknown/m 84 | 85 | it 'can use a different reporter', -> 86 | { stdout } = yield run ['-R', 'xunit', fileURL('mixed')] 87 | stdout.should.match // 88 | 89 | it 'does not allow phantomjs >= 1.9.8 < 2 due to ariya/phantomjs#12697', -> 90 | { stdout, stderr } = yield run ['-R', 'xunit', fileURL('no-tests')] 91 | stdout.should.not.contain 'Unsafe JavaScript attempt' 92 | stderr.should.not.contain 'Unsafe JavaScript attempt' 93 | 94 | describe 'exit code', -> 95 | it 'returns 0 when all tests pass', -> 96 | { code } = yield run fileURL('passing') 97 | code.should.equal 0 98 | 99 | it 'returns a failing code equal to the number of mocha failures', -> 100 | { code } = yield run fileURL('failing') 101 | code.should.equal 3 102 | 103 | it 'returns a failing code correctly even with async failing tests', -> 104 | { code } = yield run fileURL('failing-async') 105 | code.should.equal 3 106 | 107 | describe 'screenshot', -> 108 | it 'takes a screenshot into given file, suffixed with .png', -> 109 | { code } = yield run fileURL('screenshot') 110 | code.should.equal 0 111 | fileName = 'screenshot.png' 112 | fs.existsSync(fileName).should.be.true 113 | fs.unlinkSync(fileName) 114 | 115 | describe 'third party reporters', -> 116 | it 'loads and wraps node-style reporters to run in the browser', -> 117 | { stdout } = yield run ['-R', 'test/reporters/3rd-party', fileURL('mixed')] 118 | 119 | stdout.should.match /
/ 120 | stdout.should.match /

Tests Mixed<\/h1>/ 121 | 122 | it 'gives a useful error when trying to require a node module', -> 123 | { code, stderr } = yield run ['-R', 'test/reporters/node-only', fileURL('mixed')] 124 | 125 | stderr.should.match /Node modules cannot be required/ 126 | code.should.not.equal 0 127 | 128 | describe 'hooks', -> 129 | it 'should fail gracefully if they do not exist', -> 130 | { code, stderr } = yield run ['-k', 'nonexistant-file.js', fileURL('passing')] 131 | 132 | code.should.not.equal 0 133 | stderr.should.contain('Error loading hooks').and.contain "nonexistant-file.js" 134 | 135 | it 'has a hook for before tests are started', -> 136 | { code, stdout } = yield run ['-k', 'test/hooks/before-start.js', fileURL('passing')] 137 | 138 | stdout.should.contain 'Before start called!' 139 | code.should.equal 0 140 | 141 | it 'has a hook for after the test run finishes', -> 142 | { code, stdout } = yield run ['-k', 'test/hooks/after-end.js', fileURL('passing')] 143 | 144 | stdout.should.contain 'After end called!' 145 | code.should.equal 0 146 | 147 | describe 'parameters', -> 148 | describe 'user-agent', -> 149 | it 'has the default user agent', -> 150 | { stdout } = yield run [fileURL('user-agent')] 151 | stdout.should.match /PhantomJS\// 152 | 153 | it 'has a custom user agent', -> 154 | { stdout } = yield run ['-A', 'mochaUserAgent', fileURL('user-agent')] 155 | stdout.should.match /^mochaUserAgent/ 156 | 157 | it 'has a custom user agent via setting flag and 2 equal signs', -> 158 | { stdout } = yield run ['-s', 'userAgent=mocha=UserAgent', fileURL('user-agent')] 159 | stdout.should.match /^mocha=UserAgent/ 160 | 161 | describe 'cookies', -> 162 | it 'has passed cookies', -> 163 | c1Opt = '{"name":"foo","value":"bar"}' 164 | c2Opt = '{"name":"baz","value":"bat","path":"/"}' 165 | { stdout } = yield run ['-c', c1Opt, '--cookies', c2Opt, fileURL('cookie')] 166 | stdout.should.match /foo=bar; baz=bat/ 167 | 168 | describe 'viewport', -> 169 | it 'has the specified dimensions', -> 170 | { stdout } = yield run ['-v', '123x456', fileURL('viewport')] 171 | stdout.should.match /123x456/ 172 | 173 | describe 'grep', -> 174 | it 'filters tests to match the criteria', -> 175 | { code, stdout } = yield run ['-g', 'pass', fileURL('mixed')] 176 | code.should.equal 0 177 | stdout.should.not.match /fail/ 178 | 179 | it 'can be inverted to filter out tests matching the criteria', -> 180 | { code, stdout } = yield run ['--grep', 'pass', '-i', fileURL('mixed')] 181 | code.should.equal 6 182 | stdout.should.not.match /passes/ 183 | 184 | describe 'no-colors', -> 185 | it 'by default will output in color', -> 186 | { stdout } = yield run ['-R', 'dot', fileURL('mixed')] 187 | 188 | stdout.should.match /\u001b\[90m.\u001b\[0m/ # grey 189 | stdout.should.match /\u001b\[36m.\u001b\[0m/ # cyan 190 | stdout.should.match /\u001b\[31m.\u001b\[0m/ # red 191 | 192 | it 'suppresses color output', -> 193 | { stdout } = yield run ['-C', fileURL('mixed')] 194 | stdout.should.not.match /\u001b\[\d\dm/ 195 | 196 | it 'suppresses color output plural long form', -> 197 | { stdout } = yield run ['--no-colors', fileURL('mixed')] 198 | stdout.should.not.match /\u001b\[\d\dm/ 199 | 200 | describe 'bail', -> 201 | it 'should bail on the first error', -> 202 | { stdout } = yield run ['-b', fileURL('mixed')] 203 | stdout.should.contain '1 failing' 204 | 205 | describe 'path', -> 206 | it 'will use the custom path to phantomjs', -> 207 | { stderr } = yield run ['-p', 'fake/path/to/phantomjs', fileURL('passing')] 208 | stderr.should.contain "PhantomJS does not exist at 'fake/path/to/phantomjs'" 209 | 210 | it 'provides a useful error when phantomjs cannot be launched', -> 211 | { stderr } = yield run ['-p', 'package.json', fileURL('passing')] 212 | stderr.should.contain "An error occurred trying to launch phantomjs" 213 | 214 | describe 'file', -> 215 | it 'pipes reporter output to a file', -> 216 | { stdout } = yield run ['-f', 'reporteroutput.json', '-R', 'json', fileURL('file')] 217 | stdout.should.contain 'Extraneous' 218 | results = JSON.parse fs.readFileSync 'reporteroutput.json', { encoding: 'utf8' } 219 | results.passes.length.should.equal 6 220 | results.failures.length.should.equal 6 221 | 222 | after -> 223 | fs.unlinkSync 'reporteroutput.json' 224 | 225 | describe 'ignore resource errors', -> 226 | it 'by default shows resource errors', -> 227 | { code, stderr } = yield run [fileURL('resource-errors')] 228 | stderr.should.contain('Error loading resource').and.contain('nonexistant-file.css') 229 | code.should.equal 0 230 | 231 | it 'can suppress resource errors', -> 232 | { stderr } = yield run ['--ignore-resource-errors', fileURL('resource-errors')] 233 | stderr.should.be.empty 234 | 235 | describe 'env', -> 236 | it 'has passed environment variables', -> 237 | process.env.FOO = 'yowzer' 238 | { stdout } = yield run [fileURL('env')] 239 | stdout.should.match /^yowzer/ 240 | -------------------------------------------------------------------------------- /test/mocha-runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Failing 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 26 | 39 | 40 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require co-mocha 2 | --slow 5000 3 | -------------------------------------------------------------------------------- /test/no-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | No tests 5 | 6 | 7 | 8 |
9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/passing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Passing 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/reporters/3rd-party.js: -------------------------------------------------------------------------------- 1 | var Base = require('./base') 2 | , escape = function(x) { 3 | return (x || '').replace('&', '&').replace('<', '>').replace('>', '<') 4 | }; 5 | 6 | exports = module.exports = CustomDoc; 7 | 8 | function CustomDoc(runner) { 9 | Base.call(this, runner); 10 | 11 | var self = this 12 | , stats = this.stats 13 | , total = runner.total 14 | , indents = 2; 15 | 16 | function indent() { 17 | return Array(indents).join(' '); 18 | } 19 | 20 | runner.on('suite', function(suite){ 21 | if (suite.root) return; 22 | ++indents; 23 | process.stdout.write(indent() + '
\n'); 24 | ++indents; 25 | process.stdout.write(indent() + '

' + suite.title + '

\n'); 26 | process.stdout.write(indent() + '
\n'); 27 | }); 28 | 29 | runner.on('suite end', function(suite){ 30 | if (suite.root) return; 31 | process.stdout.write(indent() + '
\n'); 32 | --indents; 33 | process.stdout.write(indent() + '
\n'); 34 | --indents; 35 | }); 36 | 37 | runner.on('pass', function(test){ 38 | process.stdout.write(indent() + '
' + escape(test.title) + '
\n'); 39 | var code = escape(test.fn.toString()); 40 | process.stdout.write(indent() + '
' + code + '
\n'); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /test/reporters/node-only.js: -------------------------------------------------------------------------------- 1 | var Base = require('./base') 2 | , fs = require('fs') 3 | 4 | exports = module.exports = NodeOnly; 5 | 6 | function NodeOnly(runner) { 7 | Base.call(this, runner); 8 | 9 | runner.on('suite end', function(suite){ 10 | fs.writeFile('myresults', function(err) { 11 | // do some stuff 12 | }); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /test/resource-errors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Resource Errors 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/screenshot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Many Tests 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/slow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Slow tests 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/timeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Start Timeout 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/user-agent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests UserAgent 5 | 6 | 7 | 8 | 9 |
10 | 11 | 15 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/viewport.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests Viewport 5 | 6 | 7 | 8 | 9 |
10 | 11 | 15 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------