Adding delegated implementation
This commit is contained in:
1 parent
3cfbd0ea63
commit
7238b09112
326 files changed
+109874
-20
No files matched your search
+2000
File diff suppressed because it is too large.
Load diff
+22
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2011-2018 JS Foundation and contributors, https://js.foundation
|
||||
|
||||
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.
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<p align="center">
|
||||
<img src="https://cldup.com/xFVFxOioAU.svg" alt="Mocha test framework"/>
|
||||
</p>
|
||||
|
||||
<p align="center">:coffee: Simple, flexible, fun JavaScript test framework for Node.js & The Browser :coffee:</p>
|
||||
|
||||
<p align="center"><a href="http://travis-ci.org/mochajs/mocha"><img src="https://api.travis-ci.org/mochajs/mocha.svg?branch=master" alt="Build Status"></a> <a href="https://coveralls.io/github/mochajs/mocha"><img src="https://coveralls.io/repos/github/mochajs/mocha/badge.svg" alt="Coverage Status"></a> <a href="https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_shield"><img src="https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha.svg?type=shield" alt="FOSSA Status"></a> <a href="https://gitter.im/mochajs/mocha?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://badges.gitter.im/Join%20Chat.svg" alt="Gitter"></a> <a href="https://github.com/mochajs/mocha#backers"><img src="https://opencollective.com/mochajs/backers/badge.svg" alt="OpenCollective"></a> <a href="https://github.com/mochajs/mocha#sponsors"><img src="https://opencollective.com/mochajs/sponsors/badge.svg" alt="OpenCollective"></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><br><img alt="Mocha Browser Support h/t SauceLabs" src="https://saucelabs.com/browser-matrix/mochajs.svg" width="354"></p>
|
||||
|
||||
## Links
|
||||
|
||||
- **[Documentation](https://mochajs.org)**
|
||||
- **[Release Notes / History / Changes](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)**
|
||||
- [Code of Conduct](https://github.com/mochajs/mocha/blob/master/.github/CODE_OF_CONDUCT.md)
|
||||
- [Gitter Chatroom](https://gitter.im/mochajs/mocha) (ask questions here!)
|
||||
- [Google Group](https://groups.google.com/group/mochajs)
|
||||
- [Issue Tracker](https://github.com/mochajs/mocha/issues)
|
||||
|
||||
## Backers
|
||||
|
||||
[Become a backer](https://opencollective.com/mochajs#backer) and show your support to our open source project.
|
||||
|
||||
[](https://opencollective.com/mochajs/backer/0/website)
|
||||
[](https://opencollective.com/mochajs/backer/1/website)
|
||||
[](https://opencollective.com/mochajs/backer/2/website)
|
||||
[](https://opencollective.com/mochajs/backer/3/website)
|
||||
[](https://opencollective.com/mochajs/backer/4/website)
|
||||
[](https://opencollective.com/mochajs/backer/5/website)
|
||||
[](https://opencollective.com/mochajs/backer/6/website)
|
||||
[](https://opencollective.com/mochajs/backer/7/website)
|
||||
[](https://opencollective.com/mochajs/backer/8/website)
|
||||
[](https://opencollective.com/mochajs/backer/9/website)
|
||||
[](https://opencollective.com/mochajs/backer/10/website)
|
||||
[](https://opencollective.com/mochajs/backer/11/website)
|
||||
[](https://opencollective.com/mochajs/backer/12/website)
|
||||
[](https://opencollective.com/mochajs/backer/13/website)
|
||||
[](https://opencollective.com/mochajs/backer/14/website)
|
||||
[](https://opencollective.com/mochajs/backer/15/website)
|
||||
[](https://opencollective.com/mochajs/backer/16/website)
|
||||
[](https://opencollective.com/mochajs/backer/17/website)
|
||||
[](https://opencollective.com/mochajs/backer/18/website)
|
||||
[](https://opencollective.com/mochajs/backer/19/website)
|
||||
[](https://opencollective.com/mochajs/backer/20/website)
|
||||
[](https://opencollective.com/mochajs/backer/21/website)
|
||||
[](https://opencollective.com/mochajs/backer/22/website)
|
||||
[](https://opencollective.com/mochajs/backer/23/website)
|
||||
[](https://opencollective.com/mochajs/backer/24/website)
|
||||
[](https://opencollective.com/mochajs/backer/25/website)
|
||||
[](https://opencollective.com/mochajs/backer/26/website)
|
||||
[](https://opencollective.com/mochajs/backer/27/website)
|
||||
[](https://opencollective.com/mochajs/backer/28/website)
|
||||
[](https://opencollective.com/mochajs/backer/29/website)
|
||||
|
||||
## Sponsors
|
||||
|
||||
Does your company use Mocha? Ask your manager or marketing team if your company would be interested in supporting our project. Support will allow the maintainers to dedicate more time for maintenance and new features for everyone. Also, your company's logo will show [on GitHub](https://github.com/mochajs/mocha#readme) and on [our site](https://mochajs.org) - who doesn't want a little extra exposure? [Here's the info](https://opencollective.com/mochajs#sponsor).
|
||||
|
||||
[](https://opencollective.com/mochajs/sponsor/0/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/1/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/2/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/3/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/4/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/5/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/6/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/7/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/8/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/9/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/10/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/11/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/12/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/13/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/14/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/15/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/16/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/17/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/18/website)
|
||||
[](https://opencollective.com/mochajs/sponsor/19/website)
|
||||
|
||||
## Development
|
||||
|
||||
You might want to know that:
|
||||
|
||||
- Mocha is the *most-depended-upon* module on npm (source: [libraries.io](https://libraries.io/search?order=desc&platforms=NPM&sort=dependents_count)), and
|
||||
- Mocha is an *independent* open-source project, maintained exclusively by volunteers.
|
||||
|
||||
You might want to help:
|
||||
|
||||
- New to contributing to Mocha? Check out this list of [good first issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue)
|
||||
- Mocha could use a hand with [these issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
|
||||
- The [maintainer's handbook](https://github.com/mochajs/mocha/blob/master/MAINTAINERS.md) explains how things get done
|
||||
|
||||
Finally, come [chat with the maintainers](https://gitter.im/mochajs/contributors) on Gitter if you want to help with:
|
||||
|
||||
- Triaging issues, answering questions
|
||||
- Review, merging, and closing pull requests
|
||||
- Other project-maintenance-y things
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_large)
|
||||
+647
@@ -0,0 +1,647 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/* eslint no-unused-vars: off */
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const program = require('commander');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const minimatch = require('minimatch');
|
||||
const resolve = path.resolve;
|
||||
const exists = fs.existsSync;
|
||||
const Mocha = require('../');
|
||||
const utils = Mocha.utils;
|
||||
const interfaceNames = Object.keys(Mocha.interfaces);
|
||||
const join = path.join;
|
||||
const cwd = process.cwd();
|
||||
const getOptions = require('./options');
|
||||
const mocha = new Mocha();
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
|
||||
const Date = global.Date;
|
||||
const setTimeout = global.setTimeout;
|
||||
const setInterval = global.setInterval;
|
||||
const clearTimeout = global.clearTimeout;
|
||||
const clearInterval = global.clearInterval;
|
||||
|
||||
/**
|
||||
* Exits Mocha when tests + code under test has finished execution (default)
|
||||
* @param {number} code - Exit code; typically # of failures
|
||||
*/
|
||||
const exitLater = code => {
|
||||
process.on('exit', () => {
|
||||
process.exit(Math.min(code, 255));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exits Mocha when Mocha itself has finished execution, regardless of
|
||||
* what the tests or code under test is doing.
|
||||
* @param {number} code - Exit code; typically # of failures
|
||||
*/
|
||||
const exit = code => {
|
||||
const clampedCode = Math.min(code, 255);
|
||||
let draining = 0;
|
||||
|
||||
// Eagerly set the process's exit code in case stream.write doesn't
|
||||
// execute its callback before the process terminates.
|
||||
process.exitCode = clampedCode;
|
||||
|
||||
// flush output for Node.js Windows pipe bug
|
||||
// https://github.com/joyent/node/issues/6247 is just one bug example
|
||||
// https://github.com/visionmedia/mocha/issues/333 has a good discussion
|
||||
const done = () => {
|
||||
if (!draining--) {
|
||||
process.exit(clampedCode);
|
||||
}
|
||||
};
|
||||
|
||||
const streams = [process.stdout, process.stderr];
|
||||
|
||||
streams.forEach(stream => {
|
||||
// submit empty write request and wait for completion
|
||||
draining += 1;
|
||||
stream.write('', done);
|
||||
});
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse list.
|
||||
*/
|
||||
const list = str => str.split(/ *, */);
|
||||
|
||||
/**
|
||||
* Parse multiple flag.
|
||||
*/
|
||||
const collect = (val, memo) => memo.concat(val);
|
||||
|
||||
/**
|
||||
* Hide the cursor.
|
||||
*/
|
||||
const hideCursor = () => {
|
||||
process.stdout.write('\u001b[?25l');
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the cursor.
|
||||
*/
|
||||
const showCursor = () => {
|
||||
process.stdout.write('\u001b[?25h');
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop play()ing.
|
||||
*/
|
||||
const stop = () => {
|
||||
process.stdout.write('\u001b[2K');
|
||||
clearInterval(play.timer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Play the given array of strings.
|
||||
*/
|
||||
const play = (arr, interval) => {
|
||||
const len = arr.length;
|
||||
interval = interval || 100;
|
||||
let i = 0;
|
||||
|
||||
play.timer = setInterval(() => {
|
||||
const str = arr[i++ % len];
|
||||
process.stdout.write(`\u001b[0G${str}`);
|
||||
}, interval);
|
||||
};
|
||||
|
||||
/**
|
||||
* Files.
|
||||
*/
|
||||
|
||||
let files = [];
|
||||
|
||||
/**
|
||||
* Globals.
|
||||
*/
|
||||
|
||||
let globals = [];
|
||||
|
||||
/**
|
||||
* Requires.
|
||||
*/
|
||||
|
||||
const requires = [];
|
||||
|
||||
/**
|
||||
* Images.
|
||||
*/
|
||||
|
||||
const images = {
|
||||
fail: path.join(__dirname, '..', 'assets', 'growl', 'error.png'),
|
||||
pass: path.join(__dirname, '..', 'assets', 'growl', 'ok.png')
|
||||
};
|
||||
|
||||
// options
|
||||
|
||||
program
|
||||
.version(
|
||||
JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
|
||||
).version
|
||||
)
|
||||
.usage('[debug] [options] [files]')
|
||||
.option(
|
||||
'-A, --async-only',
|
||||
'force all tests to take a callback (async) or return a promise'
|
||||
)
|
||||
.option('-c, --colors', 'force enabling of colors')
|
||||
.option('-C, --no-colors', 'force disabling of colors')
|
||||
.option('-G, --growl', 'enable growl notification support')
|
||||
.option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
|
||||
.option('-R, --reporter <name>', 'specify the reporter to use', 'spec')
|
||||
.option('-S, --sort', 'sort test files')
|
||||
.option('-b, --bail', 'bail after first test failure')
|
||||
.option('-d, --debug', "enable node's debugger, synonym for node --debug")
|
||||
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
|
||||
.option('-f, --fgrep <string>', 'only run tests containing <string>')
|
||||
.option('-gc, --expose-gc', 'expose gc extension')
|
||||
.option('-i, --invert', 'inverts --grep and --fgrep matches')
|
||||
.option('-r, --require <name>', 'require the given module')
|
||||
.option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
|
||||
.option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
|
||||
.option(
|
||||
'-u, --ui <name>',
|
||||
`specify user-interface (${interfaceNames.join('|')})`,
|
||||
'bdd'
|
||||
)
|
||||
.option('-w, --watch', 'watch files for changes')
|
||||
.option('--check-leaks', 'check for global variable leaks')
|
||||
.option('--full-trace', 'display the full stack trace')
|
||||
.option(
|
||||
'--compilers <ext>:<module>,...',
|
||||
'use the given module(s) to compile files',
|
||||
list,
|
||||
[]
|
||||
)
|
||||
.option('--debug-brk', "enable node's debugger breaking on the first line")
|
||||
.option(
|
||||
'--globals <names>',
|
||||
'allow the given comma-delimited global [names]',
|
||||
list,
|
||||
[]
|
||||
)
|
||||
.option('--es_staging', 'enable all staged features')
|
||||
.option(
|
||||
'--harmony<_classes,_generators,...>',
|
||||
'all node --harmony* flags are available'
|
||||
)
|
||||
.option(
|
||||
'--preserve-symlinks',
|
||||
'Instructs the module loader to preserve symbolic links when resolving and caching modules'
|
||||
)
|
||||
.option('--icu-data-dir', 'include ICU data')
|
||||
.option(
|
||||
'--inline-diffs',
|
||||
'display actual/expected differences inline within each string'
|
||||
)
|
||||
.option('--no-diff', 'do not show a diff on failure')
|
||||
.option('--inspect', 'activate devtools in chrome')
|
||||
.option(
|
||||
'--inspect-brk',
|
||||
'activate devtools in chrome and break on the first line'
|
||||
)
|
||||
.option('--interfaces', 'display available interfaces')
|
||||
.option('--no-deprecation', 'silence deprecation warnings')
|
||||
.option(
|
||||
'--exit',
|
||||
'force shutdown of the event loop after test run: mocha will call process.exit'
|
||||
)
|
||||
.option('--no-timeouts', 'disables timeouts, given implicitly with --debug')
|
||||
.option('--no-warnings', 'silence all node process warnings')
|
||||
.option('--opts <path>', 'specify opts path', 'test/mocha.opts')
|
||||
.option('--perf-basic-prof', 'enable perf linux profiler (basic support)')
|
||||
.option('--napi-modules', 'enable experimental NAPI modules')
|
||||
.option('--prof', 'log statistical profiling information')
|
||||
.option('--log-timer-events', 'Time events including external callbacks')
|
||||
.option('--recursive', 'include sub directories')
|
||||
.option('--reporters', 'display available reporters')
|
||||
.option(
|
||||
'--retries <times>',
|
||||
'set numbers of time to retry a failed test case'
|
||||
)
|
||||
.option(
|
||||
'--throw-deprecation',
|
||||
'throw an exception anytime a deprecated function is used'
|
||||
)
|
||||
.option('--trace', 'trace function calls')
|
||||
.option('--trace-deprecation', 'show stack traces on deprecations')
|
||||
.option('--trace-warnings', 'show stack traces on node process warnings')
|
||||
.option('--use_strict', 'enforce strict mode')
|
||||
.option(
|
||||
'--watch-extensions <ext>,...',
|
||||
'additional extensions to monitor with --watch',
|
||||
list,
|
||||
['js']
|
||||
)
|
||||
.option('--delay', 'wait for async suite definition')
|
||||
.option('--allow-uncaught', 'enable uncaught errors to propagate')
|
||||
.option('--forbid-only', 'causes test marked with only to fail the suite')
|
||||
.option(
|
||||
'--forbid-pending',
|
||||
'causes pending tests and test marked with skip to fail the suite'
|
||||
)
|
||||
.option(
|
||||
'--file <file>',
|
||||
'include a file to be ran during the suite',
|
||||
collect,
|
||||
[]
|
||||
)
|
||||
.option('--exclude <file>', 'a file or glob pattern to ignore', collect, []);
|
||||
|
||||
program._name = 'mocha';
|
||||
|
||||
// init command
|
||||
|
||||
program
|
||||
.command('init <path>')
|
||||
.description('initialize a client-side mocha setup at <path>')
|
||||
.action(path => {
|
||||
const mkdir = require('mkdirp');
|
||||
mkdir.sync(path);
|
||||
const css = fs.readFileSync(join(__dirname, '..', 'mocha.css'));
|
||||
const js = fs.readFileSync(join(__dirname, '..', 'mocha.js'));
|
||||
const tmpl = fs.readFileSync(join(__dirname, '..', 'lib/template.html'));
|
||||
fs.writeFileSync(join(path, 'mocha.css'), css);
|
||||
fs.writeFileSync(join(path, 'mocha.js'), js);
|
||||
fs.writeFileSync(join(path, 'tests.js'), '');
|
||||
fs.writeFileSync(join(path, 'index.html'), tmpl);
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// --globals
|
||||
|
||||
program.on('option:globals', val => {
|
||||
globals = globals.concat(list(val));
|
||||
});
|
||||
|
||||
// --reporters
|
||||
|
||||
program.on('option:reporters', () => {
|
||||
console.log();
|
||||
console.log(' dot - dot matrix');
|
||||
console.log(' doc - html documentation');
|
||||
console.log(' spec - hierarchical spec list');
|
||||
console.log(' json - single json object');
|
||||
console.log(' progress - progress bar');
|
||||
console.log(' list - spec-style listing');
|
||||
console.log(' tap - test-anything-protocol');
|
||||
console.log(' landing - unicode landing strip');
|
||||
console.log(' xunit - xunit reporter');
|
||||
console.log(' min - minimal reporter (great with --watch)');
|
||||
console.log(' json-stream - newline delimited json events');
|
||||
console.log(' markdown - markdown documentation (github flavour)');
|
||||
console.log(' nyan - nyan cat!');
|
||||
console.log();
|
||||
process.exit();
|
||||
});
|
||||
|
||||
// --interfaces
|
||||
|
||||
program.on('option:interfaces', () => {
|
||||
console.log('');
|
||||
interfaceNames.forEach(interfaceName => {
|
||||
console.log(` ${interfaceName}`);
|
||||
});
|
||||
console.log('');
|
||||
process.exit();
|
||||
});
|
||||
|
||||
// -r, --require
|
||||
|
||||
module.paths.push(cwd, join(cwd, 'node_modules'));
|
||||
|
||||
program.on('option:require', mod => {
|
||||
const abs = exists(mod) || exists(`${mod}.js`);
|
||||
if (abs) {
|
||||
mod = resolve(mod);
|
||||
}
|
||||
requires.push(mod);
|
||||
});
|
||||
|
||||
// If not already done, load mocha.opts
|
||||
if (!process.env.LOADED_MOCHA_OPTS) {
|
||||
getOptions();
|
||||
}
|
||||
|
||||
// parse args
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
// infinite stack traces
|
||||
|
||||
Error.stackTraceLimit = Infinity; // TODO: config
|
||||
|
||||
// reporter options
|
||||
|
||||
const reporterOptions = {};
|
||||
if (program.reporterOptions !== undefined) {
|
||||
program.reporterOptions.split(',').forEach(opt => {
|
||||
const L = opt.split('=');
|
||||
if (L.length > 2 || L.length === 0) {
|
||||
throw new Error(`invalid reporter option '${opt}'`);
|
||||
} else if (L.length === 2) {
|
||||
reporterOptions[L[0]] = L[1];
|
||||
} else {
|
||||
reporterOptions[L[0]] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// reporter
|
||||
|
||||
mocha.reporter(program.reporter, reporterOptions);
|
||||
|
||||
// --no-colors
|
||||
|
||||
if (!program.colors) {
|
||||
mocha.useColors(false);
|
||||
}
|
||||
|
||||
// --colors
|
||||
|
||||
if (~process.argv.indexOf('--colors') || ~process.argv.indexOf('-c')) {
|
||||
mocha.useColors(true);
|
||||
}
|
||||
|
||||
// --inline-diffs
|
||||
|
||||
if (program.inlineDiffs) {
|
||||
mocha.useInlineDiffs(true);
|
||||
}
|
||||
|
||||
// --no-diff
|
||||
|
||||
if (process.argv.indexOf('--no-diff') !== -1) {
|
||||
mocha.hideDiff(true);
|
||||
}
|
||||
|
||||
// --slow <ms>
|
||||
|
||||
if (program.slow) {
|
||||
mocha.suite.slow(program.slow);
|
||||
}
|
||||
|
||||
// --no-timeouts
|
||||
|
||||
if (!program.timeouts) {
|
||||
mocha.enableTimeouts(false);
|
||||
}
|
||||
|
||||
// --timeout
|
||||
|
||||
if (program.timeout) {
|
||||
mocha.suite.timeout(program.timeout);
|
||||
}
|
||||
|
||||
// --bail
|
||||
|
||||
mocha.suite.bail(program.bail);
|
||||
|
||||
// --grep
|
||||
|
||||
if (program.grep) {
|
||||
mocha.grep(program.grep);
|
||||
}
|
||||
|
||||
// --fgrep
|
||||
|
||||
if (program.fgrep) {
|
||||
mocha.fgrep(program.fgrep);
|
||||
}
|
||||
|
||||
// --invert
|
||||
|
||||
if (program.invert) {
|
||||
mocha.invert();
|
||||
}
|
||||
|
||||
// --check-leaks
|
||||
|
||||
if (program.checkLeaks) {
|
||||
mocha.checkLeaks();
|
||||
}
|
||||
|
||||
// --stack-trace
|
||||
|
||||
if (program.fullTrace) {
|
||||
mocha.fullTrace();
|
||||
}
|
||||
|
||||
// --growl
|
||||
|
||||
if (program.growl) {
|
||||
mocha.growl();
|
||||
}
|
||||
|
||||
// --async-only
|
||||
|
||||
if (program.asyncOnly) {
|
||||
mocha.asyncOnly();
|
||||
}
|
||||
|
||||
// --delay
|
||||
|
||||
if (program.delay) {
|
||||
mocha.delay();
|
||||
}
|
||||
|
||||
// --allow-uncaught
|
||||
|
||||
if (program.allowUncaught) {
|
||||
mocha.allowUncaught();
|
||||
}
|
||||
|
||||
// --globals
|
||||
|
||||
mocha.globals(globals);
|
||||
|
||||
// --retries
|
||||
|
||||
if (program.retries) {
|
||||
mocha.suite.retries(program.retries);
|
||||
}
|
||||
|
||||
// --forbid-only
|
||||
|
||||
if (program.forbidOnly) mocha.forbidOnly();
|
||||
|
||||
// --forbid-pending
|
||||
|
||||
if (program.forbidPending) mocha.forbidPending();
|
||||
|
||||
// custom compiler support
|
||||
|
||||
if (program.compilers.length > 0) {
|
||||
require('util').deprecate(() => {},
|
||||
'"--compilers" will be removed in a future version of Mocha; see https://git.io/vdcSr for more info')();
|
||||
}
|
||||
const extensions = ['js'];
|
||||
program.compilers.forEach(c => {
|
||||
const idx = c.indexOf(':');
|
||||
const ext = c.slice(0, idx);
|
||||
let mod = c.slice(idx + 1);
|
||||
|
||||
if (mod[0] === '.') {
|
||||
mod = join(process.cwd(), mod);
|
||||
}
|
||||
require(mod);
|
||||
extensions.push(ext);
|
||||
program.watchExtensions.push(ext);
|
||||
});
|
||||
|
||||
// requires
|
||||
|
||||
requires.forEach(mod => {
|
||||
require(mod);
|
||||
});
|
||||
|
||||
// interface
|
||||
|
||||
mocha.ui(program.ui);
|
||||
|
||||
// args
|
||||
|
||||
const args = program.args;
|
||||
|
||||
// default files to test/*.{js,coffee}
|
||||
|
||||
if (!args.length) {
|
||||
args.push('test');
|
||||
}
|
||||
|
||||
args.forEach(arg => {
|
||||
let newFiles;
|
||||
try {
|
||||
newFiles = utils.lookupFiles(arg, extensions, program.recursive);
|
||||
} catch (err) {
|
||||
if (err.message.indexOf('cannot resolve path') === 0) {
|
||||
console.error(
|
||||
`Warning: Could not find any test files matching pattern: ${arg}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (typeof newFiles !== 'undefined') {
|
||||
if (typeof newFiles === 'string') {
|
||||
newFiles = [newFiles];
|
||||
}
|
||||
newFiles = newFiles.filter(fileName =>
|
||||
program.exclude.every(pattern => !minimatch(fileName, pattern))
|
||||
);
|
||||
}
|
||||
|
||||
files = files.concat(newFiles);
|
||||
});
|
||||
|
||||
if (!files.length) {
|
||||
console.error('No test files found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// resolve
|
||||
let fileArgs = program.file.map(path => resolve(path));
|
||||
files = files.map(path => resolve(path));
|
||||
|
||||
if (program.sort) {
|
||||
files.sort();
|
||||
}
|
||||
|
||||
// add files given through --file to be ran first
|
||||
files = fileArgs.concat(files);
|
||||
|
||||
// --watch
|
||||
|
||||
let runner;
|
||||
let loadAndRun;
|
||||
let purge;
|
||||
let rerun;
|
||||
|
||||
if (program.watch) {
|
||||
console.log();
|
||||
hideCursor();
|
||||
process.on('SIGINT', () => {
|
||||
showCursor();
|
||||
console.log('\n');
|
||||
process.exit(130);
|
||||
});
|
||||
|
||||
const watchFiles = utils.files(cwd, ['js'].concat(program.watchExtensions));
|
||||
let runAgain = false;
|
||||
|
||||
loadAndRun = () => {
|
||||
try {
|
||||
mocha.files = files;
|
||||
runAgain = false;
|
||||
runner = mocha.run(() => {
|
||||
runner = null;
|
||||
if (runAgain) {
|
||||
rerun();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e.stack);
|
||||
}
|
||||
};
|
||||
|
||||
purge = () => {
|
||||
watchFiles.forEach(file => {
|
||||
delete require.cache[file];
|
||||
});
|
||||
};
|
||||
|
||||
loadAndRun();
|
||||
|
||||
rerun = () => {
|
||||
purge();
|
||||
stop();
|
||||
if (!program.grep) {
|
||||
mocha.grep(null);
|
||||
}
|
||||
mocha.suite = mocha.suite.clone();
|
||||
mocha.suite.ctx = new Mocha.Context();
|
||||
mocha.ui(program.ui);
|
||||
loadAndRun();
|
||||
};
|
||||
|
||||
utils.watch(watchFiles, () => {
|
||||
runAgain = true;
|
||||
if (runner) {
|
||||
runner.abort();
|
||||
} else {
|
||||
rerun();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// load
|
||||
|
||||
mocha.files = files;
|
||||
runner = mocha.run(program.exit ? exit : exitLater);
|
||||
}
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
runner.abort();
|
||||
|
||||
// This is a hack:
|
||||
// Instead of `process.exit(130)`, set runner.failures to 130 (exit code for SIGINT)
|
||||
// The amount of failures will be emitted as error code later
|
||||
runner.failures = 130;
|
||||
});
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This tiny wrapper file checks for known node flags and appends them
|
||||
* when found, before invoking the "real" _mocha(1) executable.
|
||||
*/
|
||||
|
||||
const spawn = require('child_process').spawn;
|
||||
const path = require('path');
|
||||
const getOptions = require('./options');
|
||||
const args = [path.join(__dirname, '_mocha')];
|
||||
|
||||
// Load mocha.opts into process.argv
|
||||
// Must be loaded here to handle node-specific options
|
||||
getOptions();
|
||||
|
||||
process.argv.slice(2).forEach(arg => {
|
||||
const flag = arg.split('=')[0];
|
||||
|
||||
switch (flag) {
|
||||
case '-d':
|
||||
args.unshift('--debug');
|
||||
args.push('--no-timeouts');
|
||||
break;
|
||||
case 'debug':
|
||||
case '--debug':
|
||||
case '--debug-brk':
|
||||
case '--inspect':
|
||||
case '--inspect-brk':
|
||||
args.unshift(arg);
|
||||
args.push('--no-timeouts');
|
||||
break;
|
||||
case '-gc':
|
||||
case '--expose-gc':
|
||||
args.unshift('--expose-gc');
|
||||
break;
|
||||
case '--gc-global':
|
||||
case '--es_staging':
|
||||
case '--no-deprecation':
|
||||
case '--no-warnings':
|
||||
case '--prof':
|
||||
case '--log-timer-events':
|
||||
case '--throw-deprecation':
|
||||
case '--trace-deprecation':
|
||||
case '--trace-warnings':
|
||||
case '--use_strict':
|
||||
case '--allow-natives-syntax':
|
||||
case '--perf-basic-prof':
|
||||
case '--napi-modules':
|
||||
args.unshift(arg);
|
||||
break;
|
||||
default:
|
||||
if (arg.indexOf('--harmony') === 0) {
|
||||
args.unshift(arg);
|
||||
} else if (arg.indexOf('--trace') === 0) {
|
||||
args.unshift(arg);
|
||||
} else if (arg.indexOf('--icu-data-dir') === 0) {
|
||||
args.unshift(arg);
|
||||
} else if (arg.indexOf('--max-old-space-size') === 0) {
|
||||
args.unshift(arg);
|
||||
} else if (arg.indexOf('--preserve-symlinks') === 0) {
|
||||
args.unshift(arg);
|
||||
} else {
|
||||
args.push(arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const proc = spawn(process.execPath, args, {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
proc.on('exit', (code, signal) => {
|
||||
process.on('exit', () => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// terminate children.
|
||||
process.on('SIGINT', () => {
|
||||
proc.kill('SIGINT'); // calls runner.abort()
|
||||
proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die.
|
||||
});
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Export `getOptions`.
|
||||
*/
|
||||
|
||||
module.exports = getOptions;
|
||||
|
||||
/**
|
||||
* Get options.
|
||||
*/
|
||||
|
||||
function getOptions() {
|
||||
if (
|
||||
process.argv.length === 3 &&
|
||||
(process.argv[2] === '-h' || process.argv[2] === '--help')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const optsPath =
|
||||
process.argv.indexOf('--opts') === -1
|
||||
? 'test/mocha.opts'
|
||||
: process.argv[process.argv.indexOf('--opts') + 1];
|
||||
|
||||
try {
|
||||
const opts = fs
|
||||
.readFileSync(optsPath, 'utf8')
|
||||
.replace(/^#.*$/gm, '')
|
||||
.replace(/\\\s/g, '%20')
|
||||
.split(/\s/)
|
||||
.filter(Boolean)
|
||||
.map(value => value.replace(/%20/g, ' '));
|
||||
|
||||
process.argv = process.argv
|
||||
.slice(0, 2)
|
||||
.concat(opts.concat(process.argv.slice(2)));
|
||||
} catch (ignore) {
|
||||
// NOTE: should console.error() and throw the error
|
||||
}
|
||||
|
||||
process.env.LOADED_MOCHA_OPTS = true;
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint no-unused-vars: off */
|
||||
/* eslint-env commonjs */
|
||||
|
||||
/**
|
||||
* Shim process.stdout.
|
||||
*/
|
||||
|
||||
process.stdout = require('browser-stdout')({level: false});
|
||||
|
||||
var Mocha = require('./lib/mocha');
|
||||
|
||||
/**
|
||||
* Create a Mocha instance.
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
|
||||
var mocha = new Mocha({reporter: 'html'});
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var setInterval = global.setInterval;
|
||||
var clearTimeout = global.clearTimeout;
|
||||
var clearInterval = global.clearInterval;
|
||||
|
||||
var uncaughtExceptionHandlers = [];
|
||||
|
||||
var originalOnerrorHandler = global.onerror;
|
||||
|
||||
/**
|
||||
* Remove uncaughtException listener.
|
||||
* Revert to original onerror handler if previously defined.
|
||||
*/
|
||||
|
||||
process.removeListener = function(e, fn) {
|
||||
if (e === 'uncaughtException') {
|
||||
if (originalOnerrorHandler) {
|
||||
global.onerror = originalOnerrorHandler;
|
||||
} else {
|
||||
global.onerror = function() {};
|
||||
}
|
||||
var i = uncaughtExceptionHandlers.indexOf(fn);
|
||||
if (i !== -1) {
|
||||
uncaughtExceptionHandlers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements uncaughtException listener.
|
||||
*/
|
||||
|
||||
process.on = function(e, fn) {
|
||||
if (e === 'uncaughtException') {
|
||||
global.onerror = function(err, url, line) {
|
||||
fn(new Error(err + ' (' + url + ':' + line + ')'));
|
||||
return !mocha.allowUncaught;
|
||||
};
|
||||
uncaughtExceptionHandlers.push(fn);
|
||||
}
|
||||
};
|
||||
|
||||
// The BDD UI is registered by default, but no UI will be functional in the
|
||||
// browser without an explicit call to the overridden `mocha.ui` (see below).
|
||||
// Ensure that this default UI does not expose its methods to the global scope.
|
||||
mocha.suite.removeAllListeners('pre-require');
|
||||
|
||||
var immediateQueue = [];
|
||||
var immediateTimeout;
|
||||
|
||||
function timeslice() {
|
||||
var immediateStart = new Date().getTime();
|
||||
while (immediateQueue.length && new Date().getTime() - immediateStart < 100) {
|
||||
immediateQueue.shift()();
|
||||
}
|
||||
if (immediateQueue.length) {
|
||||
immediateTimeout = setTimeout(timeslice, 0);
|
||||
} else {
|
||||
immediateTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* High-performance override of Runner.immediately.
|
||||
*/
|
||||
|
||||
Mocha.Runner.immediately = function(callback) {
|
||||
immediateQueue.push(callback);
|
||||
if (!immediateTimeout) {
|
||||
immediateTimeout = setTimeout(timeslice, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to allow assertion libraries to throw errors directly into mocha.
|
||||
* This is useful when running tests in a browser because window.onerror will
|
||||
* only receive the 'message' attribute of the Error.
|
||||
*/
|
||||
mocha.throwError = function(err) {
|
||||
uncaughtExceptionHandlers.forEach(function(fn) {
|
||||
fn(err);
|
||||
});
|
||||
throw err;
|
||||
};
|
||||
|
||||
/**
|
||||
* Override ui to ensure that the ui functions are initialized.
|
||||
* Normally this would happen in Mocha.prototype.loadFiles.
|
||||
*/
|
||||
|
||||
mocha.ui = function(ui) {
|
||||
Mocha.prototype.ui.call(this, ui);
|
||||
this.suite.emit('pre-require', global, null, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup mocha with the given setting options.
|
||||
*/
|
||||
|
||||
mocha.setup = function(opts) {
|
||||
if (typeof opts === 'string') {
|
||||
opts = {ui: opts};
|
||||
}
|
||||
for (var opt in opts) {
|
||||
if (opts.hasOwnProperty(opt)) {
|
||||
this[opt](opts[opt]);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run mocha, returning the Runner.
|
||||
*/
|
||||
|
||||
mocha.run = function(fn) {
|
||||
var options = mocha.options;
|
||||
mocha.globals('location');
|
||||
|
||||
var query = Mocha.utils.parseQuery(global.location.search || '');
|
||||
if (query.grep) {
|
||||
mocha.grep(query.grep);
|
||||
}
|
||||
if (query.fgrep) {
|
||||
mocha.fgrep(query.fgrep);
|
||||
}
|
||||
if (query.invert) {
|
||||
mocha.invert();
|
||||
}
|
||||
|
||||
return Mocha.prototype.run.call(mocha, function(err) {
|
||||
// The DOM Document is not available in Web Workers.
|
||||
var document = global.document;
|
||||
if (
|
||||
document &&
|
||||
document.getElementById('mocha') &&
|
||||
options.noHighlighting !== true
|
||||
) {
|
||||
Mocha.utils.highlightTags('code');
|
||||
}
|
||||
if (fn) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose the process shim.
|
||||
* https://github.com/mochajs/mocha/pull/916
|
||||
*/
|
||||
|
||||
Mocha.process = process;
|
||||
|
||||
/**
|
||||
* Expose mocha.
|
||||
*/
|
||||
|
||||
global.Mocha = Mocha;
|
||||
global.mocha = mocha;
|
||||
|
||||
// this allows test/acceptance/required-tokens.js to pass; thus,
|
||||
// you can now do `const describe = require('mocha').describe` in a
|
||||
// browser context (assuming browserification). should fix #880
|
||||
module.exports = global;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./lib/mocha');
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
// just stub out growl
|
||||
|
||||
module.exports = require('../utils').noop;
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Expose `Progress`.
|
||||
*/
|
||||
|
||||
module.exports = Progress;
|
||||
|
||||
/**
|
||||
* Initialize a new `Progress` indicator.
|
||||
*/
|
||||
function Progress() {
|
||||
this.percent = 0;
|
||||
this.size(0);
|
||||
this.fontSize(11);
|
||||
this.font('helvetica, arial, sans-serif');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set progress size to `size`.
|
||||
*
|
||||
* @api public
|
||||
* @param {number} size
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.size = function(size) {
|
||||
this._size = size;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set text to `text`.
|
||||
*
|
||||
* @api public
|
||||
* @param {string} text
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.text = function(text) {
|
||||
this._text = text;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set font size to `size`.
|
||||
*
|
||||
* @api public
|
||||
* @param {number} size
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.fontSize = function(size) {
|
||||
this._fontSize = size;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set font to `family`.
|
||||
*
|
||||
* @param {string} family
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.font = function(family) {
|
||||
this._font = family;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update percentage to `n`.
|
||||
*
|
||||
* @param {number} n
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.update = function(n) {
|
||||
this.percent = n;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw on `ctx`.
|
||||
*
|
||||
* @param {CanvasRenderingContext2d} ctx
|
||||
* @return {Progress} Progress instance.
|
||||
*/
|
||||
Progress.prototype.draw = function(ctx) {
|
||||
try {
|
||||
var percent = Math.min(this.percent, 100);
|
||||
var size = this._size;
|
||||
var half = size / 2;
|
||||
var x = half;
|
||||
var y = half;
|
||||
var rad = half - 1;
|
||||
var fontSize = this._fontSize;
|
||||
|
||||
ctx.font = fontSize + 'px ' + this._font;
|
||||
|
||||
var angle = Math.PI * 2 * (percent / 100);
|
||||
ctx.clearRect(0, 0, size, size);
|
||||
|
||||
// outer circle
|
||||
ctx.strokeStyle = '#9f9f9f';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, rad, 0, angle, false);
|
||||
ctx.stroke();
|
||||
|
||||
// inner circle
|
||||
ctx.strokeStyle = '#eee';
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, rad - 1, 0, angle, true);
|
||||
ctx.stroke();
|
||||
|
||||
// text
|
||||
var text = this._text || (percent | 0) + '%';
|
||||
var w = ctx.measureText(text).width;
|
||||
|
||||
ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1);
|
||||
} catch (ignore) {
|
||||
// don't fail if we can't render progress
|
||||
}
|
||||
return this;
|
||||
};
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
exports.isatty = function isatty() {
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.getWindowSize = function getWindowSize() {
|
||||
if ('innerHeight' in global) {
|
||||
return [global.innerHeight, global.innerWidth];
|
||||
}
|
||||
// In a Web Worker, the DOM Window is not available.
|
||||
return [640, 480];
|
||||
};
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Context
|
||||
*/
|
||||
/**
|
||||
* Expose `Context`.
|
||||
*/
|
||||
|
||||
module.exports = Context;
|
||||
|
||||
/**
|
||||
* Initialize a new `Context`.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
function Context() {}
|
||||
|
||||
/**
|
||||
* Set or get the context `Runnable` to `runnable`.
|
||||
*
|
||||
* @api private
|
||||
* @param {Runnable} runnable
|
||||
* @return {Context} context
|
||||
*/
|
||||
Context.prototype.runnable = function(runnable) {
|
||||
if (!arguments.length) {
|
||||
return this._runnable;
|
||||
}
|
||||
this.test = this._runnable = runnable;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get test timeout `ms`.
|
||||
*
|
||||
* @api private
|
||||
* @param {number} ms
|
||||
* @return {Context} self
|
||||
*/
|
||||
Context.prototype.timeout = function(ms) {
|
||||
if (!arguments.length) {
|
||||
return this.runnable().timeout();
|
||||
}
|
||||
this.runnable().timeout(ms);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set test timeout `enabled`.
|
||||
*
|
||||
* @api private
|
||||
* @param {boolean} enabled
|
||||
* @return {Context} self
|
||||
*/
|
||||
Context.prototype.enableTimeouts = function(enabled) {
|
||||
if (!arguments.length) {
|
||||
return this.runnable().enableTimeouts();
|
||||
}
|
||||
this.runnable().enableTimeouts(enabled);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get test slowness threshold `ms`.
|
||||
*
|
||||
* @api private
|
||||
* @param {number} ms
|
||||
* @return {Context} self
|
||||
*/
|
||||
Context.prototype.slow = function(ms) {
|
||||
if (!arguments.length) {
|
||||
return this.runnable().slow();
|
||||
}
|
||||
this.runnable().slow(ms);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark a test as skipped.
|
||||
*
|
||||
* @api private
|
||||
* @throws Pending
|
||||
*/
|
||||
Context.prototype.skip = function() {
|
||||
this.runnable().skip();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get a number of allowed retries on failed tests
|
||||
*
|
||||
* @api private
|
||||
* @param {number} n
|
||||
* @return {Context} self
|
||||
*/
|
||||
Context.prototype.retries = function(n) {
|
||||
if (!arguments.length) {
|
||||
return this.runnable().retries();
|
||||
}
|
||||
this.runnable().retries(n);
|
||||
return this;
|
||||
};
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
var Runnable = require('./runnable');
|
||||
var inherits = require('./utils').inherits;
|
||||
|
||||
/**
|
||||
* Expose `Hook`.
|
||||
*/
|
||||
|
||||
module.exports = Hook;
|
||||
|
||||
/**
|
||||
* Initialize a new `Hook` with the given `title` and callback `fn`
|
||||
*
|
||||
* @class
|
||||
* @extends Runnable
|
||||
* @param {String} title
|
||||
* @param {Function} fn
|
||||
*/
|
||||
function Hook(title, fn) {
|
||||
Runnable.call(this, title, fn);
|
||||
this.type = 'hook';
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Runnable.prototype`.
|
||||
*/
|
||||
inherits(Hook, Runnable);
|
||||
|
||||
/**
|
||||
* Get or set the test `err`.
|
||||
*
|
||||
* @memberof Hook
|
||||
* @public
|
||||
* @param {Error} err
|
||||
* @return {Error}
|
||||
*/
|
||||
Hook.prototype.error = function(err) {
|
||||
if (!arguments.length) {
|
||||
err = this._error;
|
||||
this._error = null;
|
||||
return err;
|
||||
}
|
||||
|
||||
this._error = err;
|
||||
};
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
'use strict';
|
||||
|
||||
var Test = require('../test');
|
||||
|
||||
/**
|
||||
* BDD-style interface:
|
||||
*
|
||||
* describe('Array', function() {
|
||||
* describe('#indexOf()', function() {
|
||||
* it('should return -1 when not present', function() {
|
||||
* // ...
|
||||
* });
|
||||
*
|
||||
* it('should return the index when present', function() {
|
||||
* // ...
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function bddInterface(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on('pre-require', function(context, file, mocha) {
|
||||
var common = require('./common')(suites, context, mocha);
|
||||
|
||||
context.before = common.before;
|
||||
context.after = common.after;
|
||||
context.beforeEach = common.beforeEach;
|
||||
context.afterEach = common.afterEach;
|
||||
context.run = mocha.options.delay && common.runWithSuite(suite);
|
||||
/**
|
||||
* Describe a "suite" with the given `title`
|
||||
* and callback `fn` containing nested suites
|
||||
* and/or tests.
|
||||
*/
|
||||
|
||||
context.describe = context.context = function(title, fn) {
|
||||
return common.suite.create({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending describe.
|
||||
*/
|
||||
|
||||
context.xdescribe = context.xcontext = context.describe.skip = function(
|
||||
title,
|
||||
fn
|
||||
) {
|
||||
return common.suite.skip({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive suite.
|
||||
*/
|
||||
|
||||
context.describe.only = function(title, fn) {
|
||||
return common.suite.only({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe a specification or test-case
|
||||
* with the given `title` and callback `fn`
|
||||
* acting as a thunk.
|
||||
*/
|
||||
|
||||
context.it = context.specify = function(title, fn) {
|
||||
var suite = suites[0];
|
||||
if (suite.isPending()) {
|
||||
fn = null;
|
||||
}
|
||||
var test = new Test(title, fn);
|
||||
test.file = file;
|
||||
suite.addTest(test);
|
||||
return test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
|
||||
context.it.only = function(title, fn) {
|
||||
return common.test.only(mocha, context.it(title, fn));
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending test case.
|
||||
*/
|
||||
|
||||
context.xit = context.xspecify = context.it.skip = function(title) {
|
||||
return context.it(title);
|
||||
};
|
||||
|
||||
/**
|
||||
* Number of attempts to retry.
|
||||
*/
|
||||
context.it.retries = function(n) {
|
||||
context.retries(n);
|
||||
};
|
||||
});
|
||||
};
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
'use strict';
|
||||
|
||||
var Suite = require('../suite');
|
||||
|
||||
/**
|
||||
* Functions common to more than one interface.
|
||||
*
|
||||
* @param {Suite[]} suites
|
||||
* @param {Context} context
|
||||
* @param {Mocha} mocha
|
||||
* @return {Object} An object containing common functions.
|
||||
*/
|
||||
module.exports = function(suites, context, mocha) {
|
||||
return {
|
||||
/**
|
||||
* This is only present if flag --delay is passed into Mocha. It triggers
|
||||
* root suite execution.
|
||||
*
|
||||
* @param {Suite} suite The root suite.
|
||||
* @return {Function} A function which runs the root suite
|
||||
*/
|
||||
runWithSuite: function runWithSuite(suite) {
|
||||
return function run() {
|
||||
suite.run();
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute before running tests.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
before: function(name, fn) {
|
||||
suites[0].beforeAll(name, fn);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute after running tests.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
after: function(name, fn) {
|
||||
suites[0].afterAll(name, fn);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute before each test case.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
beforeEach: function(name, fn) {
|
||||
suites[0].beforeEach(name, fn);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute after each test case.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
afterEach: function(name, fn) {
|
||||
suites[0].afterEach(name, fn);
|
||||
},
|
||||
|
||||
suite: {
|
||||
/**
|
||||
* Create an exclusive Suite; convenience function
|
||||
* See docstring for create() below.
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @returns {Suite}
|
||||
*/
|
||||
only: function only(opts) {
|
||||
opts.isOnly = true;
|
||||
return this.create(opts);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a Suite, but skip it; convenience function
|
||||
* See docstring for create() below.
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @returns {Suite}
|
||||
*/
|
||||
skip: function skip(opts) {
|
||||
opts.pending = true;
|
||||
return this.create(opts);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a suite.
|
||||
* @param {Object} opts Options
|
||||
* @param {string} opts.title Title of Suite
|
||||
* @param {Function} [opts.fn] Suite Function (not always applicable)
|
||||
* @param {boolean} [opts.pending] Is Suite pending?
|
||||
* @param {string} [opts.file] Filepath where this Suite resides
|
||||
* @param {boolean} [opts.isOnly] Is Suite exclusive?
|
||||
* @returns {Suite}
|
||||
*/
|
||||
create: function create(opts) {
|
||||
var suite = Suite.create(suites[0], opts.title);
|
||||
suite.pending = Boolean(opts.pending);
|
||||
suite.file = opts.file;
|
||||
suites.unshift(suite);
|
||||
if (opts.isOnly) {
|
||||
suite.parent._onlySuites = suite.parent._onlySuites.concat(suite);
|
||||
}
|
||||
if (typeof opts.fn === 'function') {
|
||||
opts.fn.call(suite);
|
||||
suites.shift();
|
||||
} else if (typeof opts.fn === 'undefined' && !suite.pending) {
|
||||
throw new Error(
|
||||
'Suite "' +
|
||||
suite.fullTitle() +
|
||||
'" was defined but no callback was supplied. Supply a callback or explicitly skip the suite.'
|
||||
);
|
||||
} else if (!opts.fn && suite.pending) {
|
||||
suites.shift();
|
||||
}
|
||||
|
||||
return suite;
|
||||
}
|
||||
},
|
||||
|
||||
test: {
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*
|
||||
* @param {Object} mocha
|
||||
* @param {Function} test
|
||||
* @returns {*}
|
||||
*/
|
||||
only: function(mocha, test) {
|
||||
test.parent._onlyTests = test.parent._onlyTests.concat(test);
|
||||
return test;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pending test case.
|
||||
*
|
||||
* @param {string} title
|
||||
*/
|
||||
skip: function(title) {
|
||||
context.test(title);
|
||||
},
|
||||
|
||||
/**
|
||||
* Number of retry attempts
|
||||
*
|
||||
* @param {number} n
|
||||
*/
|
||||
retries: function(n) {
|
||||
context.retries(n);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
var Suite = require('../suite');
|
||||
var Test = require('../test');
|
||||
|
||||
/**
|
||||
* Exports-style (as Node.js module) interface:
|
||||
*
|
||||
* exports.Array = {
|
||||
* '#indexOf()': {
|
||||
* 'should return -1 when the value is not present': function() {
|
||||
*
|
||||
* },
|
||||
*
|
||||
* 'should return the correct index when the value is present': function() {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on('require', visit);
|
||||
|
||||
function visit(obj, file) {
|
||||
var suite;
|
||||
for (var key in obj) {
|
||||
if (typeof obj[key] === 'function') {
|
||||
var fn = obj[key];
|
||||
switch (key) {
|
||||
case 'before':
|
||||
suites[0].beforeAll(fn);
|
||||
break;
|
||||
case 'after':
|
||||
suites[0].afterAll(fn);
|
||||
break;
|
||||
case 'beforeEach':
|
||||
suites[0].beforeEach(fn);
|
||||
break;
|
||||
case 'afterEach':
|
||||
suites[0].afterEach(fn);
|
||||
break;
|
||||
default:
|
||||
var test = new Test(key, fn);
|
||||
test.file = file;
|
||||
suites[0].addTest(test);
|
||||
}
|
||||
} else {
|
||||
suite = Suite.create(suites[0], key);
|
||||
suites.unshift(suite);
|
||||
visit(obj[key], file);
|
||||
suites.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
exports.bdd = require('./bdd');
|
||||
exports.tdd = require('./tdd');
|
||||
exports.qunit = require('./qunit');
|
||||
exports.exports = require('./exports');
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
var Test = require('../test');
|
||||
|
||||
/**
|
||||
* QUnit-style interface:
|
||||
*
|
||||
* suite('Array');
|
||||
*
|
||||
* test('#length', function() {
|
||||
* var arr = [1,2,3];
|
||||
* ok(arr.length == 3);
|
||||
* });
|
||||
*
|
||||
* test('#indexOf()', function() {
|
||||
* var arr = [1,2,3];
|
||||
* ok(arr.indexOf(1) == 0);
|
||||
* ok(arr.indexOf(2) == 1);
|
||||
* ok(arr.indexOf(3) == 2);
|
||||
* });
|
||||
*
|
||||
* suite('String');
|
||||
*
|
||||
* test('#length', function() {
|
||||
* ok('foo'.length == 3);
|
||||
* });
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function qUnitInterface(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on('pre-require', function(context, file, mocha) {
|
||||
var common = require('./common')(suites, context, mocha);
|
||||
|
||||
context.before = common.before;
|
||||
context.after = common.after;
|
||||
context.beforeEach = common.beforeEach;
|
||||
context.afterEach = common.afterEach;
|
||||
context.run = mocha.options.delay && common.runWithSuite(suite);
|
||||
/**
|
||||
* Describe a "suite" with the given `title`.
|
||||
*/
|
||||
|
||||
context.suite = function(title) {
|
||||
if (suites.length > 1) {
|
||||
suites.shift();
|
||||
}
|
||||
return common.suite.create({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive Suite.
|
||||
*/
|
||||
|
||||
context.suite.only = function(title) {
|
||||
if (suites.length > 1) {
|
||||
suites.shift();
|
||||
}
|
||||
return common.suite.only({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe a specification or test-case
|
||||
* with the given `title` and callback `fn`
|
||||
* acting as a thunk.
|
||||
*/
|
||||
|
||||
context.test = function(title, fn) {
|
||||
var test = new Test(title, fn);
|
||||
test.file = file;
|
||||
suites[0].addTest(test);
|
||||
return test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
|
||||
context.test.only = function(title, fn) {
|
||||
return common.test.only(mocha, context.test(title, fn));
|
||||
};
|
||||
|
||||
context.test.skip = common.test.skip;
|
||||
context.test.retries = common.test.retries;
|
||||
});
|
||||
};
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
'use strict';
|
||||
|
||||
var Test = require('../test');
|
||||
|
||||
/**
|
||||
* TDD-style interface:
|
||||
*
|
||||
* suite('Array', function() {
|
||||
* suite('#indexOf()', function() {
|
||||
* suiteSetup(function() {
|
||||
*
|
||||
* });
|
||||
*
|
||||
* test('should return -1 when not present', function() {
|
||||
*
|
||||
* });
|
||||
*
|
||||
* test('should return the index when present', function() {
|
||||
*
|
||||
* });
|
||||
*
|
||||
* suiteTeardown(function() {
|
||||
*
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {Suite} suite Root suite.
|
||||
*/
|
||||
module.exports = function(suite) {
|
||||
var suites = [suite];
|
||||
|
||||
suite.on('pre-require', function(context, file, mocha) {
|
||||
var common = require('./common')(suites, context, mocha);
|
||||
|
||||
context.setup = common.beforeEach;
|
||||
context.teardown = common.afterEach;
|
||||
context.suiteSetup = common.before;
|
||||
context.suiteTeardown = common.after;
|
||||
context.run = mocha.options.delay && common.runWithSuite(suite);
|
||||
|
||||
/**
|
||||
* Describe a "suite" with the given `title` and callback `fn` containing
|
||||
* nested suites and/or tests.
|
||||
*/
|
||||
context.suite = function(title, fn) {
|
||||
return common.suite.create({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending suite.
|
||||
*/
|
||||
context.suite.skip = function(title, fn) {
|
||||
return common.suite.skip({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
context.suite.only = function(title, fn) {
|
||||
return common.suite.only({
|
||||
title: title,
|
||||
file: file,
|
||||
fn: fn
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe a specification or test-case with the given `title` and
|
||||
* callback `fn` acting as a thunk.
|
||||
*/
|
||||
context.test = function(title, fn) {
|
||||
var suite = suites[0];
|
||||
if (suite.isPending()) {
|
||||
fn = null;
|
||||
}
|
||||
var test = new Test(title, fn);
|
||||
test.file = file;
|
||||
suite.addTest(test);
|
||||
return test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
|
||||
context.test.only = function(title, fn) {
|
||||
return common.test.only(mocha, context.test(title, fn));
|
||||
};
|
||||
|
||||
context.test.skip = common.test.skip;
|
||||
context.test.retries = common.test.retries;
|
||||
});
|
||||
};
|
||||
+613
@@ -0,0 +1,613 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* mocha
|
||||
* Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
var escapeRe = require('escape-string-regexp');
|
||||
var path = require('path');
|
||||
var reporters = require('./reporters');
|
||||
var utils = require('./utils');
|
||||
|
||||
exports = module.exports = Mocha;
|
||||
|
||||
/**
|
||||
* To require local UIs and reporters when running in node.
|
||||
*/
|
||||
|
||||
if (!process.browser) {
|
||||
var cwd = process.cwd();
|
||||
module.paths.push(cwd, path.join(cwd, 'node_modules'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose internals.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @class utils
|
||||
* @memberof Mocha
|
||||
*/
|
||||
exports.utils = utils;
|
||||
exports.interfaces = require('./interfaces');
|
||||
/**
|
||||
*
|
||||
* @memberof Mocha
|
||||
* @public
|
||||
*/
|
||||
exports.reporters = reporters;
|
||||
exports.Runnable = require('./runnable');
|
||||
exports.Context = require('./context');
|
||||
/**
|
||||
*
|
||||
* @memberof Mocha
|
||||
*/
|
||||
exports.Runner = require('./runner');
|
||||
exports.Suite = require('./suite');
|
||||
exports.Hook = require('./hook');
|
||||
exports.Test = require('./test');
|
||||
|
||||
/**
|
||||
* Return image `name` path.
|
||||
*
|
||||
* @private
|
||||
* @param {string} name
|
||||
* @return {string}
|
||||
*/
|
||||
function image(name) {
|
||||
return path.join(__dirname, '..', 'assets', 'growl', name + '.png');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up mocha with `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `ui` name "bdd", "tdd", "exports" etc
|
||||
* - `reporter` reporter instance, defaults to `mocha.reporters.spec`
|
||||
* - `globals` array of accepted globals
|
||||
* - `timeout` timeout in milliseconds
|
||||
* - `retries` number of times to retry failed tests
|
||||
* - `bail` bail on the first test failure
|
||||
* - `slow` milliseconds to wait before considering a test slow
|
||||
* - `ignoreLeaks` ignore global leaks
|
||||
* - `fullTrace` display the full stack-trace on failing
|
||||
* - `grep` string or regexp to filter tests with
|
||||
*
|
||||
* @class Mocha
|
||||
* @param {Object} options
|
||||
*/
|
||||
function Mocha(options) {
|
||||
options = options || {};
|
||||
this.files = [];
|
||||
this.options = options;
|
||||
if (options.grep) {
|
||||
this.grep(new RegExp(options.grep));
|
||||
}
|
||||
if (options.fgrep) {
|
||||
this.fgrep(options.fgrep);
|
||||
}
|
||||
this.suite = new exports.Suite('', new exports.Context());
|
||||
this.ui(options.ui);
|
||||
this.bail(options.bail);
|
||||
this.reporter(options.reporter, options.reporterOptions);
|
||||
if (typeof options.timeout !== 'undefined' && options.timeout !== null) {
|
||||
this.timeout(options.timeout);
|
||||
}
|
||||
if (typeof options.retries !== 'undefined' && options.retries !== null) {
|
||||
this.retries(options.retries);
|
||||
}
|
||||
this.useColors(options.useColors);
|
||||
if (options.enableTimeouts !== null) {
|
||||
this.enableTimeouts(options.enableTimeouts);
|
||||
}
|
||||
if (options.slow) {
|
||||
this.slow(options.slow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable bailing on the first failure.
|
||||
*
|
||||
* @public
|
||||
* @api public
|
||||
* @param {boolean} [bail]
|
||||
*/
|
||||
Mocha.prototype.bail = function(bail) {
|
||||
if (!arguments.length) {
|
||||
bail = true;
|
||||
}
|
||||
this.suite.bail(bail);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add test `file`.
|
||||
*
|
||||
* @public
|
||||
* @api public
|
||||
* @param {string} file
|
||||
*/
|
||||
Mocha.prototype.addFile = function(file) {
|
||||
this.files.push(file);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set reporter to `reporter`, defaults to "spec".
|
||||
*
|
||||
* @public
|
||||
* @param {String|Function} reporter name or constructor
|
||||
* @param {Object} reporterOptions optional options
|
||||
* @api public
|
||||
* @param {string|Function} reporter name or constructor
|
||||
* @param {Object} reporterOptions optional options
|
||||
*/
|
||||
Mocha.prototype.reporter = function(reporter, reporterOptions) {
|
||||
if (typeof reporter === 'function') {
|
||||
this._reporter = reporter;
|
||||
} else {
|
||||
reporter = reporter || 'spec';
|
||||
var _reporter;
|
||||
// Try to load a built-in reporter.
|
||||
if (reporters[reporter]) {
|
||||
_reporter = reporters[reporter];
|
||||
}
|
||||
// Try to load reporters from process.cwd() and node_modules
|
||||
if (!_reporter) {
|
||||
try {
|
||||
_reporter = require(reporter);
|
||||
} catch (err) {
|
||||
if (err.message.indexOf('Cannot find module') !== -1) {
|
||||
// Try to load reporters from a path (absolute or relative)
|
||||
try {
|
||||
_reporter = require(path.resolve(process.cwd(), reporter));
|
||||
} catch (_err) {
|
||||
err.message.indexOf('Cannot find module') !== -1
|
||||
? console.warn('"' + reporter + '" reporter not found')
|
||||
: console.warn(
|
||||
'"' +
|
||||
reporter +
|
||||
'" reporter blew up with error:\n' +
|
||||
err.stack
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
'"' + reporter + '" reporter blew up with error:\n' + err.stack
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_reporter && reporter === 'teamcity') {
|
||||
console.warn(
|
||||
'The Teamcity reporter was moved to a package named ' +
|
||||
'mocha-teamcity-reporter ' +
|
||||
'(https://npmjs.org/package/mocha-teamcity-reporter).'
|
||||
);
|
||||
}
|
||||
if (!_reporter) {
|
||||
throw new Error('invalid reporter "' + reporter + '"');
|
||||
}
|
||||
this._reporter = _reporter;
|
||||
}
|
||||
this.options.reporterOptions = reporterOptions;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set test UI `name`, defaults to "bdd".
|
||||
* @public
|
||||
* @api public
|
||||
* @param {string} bdd
|
||||
*/
|
||||
Mocha.prototype.ui = function(name) {
|
||||
name = name || 'bdd';
|
||||
this._ui = exports.interfaces[name];
|
||||
if (!this._ui) {
|
||||
try {
|
||||
this._ui = require(name);
|
||||
} catch (err) {
|
||||
throw new Error('invalid interface "' + name + '"');
|
||||
}
|
||||
}
|
||||
this._ui = this._ui(this.suite);
|
||||
|
||||
this.suite.on('pre-require', function(context) {
|
||||
exports.afterEach = context.afterEach || context.teardown;
|
||||
exports.after = context.after || context.suiteTeardown;
|
||||
exports.beforeEach = context.beforeEach || context.setup;
|
||||
exports.before = context.before || context.suiteSetup;
|
||||
exports.describe = context.describe || context.suite;
|
||||
exports.it = context.it || context.test;
|
||||
exports.xit = context.xit || context.test.skip;
|
||||
exports.setup = context.setup || context.beforeEach;
|
||||
exports.suiteSetup = context.suiteSetup || context.before;
|
||||
exports.suiteTeardown = context.suiteTeardown || context.after;
|
||||
exports.suite = context.suite || context.describe;
|
||||
exports.teardown = context.teardown || context.afterEach;
|
||||
exports.test = context.test || context.it;
|
||||
exports.run = context.run;
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load registered files.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Mocha.prototype.loadFiles = function(fn) {
|
||||
var self = this;
|
||||
var suite = this.suite;
|
||||
this.files.forEach(function(file) {
|
||||
file = path.resolve(file);
|
||||
suite.emit('pre-require', global, file, self);
|
||||
suite.emit('require', require(file), file, self);
|
||||
suite.emit('post-require', global, file, self);
|
||||
});
|
||||
fn && fn();
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable growl support.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Mocha.prototype._growl = function(runner, reporter) {
|
||||
var notify = require('growl');
|
||||
|
||||
runner.on('end', function() {
|
||||
var stats = reporter.stats;
|
||||
if (stats.failures) {
|
||||
var msg = stats.failures + ' of ' + runner.total + ' tests failed';
|
||||
notify(msg, {name: 'mocha', title: 'Failed', image: image('error')});
|
||||
} else {
|
||||
notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
|
||||
name: 'mocha',
|
||||
title: 'Passed',
|
||||
image: image('ok')
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Escape string and add it to grep as a regexp.
|
||||
*
|
||||
* @public
|
||||
* @api public
|
||||
* @param str
|
||||
* @returns {Mocha}
|
||||
*/
|
||||
Mocha.prototype.fgrep = function(str) {
|
||||
return this.grep(new RegExp(escapeRe(str)));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add regexp to grep, if `re` is a string it is escaped.
|
||||
*
|
||||
* @public
|
||||
* @param {RegExp|String} re
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @param {RegExp|string} re
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.grep = function(re) {
|
||||
if (utils.isString(re)) {
|
||||
// extract args if it's regex-like, i.e: [string, pattern, flag]
|
||||
var arg = re.match(/^\/(.*)\/(g|i|)$|.*/);
|
||||
this.options.grep = new RegExp(arg[1] || arg[0], arg[2]);
|
||||
} else {
|
||||
this.options.grep = re;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* Invert `.grep()` matches.
|
||||
*
|
||||
* @public
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
*/
|
||||
Mocha.prototype.invert = function() {
|
||||
this.options.invert = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ignore global leaks.
|
||||
*
|
||||
* @public
|
||||
* @param {Boolean} ignore
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @param {boolean} ignore
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.ignoreLeaks = function(ignore) {
|
||||
this.options.ignoreLeaks = Boolean(ignore);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable global leak checking.
|
||||
*
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
Mocha.prototype.checkLeaks = function() {
|
||||
this.options.ignoreLeaks = false;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Display long stack-trace on failing
|
||||
*
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
Mocha.prototype.fullTrace = function() {
|
||||
this.options.fullStackTrace = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable growl support.
|
||||
*
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
Mocha.prototype.growl = function() {
|
||||
this.options.growl = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ignore `globals` array or string.
|
||||
*
|
||||
* @param {Array|String} globals
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
* @param {Array|string} globals
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.globals = function(globals) {
|
||||
this.options.globals = (this.options.globals || []).concat(globals);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit color output.
|
||||
*
|
||||
* @param {Boolean} colors
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
* @param {boolean} colors
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.useColors = function(colors) {
|
||||
if (colors !== undefined) {
|
||||
this.options.useColors = colors;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use inline diffs rather than +/-.
|
||||
*
|
||||
* @param {Boolean} inlineDiffs
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
* @param {boolean} inlineDiffs
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.useInlineDiffs = function(inlineDiffs) {
|
||||
this.options.useInlineDiffs = inlineDiffs !== undefined && inlineDiffs;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Do not show diffs at all.
|
||||
*
|
||||
* @param {Boolean} hideDiff
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
* @param {boolean} hideDiff
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.hideDiff = function(hideDiff) {
|
||||
this.options.hideDiff = hideDiff !== undefined && hideDiff;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the timeout in milliseconds.
|
||||
*
|
||||
* @param {Number} timeout
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
* @param {number} timeout
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.timeout = function(timeout) {
|
||||
this.suite.timeout(timeout);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the number of times to retry failed tests.
|
||||
*
|
||||
* @param {Number} retry times
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
Mocha.prototype.retries = function(n) {
|
||||
this.suite.retries(n);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set slowness threshold in milliseconds.
|
||||
*
|
||||
* @param {Number} slow
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
* @param {number} slow
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.slow = function(slow) {
|
||||
this.suite.slow(slow);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable timeouts.
|
||||
*
|
||||
* @param {Boolean} enabled
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
* @param {boolean} enabled
|
||||
* @return {Mocha}
|
||||
*/
|
||||
Mocha.prototype.enableTimeouts = function(enabled) {
|
||||
this.suite.enableTimeouts(
|
||||
arguments.length && enabled !== undefined ? enabled : true
|
||||
);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes all tests async (accepting a callback)
|
||||
*
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
Mocha.prototype.asyncOnly = function() {
|
||||
this.options.asyncOnly = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable syntax highlighting (in browser).
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
Mocha.prototype.noHighlighting = function() {
|
||||
this.options.noHighlighting = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable uncaught errors to propagate (in browser).
|
||||
*
|
||||
* @return {Mocha}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
Mocha.prototype.allowUncaught = function() {
|
||||
this.options.allowUncaught = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delay root suite execution.
|
||||
* @returns {Mocha}
|
||||
*/
|
||||
Mocha.prototype.delay = function delay() {
|
||||
this.options.delay = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests marked only fail the suite
|
||||
* @returns {Mocha}
|
||||
*/
|
||||
Mocha.prototype.forbidOnly = function() {
|
||||
this.options.forbidOnly = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending tests and tests marked skip fail the suite
|
||||
* @returns {Mocha}
|
||||
*/
|
||||
Mocha.prototype.forbidPending = function() {
|
||||
this.options.forbidPending = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run tests and invoke `fn()` when complete.
|
||||
*
|
||||
* Note that `loadFiles` relies on Node's `require` to execute
|
||||
* the test interface functions and will be subject to the
|
||||
* cache - if the files are already in the `require` cache,
|
||||
* they will effectively be skipped. Therefore, to run tests
|
||||
* multiple times or to run tests in files that are already
|
||||
* in the `require` cache, make sure to clear them from the
|
||||
* cache first in whichever manner best suits your needs.
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
* @param {Function} fn
|
||||
* @return {Runner}
|
||||
*/
|
||||
Mocha.prototype.run = function(fn) {
|
||||
if (this.files.length) {
|
||||
this.loadFiles();
|
||||
}
|
||||
var suite = this.suite;
|
||||
var options = this.options;
|
||||
options.files = this.files;
|
||||
var runner = new exports.Runner(suite, options.delay);
|
||||
var reporter = new this._reporter(runner, options);
|
||||
runner.ignoreLeaks = options.ignoreLeaks !== false;
|
||||
runner.fullStackTrace = options.fullStackTrace;
|
||||
runner.asyncOnly = options.asyncOnly;
|
||||
runner.allowUncaught = options.allowUncaught;
|
||||
runner.forbidOnly = options.forbidOnly;
|
||||
runner.forbidPending = options.forbidPending;
|
||||
if (options.grep) {
|
||||
runner.grep(options.grep, options.invert);
|
||||
}
|
||||
if (options.globals) {
|
||||
runner.globals(options.globals);
|
||||
}
|
||||
if (options.growl) {
|
||||
this._growl(runner, reporter);
|
||||
}
|
||||
if (options.useColors !== undefined) {
|
||||
exports.reporters.Base.useColors = options.useColors;
|
||||
}
|
||||
exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
|
||||
exports.reporters.Base.hideDiff = options.hideDiff;
|
||||
|
||||
function done(failures) {
|
||||
if (reporter.done) {
|
||||
reporter.done(failures, fn);
|
||||
} else {
|
||||
fn && fn(failures);
|
||||
}
|
||||
}
|
||||
|
||||
return runner.run(done);
|
||||
};
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module milliseconds
|
||||
*/
|
||||
/**
|
||||
* Helpers.
|
||||
*/
|
||||
|
||||
var s = 1000;
|
||||
var m = s * 60;
|
||||
var h = m * 60;
|
||||
var d = h * 24;
|
||||
var y = d * 365.25;
|
||||
|
||||
/**
|
||||
* Parse or format the given `val`.
|
||||
*
|
||||
* @memberof Mocha
|
||||
* @public
|
||||
* @api public
|
||||
* @param {string|number} val
|
||||
* @return {string|number}
|
||||
*/
|
||||
module.exports = function(val) {
|
||||
if (typeof val === 'string') {
|
||||
return parse(val);
|
||||
}
|
||||
return format(val);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the given `str` and return milliseconds.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} str
|
||||
* @return {number}
|
||||
*/
|
||||
function parse(str) {
|
||||
var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(
|
||||
str
|
||||
);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
var n = parseFloat(match[1]);
|
||||
var type = (match[2] || 'ms').toLowerCase();
|
||||
switch (type) {
|
||||
case 'years':
|
||||
case 'year':
|
||||
case 'y':
|
||||
return n * y;
|
||||
case 'days':
|
||||
case 'day':
|
||||
case 'd':
|
||||
return n * d;
|
||||
case 'hours':
|
||||
case 'hour':
|
||||
case 'h':
|
||||
return n * h;
|
||||
case 'minutes':
|
||||
case 'minute':
|
||||
case 'm':
|
||||
return n * m;
|
||||
case 'seconds':
|
||||
case 'second':
|
||||
case 's':
|
||||
return n * s;
|
||||
case 'ms':
|
||||
return n;
|
||||
default:
|
||||
// No default case
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format for `ms`.
|
||||
*
|
||||
* @api private
|
||||
* @param {number} ms
|
||||
* @return {string}
|
||||
*/
|
||||
function format(ms) {
|
||||
if (ms >= d) {
|
||||
return Math.round(ms / d) + 'd';
|
||||
}
|
||||
if (ms >= h) {
|
||||
return Math.round(ms / h) + 'h';
|
||||
}
|
||||
if (ms >= m) {
|
||||
return Math.round(ms / m) + 'm';
|
||||
}
|
||||
if (ms >= s) {
|
||||
return Math.round(ms / s) + 's';
|
||||
}
|
||||
return ms + 'ms';
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = Pending;
|
||||
|
||||
/**
|
||||
* Initialize a new `Pending` error with the given message.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function Pending(message) {
|
||||
this.message = message;
|
||||
}
|
||||
+540
@@ -0,0 +1,540 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Base
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var tty = require('tty');
|
||||
var diff = require('diff');
|
||||
var ms = require('../ms');
|
||||
var utils = require('../utils');
|
||||
var supportsColor = process.browser ? null : require('supports-color');
|
||||
|
||||
/**
|
||||
* Expose `Base`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Base;
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering.
|
||||
* See: https://github.com/mochajs/mocha/issues/237
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars, no-native-reassign */
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var setInterval = global.setInterval;
|
||||
var clearTimeout = global.clearTimeout;
|
||||
var clearInterval = global.clearInterval;
|
||||
/* eslint-enable no-unused-vars, no-native-reassign */
|
||||
|
||||
/**
|
||||
* Check if both stdio streams are associated with a tty.
|
||||
*/
|
||||
|
||||
var isatty = tty.isatty(1) && tty.isatty(2);
|
||||
|
||||
/**
|
||||
* Enable coloring by default, except in the browser interface.
|
||||
*/
|
||||
|
||||
exports.useColors =
|
||||
!process.browser &&
|
||||
(supportsColor.stdout || process.env.MOCHA_COLORS !== undefined);
|
||||
|
||||
/**
|
||||
* Inline diffs instead of +/-
|
||||
*/
|
||||
|
||||
exports.inlineDiffs = false;
|
||||
|
||||
/**
|
||||
* Default color map.
|
||||
*/
|
||||
|
||||
exports.colors = {
|
||||
pass: 90,
|
||||
fail: 31,
|
||||
'bright pass': 92,
|
||||
'bright fail': 91,
|
||||
'bright yellow': 93,
|
||||
pending: 36,
|
||||
suite: 0,
|
||||
'error title': 0,
|
||||
'error message': 31,
|
||||
'error stack': 90,
|
||||
checkmark: 32,
|
||||
fast: 90,
|
||||
medium: 33,
|
||||
slow: 31,
|
||||
green: 32,
|
||||
light: 90,
|
||||
'diff gutter': 90,
|
||||
'diff added': 32,
|
||||
'diff removed': 31
|
||||
};
|
||||
|
||||
/**
|
||||
* Default symbol map.
|
||||
*/
|
||||
|
||||
exports.symbols = {
|
||||
ok: '✓',
|
||||
err: '✖',
|
||||
dot: '․',
|
||||
comma: ',',
|
||||
bang: '!'
|
||||
};
|
||||
|
||||
// With node.js on Windows: use symbols available in terminal default fonts
|
||||
if (process.platform === 'win32') {
|
||||
exports.symbols.ok = '\u221A';
|
||||
exports.symbols.err = '\u00D7';
|
||||
exports.symbols.dot = '.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Color `str` with the given `type`,
|
||||
* allowing colors to be disabled,
|
||||
* as well as user-defined color
|
||||
* schemes.
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
var color = (exports.color = function(type, str) {
|
||||
if (!exports.useColors) {
|
||||
return String(str);
|
||||
}
|
||||
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
|
||||
});
|
||||
|
||||
/**
|
||||
* Expose term window size, with some defaults for when stderr is not a tty.
|
||||
*/
|
||||
|
||||
exports.window = {
|
||||
width: 75
|
||||
};
|
||||
|
||||
if (isatty) {
|
||||
exports.window.width = process.stdout.getWindowSize
|
||||
? process.stdout.getWindowSize(1)[0]
|
||||
: tty.getWindowSize()[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some basic cursor interactions that are common among reporters.
|
||||
*/
|
||||
|
||||
exports.cursor = {
|
||||
hide: function() {
|
||||
isatty && process.stdout.write('\u001b[?25l');
|
||||
},
|
||||
|
||||
show: function() {
|
||||
isatty && process.stdout.write('\u001b[?25h');
|
||||
},
|
||||
|
||||
deleteLine: function() {
|
||||
isatty && process.stdout.write('\u001b[2K');
|
||||
},
|
||||
|
||||
beginningOfLine: function() {
|
||||
isatty && process.stdout.write('\u001b[0G');
|
||||
},
|
||||
|
||||
CR: function() {
|
||||
if (isatty) {
|
||||
exports.cursor.deleteLine();
|
||||
exports.cursor.beginningOfLine();
|
||||
} else {
|
||||
process.stdout.write('\r');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function showDiff(err) {
|
||||
return (
|
||||
err &&
|
||||
err.showDiff !== false &&
|
||||
sameType(err.actual, err.expected) &&
|
||||
err.expected !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
function stringifyDiffObjs(err) {
|
||||
if (!utils.isString(err.actual) || !utils.isString(err.expected)) {
|
||||
err.actual = utils.stringify(err.actual);
|
||||
err.expected = utils.stringify(err.expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a diff between 2 strings with coloured ANSI output.
|
||||
*
|
||||
* The diff will be either inline or unified dependant on the value
|
||||
* of `Base.inlineDiff`.
|
||||
*
|
||||
* @param {string} actual
|
||||
* @param {string} expected
|
||||
* @return {string} Diff
|
||||
*/
|
||||
var generateDiff = (exports.generateDiff = function(actual, expected) {
|
||||
return exports.inlineDiffs
|
||||
? inlineDiff(actual, expected)
|
||||
: unifiedDiff(actual, expected);
|
||||
});
|
||||
|
||||
/**
|
||||
* Output the given `failures` as a list.
|
||||
*
|
||||
* @public
|
||||
* @memberof Mocha.reporters.Base
|
||||
* @variation 1
|
||||
* @param {Array} failures
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.list = function(failures) {
|
||||
console.log();
|
||||
failures.forEach(function(test, i) {
|
||||
// format
|
||||
var fmt =
|
||||
color('error title', ' %s) %s:\n') +
|
||||
color('error message', ' %s') +
|
||||
color('error stack', '\n%s\n');
|
||||
|
||||
// msg
|
||||
var msg;
|
||||
var err = test.err;
|
||||
var message;
|
||||
if (err.message && typeof err.message.toString === 'function') {
|
||||
message = err.message + '';
|
||||
} else if (typeof err.inspect === 'function') {
|
||||
message = err.inspect() + '';
|
||||
} else {
|
||||
message = '';
|
||||
}
|
||||
var stack = err.stack || message;
|
||||
var index = message ? stack.indexOf(message) : -1;
|
||||
|
||||
if (index === -1) {
|
||||
msg = message;
|
||||
} else {
|
||||
index += message.length;
|
||||
msg = stack.slice(0, index);
|
||||
// remove msg from stack
|
||||
stack = stack.slice(index + 1);
|
||||
}
|
||||
|
||||
// uncaught
|
||||
if (err.uncaught) {
|
||||
msg = 'Uncaught ' + msg;
|
||||
}
|
||||
// explicitly show diff
|
||||
if (!exports.hideDiff && showDiff(err)) {
|
||||
stringifyDiffObjs(err);
|
||||
fmt =
|
||||
color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
|
||||
var match = message.match(/^([^:]+): expected/);
|
||||
msg = '\n ' + color('error message', match ? match[1] : msg);
|
||||
|
||||
msg += generateDiff(err.actual, err.expected);
|
||||
}
|
||||
|
||||
// indent stack trace
|
||||
stack = stack.replace(/^/gm, ' ');
|
||||
|
||||
// indented test title
|
||||
var testTitle = '';
|
||||
test.titlePath().forEach(function(str, index) {
|
||||
if (index !== 0) {
|
||||
testTitle += '\n ';
|
||||
}
|
||||
for (var i = 0; i < index; i++) {
|
||||
testTitle += ' ';
|
||||
}
|
||||
testTitle += str;
|
||||
});
|
||||
|
||||
console.log(fmt, i + 1, testTitle, msg, stack);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a new `Base` reporter.
|
||||
*
|
||||
* All other reporters generally
|
||||
* inherit from this reporter, providing
|
||||
* stats such as test duration, number
|
||||
* of tests passed / failed etc.
|
||||
*
|
||||
* @memberof Mocha.reporters
|
||||
* @public
|
||||
* @class
|
||||
* @param {Runner} runner
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function Base(runner) {
|
||||
var stats = (this.stats = {
|
||||
suites: 0,
|
||||
tests: 0,
|
||||
passes: 0,
|
||||
pending: 0,
|
||||
failures: 0
|
||||
});
|
||||
var failures = (this.failures = []);
|
||||
|
||||
if (!runner) {
|
||||
return;
|
||||
}
|
||||
this.runner = runner;
|
||||
|
||||
runner.stats = stats;
|
||||
|
||||
runner.on('start', function() {
|
||||
stats.start = new Date();
|
||||
});
|
||||
|
||||
runner.on('suite', function(suite) {
|
||||
stats.suites = stats.suites || 0;
|
||||
suite.root || stats.suites++;
|
||||
});
|
||||
|
||||
runner.on('test end', function() {
|
||||
stats.tests = stats.tests || 0;
|
||||
stats.tests++;
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
stats.passes = stats.passes || 0;
|
||||
|
||||
if (test.duration > test.slow()) {
|
||||
test.speed = 'slow';
|
||||
} else if (test.duration > test.slow() / 2) {
|
||||
test.speed = 'medium';
|
||||
} else {
|
||||
test.speed = 'fast';
|
||||
}
|
||||
|
||||
stats.passes++;
|
||||
});
|
||||
|
||||
runner.on('fail', function(test, err) {
|
||||
stats.failures = stats.failures || 0;
|
||||
stats.failures++;
|
||||
if (showDiff(err)) {
|
||||
stringifyDiffObjs(err);
|
||||
}
|
||||
test.err = err;
|
||||
failures.push(test);
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
stats.end = new Date();
|
||||
stats.duration = stats.end - stats.start;
|
||||
});
|
||||
|
||||
runner.on('pending', function() {
|
||||
stats.pending++;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Output common epilogue used by many of
|
||||
* the bundled reporters.
|
||||
*
|
||||
* @memberof Mocha.reporters.Base
|
||||
* @public
|
||||
* @api public
|
||||
*/
|
||||
Base.prototype.epilogue = function() {
|
||||
var stats = this.stats;
|
||||
var fmt;
|
||||
|
||||
console.log();
|
||||
|
||||
// passes
|
||||
fmt =
|
||||
color('bright pass', ' ') +
|
||||
color('green', ' %d passing') +
|
||||
color('light', ' (%s)');
|
||||
|
||||
console.log(fmt, stats.passes || 0, ms(stats.duration));
|
||||
|
||||
// pending
|
||||
if (stats.pending) {
|
||||
fmt = color('pending', ' ') + color('pending', ' %d pending');
|
||||
|
||||
console.log(fmt, stats.pending);
|
||||
}
|
||||
|
||||
// failures
|
||||
if (stats.failures) {
|
||||
fmt = color('fail', ' %d failing');
|
||||
|
||||
console.log(fmt, stats.failures);
|
||||
|
||||
Base.list(this.failures);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log();
|
||||
};
|
||||
|
||||
/**
|
||||
* Pad the given `str` to `len`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} str
|
||||
* @param {string} len
|
||||
* @return {string}
|
||||
*/
|
||||
function pad(str, len) {
|
||||
str = String(str);
|
||||
return Array(len - str.length + 1).join(' ') + str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an inline diff between 2 strings with coloured ANSI output.
|
||||
*
|
||||
* @api private
|
||||
* @param {String} actual
|
||||
* @param {String} expected
|
||||
* @return {string} Diff
|
||||
*/
|
||||
function inlineDiff(actual, expected) {
|
||||
var msg = errorDiff(actual, expected);
|
||||
|
||||
// linenos
|
||||
var lines = msg.split('\n');
|
||||
if (lines.length > 4) {
|
||||
var width = String(lines.length).length;
|
||||
msg = lines
|
||||
.map(function(str, i) {
|
||||
return pad(++i, width) + ' |' + ' ' + str;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
// legend
|
||||
msg =
|
||||
'\n' +
|
||||
color('diff removed', 'actual') +
|
||||
' ' +
|
||||
color('diff added', 'expected') +
|
||||
'\n\n' +
|
||||
msg +
|
||||
'\n';
|
||||
|
||||
// indent
|
||||
msg = msg.replace(/^/gm, ' ');
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unified diff between two strings with coloured ANSI output.
|
||||
*
|
||||
* @api private
|
||||
* @param {String} actual
|
||||
* @param {String} expected
|
||||
* @return {string} The diff.
|
||||
*/
|
||||
function unifiedDiff(actual, expected) {
|
||||
var indent = ' ';
|
||||
function cleanUp(line) {
|
||||
if (line[0] === '+') {
|
||||
return indent + colorLines('diff added', line);
|
||||
}
|
||||
if (line[0] === '-') {
|
||||
return indent + colorLines('diff removed', line);
|
||||
}
|
||||
if (line.match(/@@/)) {
|
||||
return '--';
|
||||
}
|
||||
if (line.match(/\\ No newline/)) {
|
||||
return null;
|
||||
}
|
||||
return indent + line;
|
||||
}
|
||||
function notBlank(line) {
|
||||
return typeof line !== 'undefined' && line !== null;
|
||||
}
|
||||
var msg = diff.createPatch('string', actual, expected);
|
||||
var lines = msg.split('\n').splice(5);
|
||||
return (
|
||||
'\n ' +
|
||||
colorLines('diff added', '+ expected') +
|
||||
' ' +
|
||||
colorLines('diff removed', '- actual') +
|
||||
'\n\n' +
|
||||
lines
|
||||
.map(cleanUp)
|
||||
.filter(notBlank)
|
||||
.join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a character diff for `err`.
|
||||
*
|
||||
* @api private
|
||||
* @param {String} actual
|
||||
* @param {String} expected
|
||||
* @return {string} the diff
|
||||
*/
|
||||
function errorDiff(actual, expected) {
|
||||
return diff
|
||||
.diffWordsWithSpace(actual, expected)
|
||||
.map(function(str) {
|
||||
if (str.added) {
|
||||
return colorLines('diff added', str.value);
|
||||
}
|
||||
if (str.removed) {
|
||||
return colorLines('diff removed', str.value);
|
||||
}
|
||||
return str.value;
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Color lines for `str`, using the color `name`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} name
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
*/
|
||||
function colorLines(name, str) {
|
||||
return str
|
||||
.split('\n')
|
||||
.map(function(str) {
|
||||
return color(name, str);
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Object#toString reference.
|
||||
*/
|
||||
var objToString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Check that a / b have the same type.
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {boolean}
|
||||
*/
|
||||
function sameType(a, b) {
|
||||
return objToString.call(a) === objToString.call(b);
|
||||
}
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var tty = require('tty');
|
||||
var diff = require('diff');
|
||||
var ms = require('../ms');
|
||||
var utils = require('../utils');
|
||||
var supportsColor = process.browser ? null : require('supports-color');
|
||||
|
||||
/**
|
||||
* Expose `Base`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Base;
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering.
|
||||
* See: https://github.com/mochajs/mocha/issues/237
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars, no-native-reassign */
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var setInterval = global.setInterval;
|
||||
var clearTimeout = global.clearTimeout;
|
||||
var clearInterval = global.clearInterval;
|
||||
/* eslint-enable no-unused-vars, no-native-reassign */
|
||||
|
||||
/**
|
||||
* Check if both stdio streams are associated with a tty.
|
||||
*/
|
||||
|
||||
var isatty = tty.isatty(1) && tty.isatty(2);
|
||||
|
||||
/**
|
||||
* Enable coloring by default, except in the browser interface.
|
||||
*/
|
||||
|
||||
exports.useColors = !process.browser && (supportsColor || (process.env.MOCHA_COLORS !== undefined));
|
||||
|
||||
/**
|
||||
* Inline diffs instead of +/-
|
||||
*/
|
||||
|
||||
exports.inlineDiffs = false;
|
||||
|
||||
/**
|
||||
* Default color map.
|
||||
*/
|
||||
|
||||
exports.colors = {
|
||||
pass: 90,
|
||||
fail: 31,
|
||||
'bright pass': 92,
|
||||
'bright fail': 91,
|
||||
'bright yellow': 93,
|
||||
pending: 36,
|
||||
suite: 0,
|
||||
'error title': 0,
|
||||
'error message': 31,
|
||||
'error stack': 90,
|
||||
checkmark: 32,
|
||||
fast: 90,
|
||||
medium: 33,
|
||||
slow: 31,
|
||||
green: 32,
|
||||
light: 90,
|
||||
'diff gutter': 90,
|
||||
'diff added': 32,
|
||||
'diff removed': 31
|
||||
};
|
||||
|
||||
/**
|
||||
* Default symbol map.
|
||||
*/
|
||||
|
||||
exports.symbols = {
|
||||
ok: '✓',
|
||||
err: '✖',
|
||||
dot: '․',
|
||||
comma: ',',
|
||||
bang: '!'
|
||||
};
|
||||
|
||||
// With node.js on Windows: use symbols available in terminal default fonts
|
||||
if (process.platform === 'win32') {
|
||||
exports.symbols.ok = '\u221A';
|
||||
exports.symbols.err = '\u00D7';
|
||||
exports.symbols.dot = '.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Color `str` with the given `type`,
|
||||
* allowing colors to be disabled,
|
||||
* as well as user-defined color
|
||||
* schemes.
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
var color = exports.color = function (type, str) {
|
||||
if (!exports.useColors) {
|
||||
return String(str);
|
||||
}
|
||||
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose term window size, with some defaults for when stderr is not a tty.
|
||||
*/
|
||||
|
||||
exports.window = {
|
||||
width: 75
|
||||
};
|
||||
|
||||
if (isatty) {
|
||||
exports.window.width = process.stdout.getWindowSize
|
||||
? process.stdout.getWindowSize(1)[0]
|
||||
: tty.getWindowSize()[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some basic cursor interactions that are common among reporters.
|
||||
*/
|
||||
|
||||
exports.cursor = {
|
||||
hide: function () {
|
||||
isatty && process.stdout.write('\u001b[?25l');
|
||||
},
|
||||
|
||||
show: function () {
|
||||
isatty && process.stdout.write('\u001b[?25h');
|
||||
},
|
||||
|
||||
deleteLine: function () {
|
||||
isatty && process.stdout.write('\u001b[2K');
|
||||
},
|
||||
|
||||
beginningOfLine: function () {
|
||||
isatty && process.stdout.write('\u001b[0G');
|
||||
},
|
||||
|
||||
CR: function () {
|
||||
if (isatty) {
|
||||
exports.cursor.deleteLine();
|
||||
exports.cursor.beginningOfLine();
|
||||
} else {
|
||||
process.stdout.write('\r');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function showDiff (err) {
|
||||
return err && err.showDiff !== false && sameType(err.actual, err.expected) && err.expected !== undefined;
|
||||
}
|
||||
|
||||
function stringifyDiffObjs (err) {
|
||||
if (!utils.isString(err.actual) || !utils.isString(err.expected)) {
|
||||
err.actual = utils.stringify(err.actual);
|
||||
err.expected = utils.stringify(err.expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the given `failures` as a list.
|
||||
*
|
||||
* @param {Array} failures
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.list = function (failures) {
|
||||
console.log();
|
||||
failures.forEach(function (test, i) {
|
||||
// format
|
||||
var fmt = color('error title', ' %s) %s:\n') +
|
||||
color('error message', ' %s') +
|
||||
color('error stack', '\n%s\n');
|
||||
|
||||
// msg
|
||||
var msg;
|
||||
var err = test.err;
|
||||
var message;
|
||||
if (err.message && typeof err.message.toString === 'function') {
|
||||
message = err.message + '';
|
||||
} else if (typeof err.inspect === 'function') {
|
||||
message = err.inspect() + '';
|
||||
} else {
|
||||
message = '';
|
||||
}
|
||||
var stack = err.stack || message;
|
||||
var index = message ? stack.indexOf(message) : -1;
|
||||
|
||||
if (index === -1) {
|
||||
msg = message;
|
||||
} else {
|
||||
index += message.length;
|
||||
msg = stack.slice(0, index);
|
||||
// remove msg from stack
|
||||
stack = stack.slice(index + 1);
|
||||
}
|
||||
|
||||
// uncaught
|
||||
if (err.uncaught) {
|
||||
msg = 'Uncaught ' + msg;
|
||||
}
|
||||
|
||||
// explicitly show diff
|
||||
<<<<<<< HEAD
|
||||
if (showDiff(err)) {
|
||||
stringifyDiffObjs(err);
|
||||
=======
|
||||
if (exports.hideDiff !== true && err.showDiff !== false && sameType(actual, expected) && expected !== undefined) {
|
||||
escape = false;
|
||||
if (!(utils.isString(actual) && utils.isString(expected))) {
|
||||
err.actual = actual = utils.stringify(actual);
|
||||
err.expected = expected = utils.stringify(expected);
|
||||
}
|
||||
|
||||
>>>>>>> Add --no-diff option (fixes mochajs/mocha#2514)
|
||||
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
|
||||
var match = message.match(/^([^:]+): expected/);
|
||||
msg = '\n ' + color('error message', match ? match[1] : msg);
|
||||
|
||||
if (exports.inlineDiffs) {
|
||||
msg += inlineDiff(err);
|
||||
} else {
|
||||
msg += unifiedDiff(err);
|
||||
}
|
||||
}
|
||||
|
||||
// indent stack trace
|
||||
stack = stack.replace(/^/gm, ' ');
|
||||
|
||||
// indented test title
|
||||
var testTitle = '';
|
||||
test.titlePath().forEach(function (str, index) {
|
||||
if (index !== 0) {
|
||||
testTitle += '\n ';
|
||||
}
|
||||
for (var i = 0; i < index; i++) {
|
||||
testTitle += ' ';
|
||||
}
|
||||
testTitle += str;
|
||||
});
|
||||
|
||||
console.log(fmt, (i + 1), testTitle, msg, stack);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a new `Base` reporter.
|
||||
*
|
||||
* All other reporters generally
|
||||
* inherit from this reporter, providing
|
||||
* stats such as test duration, number
|
||||
* of tests passed / failed etc.
|
||||
*
|
||||
* @param {Runner} runner
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function Base (runner) {
|
||||
var stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 };
|
||||
var failures = this.failures = [];
|
||||
|
||||
if (!runner) {
|
||||
return;
|
||||
}
|
||||
this.runner = runner;
|
||||
|
||||
runner.stats = stats;
|
||||
|
||||
runner.on('start', function () {
|
||||
stats.start = new Date();
|
||||
});
|
||||
|
||||
runner.on('suite', function (suite) {
|
||||
stats.suites = stats.suites || 0;
|
||||
suite.root || stats.suites++;
|
||||
});
|
||||
|
||||
runner.on('test end', function () {
|
||||
stats.tests = stats.tests || 0;
|
||||
stats.tests++;
|
||||
});
|
||||
|
||||
runner.on('pass', function (test) {
|
||||
stats.passes = stats.passes || 0;
|
||||
|
||||
if (test.duration > test.slow()) {
|
||||
test.speed = 'slow';
|
||||
} else if (test.duration > test.slow() / 2) {
|
||||
test.speed = 'medium';
|
||||
} else {
|
||||
test.speed = 'fast';
|
||||
}
|
||||
|
||||
stats.passes++;
|
||||
});
|
||||
|
||||
runner.on('fail', function (test, err) {
|
||||
stats.failures = stats.failures || 0;
|
||||
stats.failures++;
|
||||
if (showDiff(err)) {
|
||||
stringifyDiffObjs(err);
|
||||
}
|
||||
test.err = err;
|
||||
failures.push(test);
|
||||
});
|
||||
|
||||
runner.on('end', function () {
|
||||
stats.end = new Date();
|
||||
stats.duration = new Date() - stats.start;
|
||||
});
|
||||
|
||||
runner.on('pending', function () {
|
||||
stats.pending++;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Output common epilogue used by many of
|
||||
* the bundled reporters.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
Base.prototype.epilogue = function () {
|
||||
var stats = this.stats;
|
||||
var fmt;
|
||||
|
||||
console.log();
|
||||
|
||||
// passes
|
||||
fmt = color('bright pass', ' ') +
|
||||
color('green', ' %d passing') +
|
||||
color('light', ' (%s)');
|
||||
|
||||
console.log(fmt,
|
||||
stats.passes || 0,
|
||||
ms(stats.duration));
|
||||
|
||||
// pending
|
||||
if (stats.pending) {
|
||||
fmt = color('pending', ' ') +
|
||||
color('pending', ' %d pending');
|
||||
|
||||
console.log(fmt, stats.pending);
|
||||
}
|
||||
|
||||
// failures
|
||||
if (stats.failures) {
|
||||
fmt = color('fail', ' %d failing');
|
||||
|
||||
console.log(fmt, stats.failures);
|
||||
|
||||
Base.list(this.failures);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log();
|
||||
};
|
||||
|
||||
/**
|
||||
* Pad the given `str` to `len`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} str
|
||||
* @param {string} len
|
||||
* @return {string}
|
||||
*/
|
||||
function pad (str, len) {
|
||||
str = String(str);
|
||||
return Array(len - str.length + 1).join(' ') + str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an inline diff between 2 strings with coloured ANSI output
|
||||
*
|
||||
* @api private
|
||||
* @param {Error} err with actual/expected
|
||||
* @return {string} Diff
|
||||
*/
|
||||
function inlineDiff (err) {
|
||||
var msg = errorDiff(err);
|
||||
|
||||
// linenos
|
||||
var lines = msg.split('\n');
|
||||
if (lines.length > 4) {
|
||||
var width = String(lines.length).length;
|
||||
msg = lines.map(function (str, i) {
|
||||
return pad(++i, width) + ' |' + ' ' + str;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
// legend
|
||||
msg = '\n' +
|
||||
color('diff removed', 'actual') +
|
||||
' ' +
|
||||
color('diff added', 'expected') +
|
||||
'\n\n' +
|
||||
msg +
|
||||
'\n';
|
||||
|
||||
// indent
|
||||
msg = msg.replace(/^/gm, ' ');
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unified diff between two strings.
|
||||
*
|
||||
* @api private
|
||||
* @param {Error} err with actual/expected
|
||||
* @return {string} The diff.
|
||||
*/
|
||||
function unifiedDiff (err) {
|
||||
var indent = ' ';
|
||||
function cleanUp (line) {
|
||||
if (line[0] === '+') {
|
||||
return indent + colorLines('diff added', line);
|
||||
}
|
||||
if (line[0] === '-') {
|
||||
return indent + colorLines('diff removed', line);
|
||||
}
|
||||
if (line.match(/@@/)) {
|
||||
return '--';
|
||||
}
|
||||
if (line.match(/\\ No newline/)) {
|
||||
return null;
|
||||
}
|
||||
return indent + line;
|
||||
}
|
||||
function notBlank (line) {
|
||||
return typeof line !== 'undefined' && line !== null;
|
||||
}
|
||||
var msg = diff.createPatch('string', err.actual, err.expected);
|
||||
var lines = msg.split('\n').splice(5);
|
||||
return '\n ' +
|
||||
colorLines('diff added', '+ expected') + ' ' +
|
||||
colorLines('diff removed', '- actual') +
|
||||
'\n\n' +
|
||||
lines.map(cleanUp).filter(notBlank).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a character diff for `err`.
|
||||
*
|
||||
* @api private
|
||||
* @param {Error} err
|
||||
* @return {string}
|
||||
*/
|
||||
function errorDiff (err) {
|
||||
return diff.diffWordsWithSpace(err.actual, err.expected).map(function (str) {
|
||||
if (str.added) {
|
||||
return colorLines('diff added', str.value);
|
||||
}
|
||||
if (str.removed) {
|
||||
return colorLines('diff removed', str.value);
|
||||
}
|
||||
return str.value;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Color lines for `str`, using the color `name`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} name
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
*/
|
||||
function colorLines (name, str) {
|
||||
return str.split('\n').map(function (str) {
|
||||
return color(name, str);
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Object#toString reference.
|
||||
*/
|
||||
var objToString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Check that a / b have the same type.
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {boolean}
|
||||
*/
|
||||
function sameType (a, b) {
|
||||
return objToString.call(a) === objToString.call(b);
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Doc
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Expose `Doc`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Doc;
|
||||
|
||||
/**
|
||||
* Initialize a new `Doc` reporter.
|
||||
*
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends {Base}
|
||||
* @public
|
||||
* @param {Runner} runner
|
||||
* @api public
|
||||
*/
|
||||
function Doc(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var indents = 2;
|
||||
|
||||
function indent() {
|
||||
return Array(indents).join(' ');
|
||||
}
|
||||
|
||||
runner.on('suite', function(suite) {
|
||||
if (suite.root) {
|
||||
return;
|
||||
}
|
||||
++indents;
|
||||
console.log('%s<section class="suite">', indent());
|
||||
++indents;
|
||||
console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
|
||||
console.log('%s<dl>', indent());
|
||||
});
|
||||
|
||||
runner.on('suite end', function(suite) {
|
||||
if (suite.root) {
|
||||
return;
|
||||
}
|
||||
console.log('%s</dl>', indent());
|
||||
--indents;
|
||||
console.log('%s</section>', indent());
|
||||
--indents;
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title));
|
||||
var code = utils.escape(utils.clean(test.body));
|
||||
console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
|
||||
});
|
||||
|
||||
runner.on('fail', function(test, err) {
|
||||
console.log(
|
||||
'%s <dt class="error">%s</dt>',
|
||||
indent(),
|
||||
utils.escape(test.title)
|
||||
);
|
||||
var code = utils.escape(utils.clean(test.body));
|
||||
console.log(
|
||||
'%s <dd class="error"><pre><code>%s</code></pre></dd>',
|
||||
indent(),
|
||||
code
|
||||
);
|
||||
console.log('%s <dd class="error">%s</dd>', indent(), utils.escape(err));
|
||||
});
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Dot
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var color = Base.color;
|
||||
|
||||
/**
|
||||
* Expose `Dot`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Dot;
|
||||
|
||||
/**
|
||||
* Initialize a new `Dot` matrix test reporter.
|
||||
*
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @public
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function Dot(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var width = (Base.window.width * 0.75) | 0;
|
||||
var n = -1;
|
||||
|
||||
runner.on('start', function() {
|
||||
process.stdout.write('\n');
|
||||
});
|
||||
|
||||
runner.on('pending', function() {
|
||||
if (++n % width === 0) {
|
||||
process.stdout.write('\n ');
|
||||
}
|
||||
process.stdout.write(color('pending', Base.symbols.comma));
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
if (++n % width === 0) {
|
||||
process.stdout.write('\n ');
|
||||
}
|
||||
if (test.speed === 'slow') {
|
||||
process.stdout.write(color('bright yellow', Base.symbols.dot));
|
||||
} else {
|
||||
process.stdout.write(color(test.speed, Base.symbols.dot));
|
||||
}
|
||||
});
|
||||
|
||||
runner.on('fail', function() {
|
||||
if (++n % width === 0) {
|
||||
process.stdout.write('\n ');
|
||||
}
|
||||
process.stdout.write(color('fail', Base.symbols.bang));
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
console.log();
|
||||
self.epilogue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Dot, Base);
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint-env browser */
|
||||
/**
|
||||
* @module HTML
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var utils = require('../utils');
|
||||
var Progress = require('../browser/progress');
|
||||
var escapeRe = require('escape-string-regexp');
|
||||
var escape = utils.escape;
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars, no-native-reassign */
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var setInterval = global.setInterval;
|
||||
var clearTimeout = global.clearTimeout;
|
||||
var clearInterval = global.clearInterval;
|
||||
/* eslint-enable no-unused-vars, no-native-reassign */
|
||||
|
||||
/**
|
||||
* Expose `HTML`.
|
||||
*/
|
||||
|
||||
exports = module.exports = HTML;
|
||||
|
||||
/**
|
||||
* Stats template.
|
||||
*/
|
||||
|
||||
var statsTemplate =
|
||||
'<ul id="mocha-stats">' +
|
||||
'<li class="progress"><canvas width="40" height="40"></canvas></li>' +
|
||||
'<li class="passes"><a href="javascript:void(0);">passes:</a> <em>0</em></li>' +
|
||||
'<li class="failures"><a href="javascript:void(0);">failures:</a> <em>0</em></li>' +
|
||||
'<li class="duration">duration: <em>0</em>s</li>' +
|
||||
'</ul>';
|
||||
|
||||
var playIcon = '‣';
|
||||
|
||||
/**
|
||||
* Initialize a new `HTML` reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function HTML(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var stats = this.stats;
|
||||
var stat = fragment(statsTemplate);
|
||||
var items = stat.getElementsByTagName('li');
|
||||
var passes = items[1].getElementsByTagName('em')[0];
|
||||
var passesLink = items[1].getElementsByTagName('a')[0];
|
||||
var failures = items[2].getElementsByTagName('em')[0];
|
||||
var failuresLink = items[2].getElementsByTagName('a')[0];
|
||||
var duration = items[3].getElementsByTagName('em')[0];
|
||||
var canvas = stat.getElementsByTagName('canvas')[0];
|
||||
var report = fragment('<ul id="mocha-report"></ul>');
|
||||
var stack = [report];
|
||||
var progress;
|
||||
var ctx;
|
||||
var root = document.getElementById('mocha');
|
||||
|
||||
if (canvas.getContext) {
|
||||
var ratio = window.devicePixelRatio || 1;
|
||||
canvas.style.width = canvas.width;
|
||||
canvas.style.height = canvas.height;
|
||||
canvas.width *= ratio;
|
||||
canvas.height *= ratio;
|
||||
ctx = canvas.getContext('2d');
|
||||
ctx.scale(ratio, ratio);
|
||||
progress = new Progress();
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
return error('#mocha div missing, add it to your document');
|
||||
}
|
||||
|
||||
// pass toggle
|
||||
on(passesLink, 'click', function(evt) {
|
||||
evt.preventDefault();
|
||||
unhide();
|
||||
var name = /pass/.test(report.className) ? '' : ' pass';
|
||||
report.className = report.className.replace(/fail|pass/g, '') + name;
|
||||
if (report.className.trim()) {
|
||||
hideSuitesWithout('test pass');
|
||||
}
|
||||
});
|
||||
|
||||
// failure toggle
|
||||
on(failuresLink, 'click', function(evt) {
|
||||
evt.preventDefault();
|
||||
unhide();
|
||||
var name = /fail/.test(report.className) ? '' : ' fail';
|
||||
report.className = report.className.replace(/fail|pass/g, '') + name;
|
||||
if (report.className.trim()) {
|
||||
hideSuitesWithout('test fail');
|
||||
}
|
||||
});
|
||||
|
||||
root.appendChild(stat);
|
||||
root.appendChild(report);
|
||||
|
||||
if (progress) {
|
||||
progress.size(40);
|
||||
}
|
||||
|
||||
runner.on('suite', function(suite) {
|
||||
if (suite.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
// suite
|
||||
var url = self.suiteURL(suite);
|
||||
var el = fragment(
|
||||
'<li class="suite"><h1><a href="%s">%s</a></h1></li>',
|
||||
url,
|
||||
escape(suite.title)
|
||||
);
|
||||
|
||||
// container
|
||||
stack[0].appendChild(el);
|
||||
stack.unshift(document.createElement('ul'));
|
||||
el.appendChild(stack[0]);
|
||||
});
|
||||
|
||||
runner.on('suite end', function(suite) {
|
||||
if (suite.root) {
|
||||
updateStats();
|
||||
return;
|
||||
}
|
||||
stack.shift();
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
var url = self.testURL(test);
|
||||
var markup =
|
||||
'<li class="test pass %e"><h2>%e<span class="duration">%ems</span> ' +
|
||||
'<a href="%s" class="replay">' +
|
||||
playIcon +
|
||||
'</a></h2></li>';
|
||||
var el = fragment(markup, test.speed, test.title, test.duration, url);
|
||||
self.addCodeToggle(el, test.body);
|
||||
appendToStack(el);
|
||||
updateStats();
|
||||
});
|
||||
|
||||
runner.on('fail', function(test) {
|
||||
var el = fragment(
|
||||
'<li class="test fail"><h2>%e <a href="%e" class="replay">' +
|
||||
playIcon +
|
||||
'</a></h2></li>',
|
||||
test.title,
|
||||
self.testURL(test)
|
||||
);
|
||||
var stackString; // Note: Includes leading newline
|
||||
var message = test.err.toString();
|
||||
|
||||
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
|
||||
// check for the result of the stringifying.
|
||||
if (message === '[object Error]') {
|
||||
message = test.err.message;
|
||||
}
|
||||
|
||||
if (test.err.stack) {
|
||||
var indexOfMessage = test.err.stack.indexOf(test.err.message);
|
||||
if (indexOfMessage === -1) {
|
||||
stackString = test.err.stack;
|
||||
} else {
|
||||
stackString = test.err.stack.substr(
|
||||
test.err.message.length + indexOfMessage
|
||||
);
|
||||
}
|
||||
} else if (test.err.sourceURL && test.err.line !== undefined) {
|
||||
// Safari doesn't give you a stack. Let's at least provide a source line.
|
||||
stackString = '\n(' + test.err.sourceURL + ':' + test.err.line + ')';
|
||||
}
|
||||
|
||||
stackString = stackString || '';
|
||||
|
||||
if (test.err.htmlMessage && stackString) {
|
||||
el.appendChild(
|
||||
fragment(
|
||||
'<div class="html-error">%s\n<pre class="error">%e</pre></div>',
|
||||
test.err.htmlMessage,
|
||||
stackString
|
||||
)
|
||||
);
|
||||
} else if (test.err.htmlMessage) {
|
||||
el.appendChild(
|
||||
fragment('<div class="html-error">%s</div>', test.err.htmlMessage)
|
||||
);
|
||||
} else {
|
||||
el.appendChild(
|
||||
fragment('<pre class="error">%e%e</pre>', message, stackString)
|
||||
);
|
||||
}
|
||||
|
||||
self.addCodeToggle(el, test.body);
|
||||
appendToStack(el);
|
||||
updateStats();
|
||||
});
|
||||
|
||||
runner.on('pending', function(test) {
|
||||
var el = fragment(
|
||||
'<li class="test pass pending"><h2>%e</h2></li>',
|
||||
test.title
|
||||
);
|
||||
appendToStack(el);
|
||||
updateStats();
|
||||
});
|
||||
|
||||
function appendToStack(el) {
|
||||
// Don't call .appendChild if #mocha-report was already .shift()'ed off the stack.
|
||||
if (stack[0]) {
|
||||
stack[0].appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
function updateStats() {
|
||||
// TODO: add to stats
|
||||
var percent = (stats.tests / runner.total * 100) | 0;
|
||||
if (progress) {
|
||||
progress.update(percent).draw(ctx);
|
||||
}
|
||||
|
||||
// update stats
|
||||
var ms = new Date() - stats.start;
|
||||
text(passes, stats.passes);
|
||||
text(failures, stats.failures);
|
||||
text(duration, (ms / 1000).toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a URL, preserving querystring ("search") parameters.
|
||||
*
|
||||
* @param {string} s
|
||||
* @return {string} A new URL.
|
||||
*/
|
||||
function makeUrl(s) {
|
||||
var search = window.location.search;
|
||||
|
||||
// Remove previous grep query parameter if present
|
||||
if (search) {
|
||||
search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?');
|
||||
}
|
||||
|
||||
return (
|
||||
window.location.pathname +
|
||||
(search ? search + '&' : '?') +
|
||||
'grep=' +
|
||||
encodeURIComponent(escapeRe(s))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide suite URL.
|
||||
*
|
||||
* @param {Object} [suite]
|
||||
*/
|
||||
HTML.prototype.suiteURL = function(suite) {
|
||||
return makeUrl(suite.fullTitle());
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide test URL.
|
||||
*
|
||||
* @param {Object} [test]
|
||||
*/
|
||||
HTML.prototype.testURL = function(test) {
|
||||
return makeUrl(test.fullTitle());
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds code toggle functionality for the provided test's list element.
|
||||
*
|
||||
* @param {HTMLLIElement} el
|
||||
* @param {string} contents
|
||||
*/
|
||||
HTML.prototype.addCodeToggle = function(el, contents) {
|
||||
var h2 = el.getElementsByTagName('h2')[0];
|
||||
|
||||
on(h2, 'click', function() {
|
||||
pre.style.display = pre.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
|
||||
var pre = fragment('<pre><code>%e</code></pre>', utils.clean(contents));
|
||||
el.appendChild(pre);
|
||||
pre.style.display = 'none';
|
||||
};
|
||||
|
||||
/**
|
||||
* Display error `msg`.
|
||||
*
|
||||
* @param {string} msg
|
||||
*/
|
||||
function error(msg) {
|
||||
document.body.appendChild(fragment('<div id="mocha-error">%s</div>', msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a DOM fragment from `html`.
|
||||
*
|
||||
* @param {string} html
|
||||
*/
|
||||
function fragment(html) {
|
||||
var args = arguments;
|
||||
var div = document.createElement('div');
|
||||
var i = 1;
|
||||
|
||||
div.innerHTML = html.replace(/%([se])/g, function(_, type) {
|
||||
switch (type) {
|
||||
case 's':
|
||||
return String(args[i++]);
|
||||
case 'e':
|
||||
return escape(args[i++]);
|
||||
// no default
|
||||
}
|
||||
});
|
||||
|
||||
return div.firstChild;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for suites that do not have elements
|
||||
* with `classname`, and hide them.
|
||||
*
|
||||
* @param {text} classname
|
||||
*/
|
||||
function hideSuitesWithout(classname) {
|
||||
var suites = document.getElementsByClassName('suite');
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var els = suites[i].getElementsByClassName(classname);
|
||||
if (!els.length) {
|
||||
suites[i].className += ' hidden';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhide .hidden suites.
|
||||
*/
|
||||
function unhide() {
|
||||
var els = document.getElementsByClassName('suite hidden');
|
||||
for (var i = 0; i < els.length; ++i) {
|
||||
els[i].className = els[i].className.replace('suite hidden', 'suite');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an element's text contents.
|
||||
*
|
||||
* @param {HTMLElement} el
|
||||
* @param {string} contents
|
||||
*/
|
||||
function text(el, contents) {
|
||||
if (el.textContent) {
|
||||
el.textContent = contents;
|
||||
} else {
|
||||
el.innerText = contents;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen on `event` with callback `fn`.
|
||||
*/
|
||||
function on(el, event, fn) {
|
||||
if (el.addEventListener) {
|
||||
el.addEventListener(event, fn, false);
|
||||
} else {
|
||||
el.attachEvent('on' + event, fn);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
// Alias exports to a their normalized format Mocha#reporter to prevent a need
|
||||
// for dynamic (try/catch) requires, which Browserify doesn't handle.
|
||||
exports.Base = exports.base = require('./base');
|
||||
exports.Dot = exports.dot = require('./dot');
|
||||
exports.Doc = exports.doc = require('./doc');
|
||||
exports.TAP = exports.tap = require('./tap');
|
||||
exports.JSON = exports.json = require('./json');
|
||||
exports.HTML = exports.html = require('./html');
|
||||
exports.List = exports.list = require('./list');
|
||||
exports.Min = exports.min = require('./min');
|
||||
exports.Spec = exports.spec = require('./spec');
|
||||
exports.Nyan = exports.nyan = require('./nyan');
|
||||
exports.XUnit = exports.xunit = require('./xunit');
|
||||
exports.Markdown = exports.markdown = require('./markdown');
|
||||
exports.Progress = exports.progress = require('./progress');
|
||||
exports.Landing = exports.landing = require('./landing');
|
||||
exports.JSONStream = exports['json-stream'] = require('./json-stream');
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module JSONStream
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
|
||||
/**
|
||||
* Expose `List`.
|
||||
*/
|
||||
|
||||
exports = module.exports = List;
|
||||
|
||||
/**
|
||||
* Initialize a new `JSONStream` test reporter.
|
||||
*
|
||||
* @public
|
||||
* @name JSONStream
|
||||
* @class JSONStream
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function List(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var total = runner.total;
|
||||
|
||||
runner.on('start', function() {
|
||||
console.log(JSON.stringify(['start', {total: total}]));
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
console.log(JSON.stringify(['pass', clean(test)]));
|
||||
});
|
||||
|
||||
runner.on('fail', function(test, err) {
|
||||
test = clean(test);
|
||||
test.err = err.message;
|
||||
test.stack = err.stack || null;
|
||||
console.log(JSON.stringify(['fail', test]));
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
process.stdout.write(JSON.stringify(['end', self.stats]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a plain-object representation of `test`
|
||||
* free of cyclic properties etc.
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} test
|
||||
* @return {Object}
|
||||
*/
|
||||
function clean(test) {
|
||||
return {
|
||||
title: test.title,
|
||||
fullTitle: test.fullTitle(),
|
||||
duration: test.duration,
|
||||
currentRetry: test.currentRetry()
|
||||
};
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module JSON
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
|
||||
/**
|
||||
* Expose `JSON`.
|
||||
*/
|
||||
|
||||
exports = module.exports = JSONReporter;
|
||||
|
||||
/**
|
||||
* Initialize a new `JSON` reporter.
|
||||
*
|
||||
* @public
|
||||
* @class JSON
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function JSONReporter(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var tests = [];
|
||||
var pending = [];
|
||||
var failures = [];
|
||||
var passes = [];
|
||||
|
||||
runner.on('test end', function(test) {
|
||||
tests.push(test);
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
passes.push(test);
|
||||
});
|
||||
|
||||
runner.on('fail', function(test) {
|
||||
failures.push(test);
|
||||
});
|
||||
|
||||
runner.on('pending', function(test) {
|
||||
pending.push(test);
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
var obj = {
|
||||
stats: self.stats,
|
||||
tests: tests.map(clean),
|
||||
pending: pending.map(clean),
|
||||
failures: failures.map(clean),
|
||||
passes: passes.map(clean)
|
||||
};
|
||||
|
||||
runner.testResults = obj;
|
||||
|
||||
process.stdout.write(JSON.stringify(obj, null, 2));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a plain-object representation of `test`
|
||||
* free of cyclic properties etc.
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} test
|
||||
* @return {Object}
|
||||
*/
|
||||
function clean(test) {
|
||||
var err = test.err || {};
|
||||
if (err instanceof Error) {
|
||||
err = errorJSON(err);
|
||||
}
|
||||
|
||||
return {
|
||||
title: test.title,
|
||||
fullTitle: test.fullTitle(),
|
||||
duration: test.duration,
|
||||
currentRetry: test.currentRetry(),
|
||||
err: cleanCycles(err)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces any circular references inside `obj` with '[object Object]'
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} obj
|
||||
* @return {Object}
|
||||
*/
|
||||
function cleanCycles(obj) {
|
||||
var cache = [];
|
||||
return JSON.parse(
|
||||
JSON.stringify(obj, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) {
|
||||
// Instead of going in a circle, we'll print [object Object]
|
||||
return '' + value;
|
||||
}
|
||||
cache.push(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform an Error object into a JSON object.
|
||||
*
|
||||
* @api private
|
||||
* @param {Error} err
|
||||
* @return {Object}
|
||||
*/
|
||||
function errorJSON(err) {
|
||||
var res = {};
|
||||
Object.getOwnPropertyNames(err).forEach(function(key) {
|
||||
res[key] = err[key];
|
||||
}, err);
|
||||
return res;
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
|
||||
/**
|
||||
* Expose `JSON`.
|
||||
*/
|
||||
|
||||
exports = module.exports = JSONReporter;
|
||||
|
||||
/**
|
||||
* Initialize a new `JSON` reporter.
|
||||
*
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
* @param {options} mocha invocation options. Invoking
|
||||
* `mocha -R --reporter-options output-file=asdf` yields options like:
|
||||
* { ... reporterOptions: { "output-file": "asdf" } ... }
|
||||
*/
|
||||
function JSONReporter (runner, options) {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
options = options || {};
|
||||
var reptOptions = options.reporterOptions || {};
|
||||
>>>>>>> + json ouput controls: output-file, output-object
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var tests = [];
|
||||
var pending = [];
|
||||
var failures = [];
|
||||
var passes = [];
|
||||
|
||||
runner.on('test end', function (test) {
|
||||
tests.push(test);
|
||||
});
|
||||
|
||||
runner.on('pass', function (test) {
|
||||
passes.push(test);
|
||||
});
|
||||
|
||||
runner.on('fail', function (test) {
|
||||
failures.push(test);
|
||||
});
|
||||
|
||||
runner.on('pending', function (test) {
|
||||
pending.push(test);
|
||||
});
|
||||
|
||||
runner.once('end', function () {
|
||||
var obj = {
|
||||
stats: self.stats,
|
||||
tests: tests.map(clean),
|
||||
pending: pending.map(clean),
|
||||
failures: failures.map(clean),
|
||||
passes: passes.map(clean)
|
||||
};
|
||||
|
||||
runner.testResults = obj;
|
||||
<<<<<<< HEAD
|
||||
if ('output-object' in options.reporterOptions) {
|
||||
// Pass to reporter with: reporter("json", {"output-object": myObject})
|
||||
Object.assign(options.reporterOptions['output-object'], obj);
|
||||
} else {
|
||||
var text = JSON.stringify(obj, null, 2);
|
||||
if ('output-file' in options.reporterOptions) {
|
||||
// Direct output with `mocha -R --reporter-options output-file=rpt.json`
|
||||
try {
|
||||
require('fs').writeFileSync(options.reporterOptions['output-file'], text);
|
||||
} catch (e) {
|
||||
console.warn('error writing to ' + options.reporterOptions.output + ':', e);
|
||||
=======
|
||||
if ('output-object' in reptOptions) {
|
||||
// Pass to reporter with: reporter("json", {"output-object": myObject})
|
||||
Object.assign(reptOptions['output-object'], obj);
|
||||
} else {
|
||||
var text = JSON.stringify(obj, null, 2);
|
||||
if ('output-file' in reptOptions) {
|
||||
// Direct output with `mocha -R --reporter-options output-file=rpt.json`
|
||||
try {
|
||||
require('fs').writeFileSync(reptOptions['output-file'], text);
|
||||
} catch (e) {
|
||||
console.warn('error writing to ' + reptOptions.output + ':', e);
|
||||
>>>>>>> + json ouput controls: output-file, output-object
|
||||
}
|
||||
} else {
|
||||
process.stdout.write(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a plain-object representation of `test`
|
||||
* free of cyclic properties etc.
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} test
|
||||
* @return {Object}
|
||||
*/
|
||||
function clean (test) {
|
||||
return {
|
||||
title: test.title,
|
||||
fullTitle: test.fullTitle(),
|
||||
duration: test.duration,
|
||||
currentRetry: test.currentRetry(),
|
||||
err: errorJSON(test.err || {})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform `error` into a JSON object.
|
||||
*
|
||||
* @api private
|
||||
* @param {Error} err
|
||||
* @return {Object}
|
||||
*/
|
||||
function errorJSON (err) {
|
||||
var res = {};
|
||||
Object.getOwnPropertyNames(err).forEach(function (key) {
|
||||
res[key] = err[key];
|
||||
}, err);
|
||||
return res;
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Landing
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var cursor = Base.cursor;
|
||||
var color = Base.color;
|
||||
|
||||
/**
|
||||
* Expose `Landing`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Landing;
|
||||
|
||||
/**
|
||||
* Airplane color.
|
||||
*/
|
||||
|
||||
Base.colors.plane = 0;
|
||||
|
||||
/**
|
||||
* Airplane crash color.
|
||||
*/
|
||||
|
||||
Base.colors['plane crash'] = 31;
|
||||
|
||||
/**
|
||||
* Runway color.
|
||||
*/
|
||||
|
||||
Base.colors.runway = 90;
|
||||
|
||||
/**
|
||||
* Initialize a new `Landing` reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function Landing(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var width = (Base.window.width * 0.75) | 0;
|
||||
var total = runner.total;
|
||||
var stream = process.stdout;
|
||||
var plane = color('plane', '✈');
|
||||
var crashed = -1;
|
||||
var n = 0;
|
||||
|
||||
function runway() {
|
||||
var buf = Array(width).join('-');
|
||||
return ' ' + color('runway', buf);
|
||||
}
|
||||
|
||||
runner.on('start', function() {
|
||||
stream.write('\n\n\n ');
|
||||
cursor.hide();
|
||||
});
|
||||
|
||||
runner.on('test end', function(test) {
|
||||
// check if the plane crashed
|
||||
var col = crashed === -1 ? (width * ++n / total) | 0 : crashed;
|
||||
|
||||
// show the crash
|
||||
if (test.state === 'failed') {
|
||||
plane = color('plane crash', '✈');
|
||||
crashed = col;
|
||||
}
|
||||
|
||||
// render landing strip
|
||||
stream.write('\u001b[' + (width + 1) + 'D\u001b[2A');
|
||||
stream.write(runway());
|
||||
stream.write('\n ');
|
||||
stream.write(color('runway', Array(col).join('⋅')));
|
||||
stream.write(plane);
|
||||
stream.write(color('runway', Array(width - col).join('⋅') + '\n'));
|
||||
stream.write(runway());
|
||||
stream.write('\u001b[0m');
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
cursor.show();
|
||||
console.log();
|
||||
self.epilogue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Landing, Base);
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module List
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var color = Base.color;
|
||||
var cursor = Base.cursor;
|
||||
|
||||
/**
|
||||
* Expose `List`.
|
||||
*/
|
||||
|
||||
exports = module.exports = List;
|
||||
|
||||
/**
|
||||
* Initialize a new `List` test reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function List(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var n = 0;
|
||||
|
||||
runner.on('start', function() {
|
||||
console.log();
|
||||
});
|
||||
|
||||
runner.on('test', function(test) {
|
||||
process.stdout.write(color('pass', ' ' + test.fullTitle() + ': '));
|
||||
});
|
||||
|
||||
runner.on('pending', function(test) {
|
||||
var fmt = color('checkmark', ' -') + color('pending', ' %s');
|
||||
console.log(fmt, test.fullTitle());
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
var fmt =
|
||||
color('checkmark', ' ' + Base.symbols.ok) +
|
||||
color('pass', ' %s: ') +
|
||||
color(test.speed, '%dms');
|
||||
cursor.CR();
|
||||
console.log(fmt, test.fullTitle(), test.duration);
|
||||
});
|
||||
|
||||
runner.on('fail', function(test) {
|
||||
cursor.CR();
|
||||
console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
|
||||
});
|
||||
|
||||
runner.once('end', self.epilogue.bind(self));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(List, Base);
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Markdown
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
var SUITE_PREFIX = '$';
|
||||
|
||||
/**
|
||||
* Expose `Markdown`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Markdown;
|
||||
|
||||
/**
|
||||
* Initialize a new `Markdown` reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function Markdown(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var level = 0;
|
||||
var buf = '';
|
||||
|
||||
function title(str) {
|
||||
return Array(level).join('#') + ' ' + str;
|
||||
}
|
||||
|
||||
function mapTOC(suite, obj) {
|
||||
var ret = obj;
|
||||
var key = SUITE_PREFIX + suite.title;
|
||||
|
||||
obj = obj[key] = obj[key] || {suite: suite};
|
||||
suite.suites.forEach(function(suite) {
|
||||
mapTOC(suite, obj);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function stringifyTOC(obj, level) {
|
||||
++level;
|
||||
var buf = '';
|
||||
var link;
|
||||
for (var key in obj) {
|
||||
if (key === 'suite') {
|
||||
continue;
|
||||
}
|
||||
if (key !== SUITE_PREFIX) {
|
||||
link = ' - [' + key.substring(1) + ']';
|
||||
link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
|
||||
buf += Array(level).join(' ') + link;
|
||||
}
|
||||
buf += stringifyTOC(obj[key], level);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
function generateTOC(suite) {
|
||||
var obj = mapTOC(suite, {});
|
||||
return stringifyTOC(obj, 0);
|
||||
}
|
||||
|
||||
generateTOC(runner.suite);
|
||||
|
||||
runner.on('suite', function(suite) {
|
||||
++level;
|
||||
var slug = utils.slug(suite.fullTitle());
|
||||
buf += '<a name="' + slug + '"></a>' + '\n';
|
||||
buf += title(suite.title) + '\n';
|
||||
});
|
||||
|
||||
runner.on('suite end', function() {
|
||||
--level;
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
var code = utils.clean(test.body);
|
||||
buf += test.title + '.\n';
|
||||
buf += '\n```js\n';
|
||||
buf += code + '\n';
|
||||
buf += '```\n\n';
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
process.stdout.write('# TOC\n');
|
||||
process.stdout.write(generateTOC(runner.suite));
|
||||
process.stdout.write(buf);
|
||||
});
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Min
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
|
||||
/**
|
||||
* Expose `Min`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Min;
|
||||
|
||||
/**
|
||||
* Initialize a new `Min` minimal test reporter (best used with --watch).
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function Min(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
runner.on('start', function() {
|
||||
// clear screen
|
||||
process.stdout.write('\u001b[2J');
|
||||
// set cursor position
|
||||
process.stdout.write('\u001b[1;3H');
|
||||
});
|
||||
|
||||
runner.once('end', this.epilogue.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Min, Base);
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Nyan
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
|
||||
/**
|
||||
* Expose `Dot`.
|
||||
*/
|
||||
|
||||
exports = module.exports = NyanCat;
|
||||
|
||||
/**
|
||||
* Initialize a new `Dot` matrix test reporter.
|
||||
*
|
||||
* @param {Runner} runner
|
||||
* @api public
|
||||
* @public
|
||||
* @class Nyan
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
*/
|
||||
|
||||
function NyanCat(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var width = (Base.window.width * 0.75) | 0;
|
||||
var nyanCatWidth = (this.nyanCatWidth = 11);
|
||||
|
||||
this.colorIndex = 0;
|
||||
this.numberOfLines = 4;
|
||||
this.rainbowColors = self.generateColors();
|
||||
this.scoreboardWidth = 5;
|
||||
this.tick = 0;
|
||||
this.trajectories = [[], [], [], []];
|
||||
this.trajectoryWidthMax = width - nyanCatWidth;
|
||||
|
||||
runner.on('start', function() {
|
||||
Base.cursor.hide();
|
||||
self.draw();
|
||||
});
|
||||
|
||||
runner.on('pending', function() {
|
||||
self.draw();
|
||||
});
|
||||
|
||||
runner.on('pass', function() {
|
||||
self.draw();
|
||||
});
|
||||
|
||||
runner.on('fail', function() {
|
||||
self.draw();
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
Base.cursor.show();
|
||||
for (var i = 0; i < self.numberOfLines; i++) {
|
||||
write('\n');
|
||||
}
|
||||
self.epilogue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(NyanCat, Base);
|
||||
|
||||
/**
|
||||
* Draw the nyan cat
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
NyanCat.prototype.draw = function() {
|
||||
this.appendRainbow();
|
||||
this.drawScoreboard();
|
||||
this.drawRainbow();
|
||||
this.drawNyanCat();
|
||||
this.tick = !this.tick;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the "scoreboard" showing the number
|
||||
* of passes, failures and pending tests.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
NyanCat.prototype.drawScoreboard = function() {
|
||||
var stats = this.stats;
|
||||
|
||||
function draw(type, n) {
|
||||
write(' ');
|
||||
write(Base.color(type, n));
|
||||
write('\n');
|
||||
}
|
||||
|
||||
draw('green', stats.passes);
|
||||
draw('fail', stats.failures);
|
||||
draw('pending', stats.pending);
|
||||
write('\n');
|
||||
|
||||
this.cursorUp(this.numberOfLines);
|
||||
};
|
||||
|
||||
/**
|
||||
* Append the rainbow.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
NyanCat.prototype.appendRainbow = function() {
|
||||
var segment = this.tick ? '_' : '-';
|
||||
var rainbowified = this.rainbowify(segment);
|
||||
|
||||
for (var index = 0; index < this.numberOfLines; index++) {
|
||||
var trajectory = this.trajectories[index];
|
||||
if (trajectory.length >= this.trajectoryWidthMax) {
|
||||
trajectory.shift();
|
||||
}
|
||||
trajectory.push(rainbowified);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the rainbow.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
NyanCat.prototype.drawRainbow = function() {
|
||||
var self = this;
|
||||
|
||||
this.trajectories.forEach(function(line) {
|
||||
write('\u001b[' + self.scoreboardWidth + 'C');
|
||||
write(line.join(''));
|
||||
write('\n');
|
||||
});
|
||||
|
||||
this.cursorUp(this.numberOfLines);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the nyan cat
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
NyanCat.prototype.drawNyanCat = function() {
|
||||
var self = this;
|
||||
var startWidth = this.scoreboardWidth + this.trajectories[0].length;
|
||||
var dist = '\u001b[' + startWidth + 'C';
|
||||
var padding = '';
|
||||
|
||||
write(dist);
|
||||
write('_,------,');
|
||||
write('\n');
|
||||
|
||||
write(dist);
|
||||
padding = self.tick ? ' ' : ' ';
|
||||
write('_|' + padding + '/\\_/\\ ');
|
||||
write('\n');
|
||||
|
||||
write(dist);
|
||||
padding = self.tick ? '_' : '__';
|
||||
var tail = self.tick ? '~' : '^';
|
||||
write(tail + '|' + padding + this.face() + ' ');
|
||||
write('\n');
|
||||
|
||||
write(dist);
|
||||
padding = self.tick ? ' ' : ' ';
|
||||
write(padding + '"" "" ');
|
||||
write('\n');
|
||||
|
||||
this.cursorUp(this.numberOfLines);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw nyan cat face.
|
||||
*
|
||||
* @api private
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
NyanCat.prototype.face = function() {
|
||||
var stats = this.stats;
|
||||
if (stats.failures) {
|
||||
return '( x .x)';
|
||||
} else if (stats.pending) {
|
||||
return '( o .o)';
|
||||
} else if (stats.passes) {
|
||||
return '( ^ .^)';
|
||||
}
|
||||
return '( - .-)';
|
||||
};
|
||||
|
||||
/**
|
||||
* Move cursor up `n`.
|
||||
*
|
||||
* @api private
|
||||
* @param {number} n
|
||||
*/
|
||||
|
||||
NyanCat.prototype.cursorUp = function(n) {
|
||||
write('\u001b[' + n + 'A');
|
||||
};
|
||||
|
||||
/**
|
||||
* Move cursor down `n`.
|
||||
*
|
||||
* @api private
|
||||
* @param {number} n
|
||||
*/
|
||||
|
||||
NyanCat.prototype.cursorDown = function(n) {
|
||||
write('\u001b[' + n + 'B');
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate rainbow colors.
|
||||
*
|
||||
* @api private
|
||||
* @return {Array}
|
||||
*/
|
||||
NyanCat.prototype.generateColors = function() {
|
||||
var colors = [];
|
||||
|
||||
for (var i = 0; i < 6 * 7; i++) {
|
||||
var pi3 = Math.floor(Math.PI / 3);
|
||||
var n = i * (1.0 / 6);
|
||||
var r = Math.floor(3 * Math.sin(n) + 3);
|
||||
var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3);
|
||||
var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3);
|
||||
colors.push(36 * r + 6 * g + b + 16);
|
||||
}
|
||||
|
||||
return colors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply rainbow to the given `str`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
*/
|
||||
NyanCat.prototype.rainbowify = function(str) {
|
||||
if (!Base.useColors) {
|
||||
return str;
|
||||
}
|
||||
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
|
||||
this.colorIndex += 1;
|
||||
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
|
||||
};
|
||||
|
||||
/**
|
||||
* Stdout helper.
|
||||
*
|
||||
* @param {string} string A message to write to stdout.
|
||||
*/
|
||||
function write(string) {
|
||||
process.stdout.write(string);
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Progress
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var color = Base.color;
|
||||
var cursor = Base.cursor;
|
||||
|
||||
/**
|
||||
* Expose `Progress`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Progress;
|
||||
|
||||
/**
|
||||
* General progress bar color.
|
||||
*/
|
||||
|
||||
Base.colors.progress = 90;
|
||||
|
||||
/**
|
||||
* Initialize a new `Progress` bar test reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
* @param {Object} options
|
||||
*/
|
||||
function Progress(runner, options) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var width = (Base.window.width * 0.5) | 0;
|
||||
var total = runner.total;
|
||||
var complete = 0;
|
||||
var lastN = -1;
|
||||
|
||||
// default chars
|
||||
options = options || {};
|
||||
var reporterOptions = options.reporterOptions || {};
|
||||
|
||||
options.open = reporterOptions.open || '[';
|
||||
options.complete = reporterOptions.complete || '▬';
|
||||
options.incomplete = reporterOptions.incomplete || Base.symbols.dot;
|
||||
options.close = reporterOptions.close || ']';
|
||||
options.verbose = reporterOptions.verbose || false;
|
||||
|
||||
// tests started
|
||||
runner.on('start', function() {
|
||||
console.log();
|
||||
cursor.hide();
|
||||
});
|
||||
|
||||
// tests complete
|
||||
runner.on('test end', function() {
|
||||
complete++;
|
||||
|
||||
var percent = complete / total;
|
||||
var n = (width * percent) | 0;
|
||||
var i = width - n;
|
||||
|
||||
if (n === lastN && !options.verbose) {
|
||||
// Don't re-render the line if it hasn't changed
|
||||
return;
|
||||
}
|
||||
lastN = n;
|
||||
|
||||
cursor.CR();
|
||||
process.stdout.write('\u001b[J');
|
||||
process.stdout.write(color('progress', ' ' + options.open));
|
||||
process.stdout.write(Array(n).join(options.complete));
|
||||
process.stdout.write(Array(i).join(options.incomplete));
|
||||
process.stdout.write(color('progress', options.close));
|
||||
if (options.verbose) {
|
||||
process.stdout.write(color('progress', ' ' + complete + ' of ' + total));
|
||||
}
|
||||
});
|
||||
|
||||
// tests are complete, output some stats
|
||||
// and the failures if any
|
||||
runner.once('end', function() {
|
||||
cursor.show();
|
||||
console.log();
|
||||
self.epilogue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Progress, Base);
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Spec
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var inherits = require('../utils').inherits;
|
||||
var color = Base.color;
|
||||
|
||||
/**
|
||||
* Expose `Spec`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Spec;
|
||||
|
||||
/**
|
||||
* Initialize a new `Spec` test reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function Spec(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var self = this;
|
||||
var indents = 0;
|
||||
var n = 0;
|
||||
|
||||
function indent() {
|
||||
return Array(indents).join(' ');
|
||||
}
|
||||
|
||||
runner.on('start', function() {
|
||||
console.log();
|
||||
});
|
||||
|
||||
runner.on('suite', function(suite) {
|
||||
++indents;
|
||||
console.log(color('suite', '%s%s'), indent(), suite.title);
|
||||
});
|
||||
|
||||
runner.on('suite end', function() {
|
||||
--indents;
|
||||
if (indents === 1) {
|
||||
console.log();
|
||||
}
|
||||
});
|
||||
|
||||
runner.on('pending', function(test) {
|
||||
var fmt = indent() + color('pending', ' - %s');
|
||||
console.log(fmt, test.title);
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
var fmt;
|
||||
if (test.speed === 'fast') {
|
||||
fmt =
|
||||
indent() +
|
||||
color('checkmark', ' ' + Base.symbols.ok) +
|
||||
color('pass', ' %s');
|
||||
console.log(fmt, test.title);
|
||||
} else {
|
||||
fmt =
|
||||
indent() +
|
||||
color('checkmark', ' ' + Base.symbols.ok) +
|
||||
color('pass', ' %s') +
|
||||
color(test.speed, ' (%dms)');
|
||||
console.log(fmt, test.title, test.duration);
|
||||
}
|
||||
});
|
||||
|
||||
runner.on('fail', function(test) {
|
||||
console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
|
||||
});
|
||||
|
||||
runner.once('end', self.epilogue.bind(self));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Spec, Base);
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module TAP
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
|
||||
/**
|
||||
* Expose `TAP`.
|
||||
*/
|
||||
|
||||
exports = module.exports = TAP;
|
||||
|
||||
/**
|
||||
* Initialize a new `TAP` reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function TAP(runner) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var n = 1;
|
||||
var passes = 0;
|
||||
var failures = 0;
|
||||
|
||||
runner.on('start', function() {
|
||||
var total = runner.grepTotal(runner.suite);
|
||||
console.log('%d..%d', 1, total);
|
||||
});
|
||||
|
||||
runner.on('test end', function() {
|
||||
++n;
|
||||
});
|
||||
|
||||
runner.on('pending', function(test) {
|
||||
console.log('ok %d %s # SKIP -', n, title(test));
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
passes++;
|
||||
console.log('ok %d %s', n, title(test));
|
||||
});
|
||||
|
||||
runner.on('fail', function(test, err) {
|
||||
failures++;
|
||||
console.log('not ok %d %s', n, title(test));
|
||||
if (err.stack) {
|
||||
console.log(err.stack.replace(/^/gm, ' '));
|
||||
}
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
console.log('# tests ' + (passes + failures));
|
||||
console.log('# pass ' + passes);
|
||||
console.log('# fail ' + failures);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a TAP-safe title of `test`
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} test
|
||||
* @return {String}
|
||||
*/
|
||||
function title(test) {
|
||||
return test.fullTitle().replace(/#/g, '');
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module XUnit
|
||||
*/
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Base = require('./base');
|
||||
var utils = require('../utils');
|
||||
var inherits = utils.inherits;
|
||||
var fs = require('fs');
|
||||
var escape = utils.escape;
|
||||
var mkdirp = require('mkdirp');
|
||||
var path = require('path');
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars, no-native-reassign */
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var setInterval = global.setInterval;
|
||||
var clearTimeout = global.clearTimeout;
|
||||
var clearInterval = global.clearInterval;
|
||||
/* eslint-enable no-unused-vars, no-native-reassign */
|
||||
|
||||
/**
|
||||
* Expose `XUnit`.
|
||||
*/
|
||||
|
||||
exports = module.exports = XUnit;
|
||||
|
||||
/**
|
||||
* Initialize a new `XUnit` reporter.
|
||||
*
|
||||
* @public
|
||||
* @class
|
||||
* @memberof Mocha.reporters
|
||||
* @extends Mocha.reporters.Base
|
||||
* @api public
|
||||
* @param {Runner} runner
|
||||
*/
|
||||
function XUnit(runner, options) {
|
||||
Base.call(this, runner);
|
||||
|
||||
var stats = this.stats;
|
||||
var tests = [];
|
||||
var self = this;
|
||||
|
||||
// the name of the test suite, as it will appear in the resulting XML file
|
||||
var suiteName;
|
||||
|
||||
// the default name of the test suite if none is provided
|
||||
var DEFAULT_SUITE_NAME = 'Mocha Tests';
|
||||
|
||||
if (options && options.reporterOptions) {
|
||||
if (options.reporterOptions.output) {
|
||||
if (!fs.createWriteStream) {
|
||||
throw new Error('file output not supported in browser');
|
||||
}
|
||||
|
||||
mkdirp.sync(path.dirname(options.reporterOptions.output));
|
||||
self.fileStream = fs.createWriteStream(options.reporterOptions.output);
|
||||
}
|
||||
|
||||
// get the suite name from the reporter options (if provided)
|
||||
suiteName = options.reporterOptions.suiteName;
|
||||
}
|
||||
|
||||
// fall back to the default suite name
|
||||
suiteName = suiteName || DEFAULT_SUITE_NAME;
|
||||
|
||||
runner.on('pending', function(test) {
|
||||
tests.push(test);
|
||||
});
|
||||
|
||||
runner.on('pass', function(test) {
|
||||
tests.push(test);
|
||||
});
|
||||
|
||||
runner.on('fail', function(test) {
|
||||
tests.push(test);
|
||||
});
|
||||
|
||||
runner.once('end', function() {
|
||||
self.write(
|
||||
tag(
|
||||
'testsuite',
|
||||
{
|
||||
name: suiteName,
|
||||
tests: stats.tests,
|
||||
failures: stats.failures,
|
||||
errors: stats.failures,
|
||||
skipped: stats.tests - stats.failures - stats.passes,
|
||||
timestamp: new Date().toUTCString(),
|
||||
time: stats.duration / 1000 || 0
|
||||
},
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
tests.forEach(function(t) {
|
||||
self.test(t);
|
||||
});
|
||||
|
||||
self.write('</testsuite>');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(XUnit, Base);
|
||||
|
||||
/**
|
||||
* Override done to close the stream (if it's a file).
|
||||
*
|
||||
* @param failures
|
||||
* @param {Function} fn
|
||||
*/
|
||||
XUnit.prototype.done = function(failures, fn) {
|
||||
if (this.fileStream) {
|
||||
this.fileStream.end(function() {
|
||||
fn(failures);
|
||||
});
|
||||
} else {
|
||||
fn(failures);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Write out the given line.
|
||||
*
|
||||
* @param {string} line
|
||||
*/
|
||||
XUnit.prototype.write = function(line) {
|
||||
if (this.fileStream) {
|
||||
this.fileStream.write(line + '\n');
|
||||
} else if (typeof process === 'object' && process.stdout) {
|
||||
process.stdout.write(line + '\n');
|
||||
} else {
|
||||
console.log(line);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Output tag for the given `test.`
|
||||
*
|
||||
* @param {Test} test
|
||||
*/
|
||||
XUnit.prototype.test = function(test) {
|
||||
var attrs = {
|
||||
classname: test.parent.fullTitle(),
|
||||
name: test.title,
|
||||
time: test.duration / 1000 || 0
|
||||
};
|
||||
|
||||
if (test.state === 'failed') {
|
||||
var err = test.err;
|
||||
this.write(
|
||||
tag(
|
||||
'testcase',
|
||||
attrs,
|
||||
false,
|
||||
tag(
|
||||
'failure',
|
||||
{},
|
||||
false,
|
||||
escape(err.message) + '\n' + escape(err.stack)
|
||||
)
|
||||
)
|
||||
);
|
||||
} else if (test.isPending()) {
|
||||
this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
|
||||
} else {
|
||||
this.write(tag('testcase', attrs, true));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* HTML tag helper.
|
||||
*
|
||||
* @param name
|
||||
* @param attrs
|
||||
* @param close
|
||||
* @param content
|
||||
* @return {string}
|
||||
*/
|
||||
function tag(name, attrs, close, content) {
|
||||
var end = close ? '/>' : '>';
|
||||
var pairs = [];
|
||||
var tag;
|
||||
|
||||
for (var key in attrs) {
|
||||
if (Object.prototype.hasOwnProperty.call(attrs, key)) {
|
||||
pairs.push(key + '="' + escape(attrs[key]) + '"');
|
||||
}
|
||||
}
|
||||
|
||||
tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
|
||||
if (content) {
|
||||
tag += content + '</' + name + end;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
+441
@@ -0,0 +1,441 @@
|
||||
'use strict';
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Pending = require('./pending');
|
||||
var debug = require('debug')('mocha:runnable');
|
||||
var milliseconds = require('./ms');
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* Save timer references to avoid Sinon interfering (see GH-237).
|
||||
*/
|
||||
|
||||
/* eslint-disable no-unused-vars, no-native-reassign */
|
||||
var Date = global.Date;
|
||||
var setTimeout = global.setTimeout;
|
||||
var setInterval = global.setInterval;
|
||||
var clearTimeout = global.clearTimeout;
|
||||
var clearInterval = global.clearInterval;
|
||||
/* eslint-enable no-unused-vars, no-native-reassign */
|
||||
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
module.exports = Runnable;
|
||||
|
||||
/**
|
||||
* Initialize a new `Runnable` with the given `title` and callback `fn`. Derived from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
*
|
||||
* @class
|
||||
* @extends EventEmitter
|
||||
* @param {String} title
|
||||
* @param {Function} fn
|
||||
*/
|
||||
function Runnable(title, fn) {
|
||||
this.title = title;
|
||||
this.fn = fn;
|
||||
this.body = (fn || '').toString();
|
||||
this.async = fn && fn.length;
|
||||
this.sync = !this.async;
|
||||
this._timeout = 2000;
|
||||
this._slow = 75;
|
||||
this._enableTimeouts = true;
|
||||
this.timedOut = false;
|
||||
this._retries = -1;
|
||||
this._currentRetry = 0;
|
||||
this.pending = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `EventEmitter.prototype`.
|
||||
*/
|
||||
utils.inherits(Runnable, EventEmitter);
|
||||
|
||||
/**
|
||||
* Set & get timeout `ms`.
|
||||
*
|
||||
* @api private
|
||||
* @param {number|string} ms
|
||||
* @return {Runnable|number} ms or Runnable instance.
|
||||
*/
|
||||
Runnable.prototype.timeout = function(ms) {
|
||||
if (!arguments.length) {
|
||||
return this._timeout;
|
||||
}
|
||||
// see #1652 for reasoning
|
||||
if (ms === 0 || ms > Math.pow(2, 31)) {
|
||||
this._enableTimeouts = false;
|
||||
}
|
||||
if (typeof ms === 'string') {
|
||||
ms = milliseconds(ms);
|
||||
}
|
||||
debug('timeout %d', ms);
|
||||
this._timeout = ms;
|
||||
if (this.timer) {
|
||||
this.resetTimeout();
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get slow `ms`.
|
||||
*
|
||||
* @api private
|
||||
* @param {number|string} ms
|
||||
* @return {Runnable|number} ms or Runnable instance.
|
||||
*/
|
||||
Runnable.prototype.slow = function(ms) {
|
||||
if (!arguments.length || typeof ms === 'undefined') {
|
||||
return this._slow;
|
||||
}
|
||||
if (typeof ms === 'string') {
|
||||
ms = milliseconds(ms);
|
||||
}
|
||||
debug('slow %d', ms);
|
||||
this._slow = ms;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set and get whether timeout is `enabled`.
|
||||
*
|
||||
* @api private
|
||||
* @param {boolean} enabled
|
||||
* @return {Runnable|boolean} enabled or Runnable instance.
|
||||
*/
|
||||
Runnable.prototype.enableTimeouts = function(enabled) {
|
||||
if (!arguments.length) {
|
||||
return this._enableTimeouts;
|
||||
}
|
||||
debug('enableTimeouts %s', enabled);
|
||||
this._enableTimeouts = enabled;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Halt and mark as pending.
|
||||
*
|
||||
* @memberof Mocha.Runnable
|
||||
* @public
|
||||
* @api public
|
||||
*/
|
||||
Runnable.prototype.skip = function() {
|
||||
throw new Pending('sync skip');
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this runnable or its parent suite is marked as pending.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Runnable.prototype.isPending = function() {
|
||||
return this.pending || (this.parent && this.parent.isPending());
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` if this Runnable has failed.
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
Runnable.prototype.isFailed = function() {
|
||||
return !this.isPending() && this.state === 'failed';
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` if this Runnable has passed.
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
Runnable.prototype.isPassed = function() {
|
||||
return !this.isPending() && this.state === 'passed';
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get number of retries.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Runnable.prototype.retries = function(n) {
|
||||
if (!arguments.length) {
|
||||
return this._retries;
|
||||
}
|
||||
this._retries = n;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get current retry
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Runnable.prototype.currentRetry = function(n) {
|
||||
if (!arguments.length) {
|
||||
return this._currentRetry;
|
||||
}
|
||||
this._currentRetry = n;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the full title generated by recursively concatenating the parent's
|
||||
* full title.
|
||||
*
|
||||
* @memberof Mocha.Runnable
|
||||
* @public
|
||||
* @api public
|
||||
* @return {string}
|
||||
*/
|
||||
Runnable.prototype.fullTitle = function() {
|
||||
return this.titlePath().join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the title path generated by concatenating the parent's title path with the title.
|
||||
*
|
||||
* @memberof Mocha.Runnable
|
||||
* @public
|
||||
* @api public
|
||||
* @return {string}
|
||||
*/
|
||||
Runnable.prototype.titlePath = function() {
|
||||
return this.parent.titlePath().concat([this.title]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the timeout.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Runnable.prototype.clearTimeout = function() {
|
||||
clearTimeout(this.timer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the runnable void of private properties.
|
||||
*
|
||||
* @api private
|
||||
* @return {string}
|
||||
*/
|
||||
Runnable.prototype.inspect = function() {
|
||||
return JSON.stringify(
|
||||
this,
|
||||
function(key, val) {
|
||||
if (key[0] === '_') {
|
||||
return;
|
||||
}
|
||||
if (key === 'parent') {
|
||||
return '#<Suite>';
|
||||
}
|
||||
if (key === 'ctx') {
|
||||
return '#<Context>';
|
||||
}
|
||||
return val;
|
||||
},
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the timeout.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Runnable.prototype.resetTimeout = function() {
|
||||
var self = this;
|
||||
var ms = this.timeout() || 1e9;
|
||||
|
||||
if (!this._enableTimeouts) {
|
||||
return;
|
||||
}
|
||||
this.clearTimeout();
|
||||
this.timer = setTimeout(function() {
|
||||
if (!self._enableTimeouts) {
|
||||
return;
|
||||
}
|
||||
self.callback(self._timeoutError(ms));
|
||||
self.timedOut = true;
|
||||
}, ms);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get a list of whitelisted globals for this test run.
|
||||
*
|
||||
* @api private
|
||||
* @param {string[]} globals
|
||||
*/
|
||||
Runnable.prototype.globals = function(globals) {
|
||||
if (!arguments.length) {
|
||||
return this._allowedGlobals;
|
||||
}
|
||||
this._allowedGlobals = globals;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run the test and invoke `fn(err)`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api private
|
||||
*/
|
||||
Runnable.prototype.run = function(fn) {
|
||||
var self = this;
|
||||
var start = new Date();
|
||||
var ctx = this.ctx;
|
||||
var finished;
|
||||
var emitted;
|
||||
|
||||
// Sometimes the ctx exists, but it is not runnable
|
||||
if (ctx && ctx.runnable) {
|
||||
ctx.runnable(this);
|
||||
}
|
||||
|
||||
// called multiple times
|
||||
function multiple(err) {
|
||||
if (emitted) {
|
||||
return;
|
||||
}
|
||||
emitted = true;
|
||||
var msg = 'done() called multiple times';
|
||||
if (err && err.message) {
|
||||
err.message += " (and Mocha's " + msg + ')';
|
||||
self.emit('error', err);
|
||||
} else {
|
||||
self.emit('error', new Error(msg));
|
||||
}
|
||||
}
|
||||
|
||||
// finished
|
||||
function done(err) {
|
||||
var ms = self.timeout();
|
||||
if (self.timedOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (finished) {
|
||||
return multiple(err);
|
||||
}
|
||||
|
||||
self.clearTimeout();
|
||||
self.duration = new Date() - start;
|
||||
finished = true;
|
||||
if (!err && self.duration > ms && self._enableTimeouts) {
|
||||
err = self._timeoutError(ms);
|
||||
}
|
||||
fn(err);
|
||||
}
|
||||
|
||||
// for .resetTimeout()
|
||||
this.callback = done;
|
||||
|
||||
// explicit async with `done` argument
|
||||
if (this.async) {
|
||||
this.resetTimeout();
|
||||
|
||||
// allows skip() to be used in an explicit async context
|
||||
this.skip = function asyncSkip() {
|
||||
done(new Pending('async skip call'));
|
||||
// halt execution. the Runnable will be marked pending
|
||||
// by the previous call, and the uncaught handler will ignore
|
||||
// the failure.
|
||||
throw new Pending('async skip; aborting execution');
|
||||
};
|
||||
|
||||
if (this.allowUncaught) {
|
||||
return callFnAsync(this.fn);
|
||||
}
|
||||
try {
|
||||
callFnAsync(this.fn);
|
||||
} catch (err) {
|
||||
emitted = true;
|
||||
done(utils.getError(err));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.allowUncaught) {
|
||||
if (this.isPending()) {
|
||||
done();
|
||||
} else {
|
||||
callFn(this.fn);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// sync or promise-returning
|
||||
try {
|
||||
if (this.isPending()) {
|
||||
done();
|
||||
} else {
|
||||
callFn(this.fn);
|
||||
}
|
||||
} catch (err) {
|
||||
emitted = true;
|
||||
done(utils.getError(err));
|
||||
}
|
||||
|
||||
function callFn(fn) {
|
||||
var result = fn.call(ctx);
|
||||
if (result && typeof result.then === 'function') {
|
||||
self.resetTimeout();
|
||||
result.then(
|
||||
function() {
|
||||
done();
|
||||
// Return null so libraries like bluebird do not warn about
|
||||
// subsequently constructed Promises.
|
||||
return null;
|
||||
},
|
||||
function(reason) {
|
||||
done(reason || new Error('Promise rejected with no or falsy reason'));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (self.asyncOnly) {
|
||||
return done(
|
||||
new Error(
|
||||
'--async-only option in use without declaring `done()` or returning a promise'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function callFnAsync(fn) {
|
||||
var result = fn.call(ctx, function(err) {
|
||||
if (err instanceof Error || toString.call(err) === '[object Error]') {
|
||||
return done(err);
|
||||
}
|
||||
if (err) {
|
||||
if (Object.prototype.toString.call(err) === '[object Object]') {
|
||||
return done(
|
||||
new Error('done() invoked with non-Error: ' + JSON.stringify(err))
|
||||
);
|
||||
}
|
||||
return done(new Error('done() invoked with non-Error: ' + err));
|
||||
}
|
||||
if (result && utils.isPromise(result)) {
|
||||
return done(
|
||||
new Error(
|
||||
'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiates a "timeout" error
|
||||
*
|
||||
* @param {number} ms - Timeout (in milliseconds)
|
||||
* @returns {Error} a "timeout" error
|
||||
* @private
|
||||
*/
|
||||
Runnable.prototype._timeoutError = function(ms) {
|
||||
var msg =
|
||||
'Timeout of ' +
|
||||
ms +
|
||||
'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.';
|
||||
if (this.file) {
|
||||
msg += ' (' + this.file + ')';
|
||||
}
|
||||
return new Error(msg);
|
||||
};
|
||||
+1009
File diff suppressed because it is too large.
Load diff
+427
@@ -0,0 +1,427 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @module Suite
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Hook = require('./hook');
|
||||
var utils = require('./utils');
|
||||
var inherits = utils.inherits;
|
||||
var debug = require('debug')('mocha:suite');
|
||||
var milliseconds = require('./ms');
|
||||
|
||||
/**
|
||||
* Expose `Suite`.
|
||||
*/
|
||||
|
||||
exports = module.exports = Suite;
|
||||
|
||||
/**
|
||||
* Create a new `Suite` with the given `title` and parent `Suite`. When a suite
|
||||
* with the same title is already present, that suite is returned to provide
|
||||
* nicer reporter and more flexible meta-testing.
|
||||
*
|
||||
* @memberof Mocha
|
||||
* @public
|
||||
* @api public
|
||||
* @param {Suite} parent
|
||||
* @param {string} title
|
||||
* @return {Suite}
|
||||
*/
|
||||
exports.create = function(parent, title) {
|
||||
var suite = new Suite(title, parent.ctx);
|
||||
suite.parent = parent;
|
||||
title = suite.fullTitle();
|
||||
parent.addSuite(suite);
|
||||
return suite;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a new `Suite` with the given `title` and `ctx`. Derived from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
*
|
||||
* @memberof Mocha
|
||||
* @public
|
||||
* @class
|
||||
* @param {string} title
|
||||
* @param {Context} parentContext
|
||||
*/
|
||||
function Suite(title, parentContext) {
|
||||
if (!utils.isString(title)) {
|
||||
throw new Error(
|
||||
'Suite `title` should be a "string" but "' +
|
||||
typeof title +
|
||||
'" was given instead.'
|
||||
);
|
||||
}
|
||||
this.title = title;
|
||||
function Context() {}
|
||||
Context.prototype = parentContext;
|
||||
this.ctx = new Context();
|
||||
this.suites = [];
|
||||
this.tests = [];
|
||||
this.pending = false;
|
||||
this._beforeEach = [];
|
||||
this._beforeAll = [];
|
||||
this._afterEach = [];
|
||||
this._afterAll = [];
|
||||
this.root = !title;
|
||||
this._timeout = 2000;
|
||||
this._enableTimeouts = true;
|
||||
this._slow = 75;
|
||||
this._bail = false;
|
||||
this._retries = -1;
|
||||
this._onlyTests = [];
|
||||
this._onlySuites = [];
|
||||
this.delayed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `EventEmitter.prototype`.
|
||||
*/
|
||||
inherits(Suite, EventEmitter);
|
||||
|
||||
/**
|
||||
* Return a clone of this `Suite`.
|
||||
*
|
||||
* @api private
|
||||
* @return {Suite}
|
||||
*/
|
||||
Suite.prototype.clone = function() {
|
||||
var suite = new Suite(this.title);
|
||||
debug('clone');
|
||||
suite.ctx = this.ctx;
|
||||
suite.timeout(this.timeout());
|
||||
suite.retries(this.retries());
|
||||
suite.enableTimeouts(this.enableTimeouts());
|
||||
suite.slow(this.slow());
|
||||
suite.bail(this.bail());
|
||||
return suite;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get timeout `ms` or short-hand such as "2s".
|
||||
*
|
||||
* @api private
|
||||
* @param {number|string} ms
|
||||
* @return {Suite|number} for chaining
|
||||
*/
|
||||
Suite.prototype.timeout = function(ms) {
|
||||
if (!arguments.length) {
|
||||
return this._timeout;
|
||||
}
|
||||
if (ms.toString() === '0') {
|
||||
this._enableTimeouts = false;
|
||||
}
|
||||
if (typeof ms === 'string') {
|
||||
ms = milliseconds(ms);
|
||||
}
|
||||
debug('timeout %d', ms);
|
||||
this._timeout = parseInt(ms, 10);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get number of times to retry a failed test.
|
||||
*
|
||||
* @api private
|
||||
* @param {number|string} n
|
||||
* @return {Suite|number} for chaining
|
||||
*/
|
||||
Suite.prototype.retries = function(n) {
|
||||
if (!arguments.length) {
|
||||
return this._retries;
|
||||
}
|
||||
debug('retries %d', n);
|
||||
this._retries = parseInt(n, 10) || 0;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get timeout to `enabled`.
|
||||
*
|
||||
* @api private
|
||||
* @param {boolean} enabled
|
||||
* @return {Suite|boolean} self or enabled
|
||||
*/
|
||||
Suite.prototype.enableTimeouts = function(enabled) {
|
||||
if (!arguments.length) {
|
||||
return this._enableTimeouts;
|
||||
}
|
||||
debug('enableTimeouts %s', enabled);
|
||||
this._enableTimeouts = enabled;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get slow `ms` or short-hand such as "2s".
|
||||
*
|
||||
* @api private
|
||||
* @param {number|string} ms
|
||||
* @return {Suite|number} for chaining
|
||||
*/
|
||||
Suite.prototype.slow = function(ms) {
|
||||
if (!arguments.length) {
|
||||
return this._slow;
|
||||
}
|
||||
if (typeof ms === 'string') {
|
||||
ms = milliseconds(ms);
|
||||
}
|
||||
debug('slow %d', ms);
|
||||
this._slow = ms;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get whether to bail after first error.
|
||||
*
|
||||
* @api private
|
||||
* @param {boolean} bail
|
||||
* @return {Suite|number} for chaining
|
||||
*/
|
||||
Suite.prototype.bail = function(bail) {
|
||||
if (!arguments.length) {
|
||||
return this._bail;
|
||||
}
|
||||
debug('bail %s', bail);
|
||||
this._bail = bail;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this suite or its parent suite is marked as pending.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Suite.prototype.isPending = function() {
|
||||
return this.pending || (this.parent && this.parent.isPending());
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic hook-creator.
|
||||
* @private
|
||||
* @param {string} title - Title of hook
|
||||
* @param {Function} fn - Hook callback
|
||||
* @returns {Hook} A new hook
|
||||
*/
|
||||
Suite.prototype._createHook = function(title, fn) {
|
||||
var hook = new Hook(title, fn);
|
||||
hook.parent = this;
|
||||
hook.timeout(this.timeout());
|
||||
hook.retries(this.retries());
|
||||
hook.enableTimeouts(this.enableTimeouts());
|
||||
hook.slow(this.slow());
|
||||
hook.ctx = this.ctx;
|
||||
hook.file = this.file;
|
||||
return hook;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run `fn(test[, done])` before running tests.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} title
|
||||
* @param {Function} fn
|
||||
* @return {Suite} for chaining
|
||||
*/
|
||||
Suite.prototype.beforeAll = function(title, fn) {
|
||||
if (this.isPending()) {
|
||||
return this;
|
||||
}
|
||||
if (typeof title === 'function') {
|
||||
fn = title;
|
||||
title = fn.name;
|
||||
}
|
||||
title = '"before all" hook' + (title ? ': ' + title : '');
|
||||
|
||||
var hook = this._createHook(title, fn);
|
||||
this._beforeAll.push(hook);
|
||||
this.emit('beforeAll', hook);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run `fn(test[, done])` after running tests.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} title
|
||||
* @param {Function} fn
|
||||
* @return {Suite} for chaining
|
||||
*/
|
||||
Suite.prototype.afterAll = function(title, fn) {
|
||||
if (this.isPending()) {
|
||||
return this;
|
||||
}
|
||||
if (typeof title === 'function') {
|
||||
fn = title;
|
||||
title = fn.name;
|
||||
}
|
||||
title = '"after all" hook' + (title ? ': ' + title : '');
|
||||
|
||||
var hook = this._createHook(title, fn);
|
||||
this._afterAll.push(hook);
|
||||
this.emit('afterAll', hook);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run `fn(test[, done])` before each test case.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} title
|
||||
* @param {Function} fn
|
||||
* @return {Suite} for chaining
|
||||
*/
|
||||
Suite.prototype.beforeEach = function(title, fn) {
|
||||
if (this.isPending()) {
|
||||
return this;
|
||||
}
|
||||
if (typeof title === 'function') {
|
||||
fn = title;
|
||||
title = fn.name;
|
||||
}
|
||||
title = '"before each" hook' + (title ? ': ' + title : '');
|
||||
|
||||
var hook = this._createHook(title, fn);
|
||||
this._beforeEach.push(hook);
|
||||
this.emit('beforeEach', hook);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run `fn(test[, done])` after each test case.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} title
|
||||
* @param {Function} fn
|
||||
* @return {Suite} for chaining
|
||||
*/
|
||||
Suite.prototype.afterEach = function(title, fn) {
|
||||
if (this.isPending()) {
|
||||
return this;
|
||||
}
|
||||
if (typeof title === 'function') {
|
||||
fn = title;
|
||||
title = fn.name;
|
||||
}
|
||||
title = '"after each" hook' + (title ? ': ' + title : '');
|
||||
|
||||
var hook = this._createHook(title, fn);
|
||||
this._afterEach.push(hook);
|
||||
this.emit('afterEach', hook);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a test `suite`.
|
||||
*
|
||||
* @api private
|
||||
* @param {Suite} suite
|
||||
* @return {Suite} for chaining
|
||||
*/
|
||||
Suite.prototype.addSuite = function(suite) {
|
||||
suite.parent = this;
|
||||
suite.timeout(this.timeout());
|
||||
suite.retries(this.retries());
|
||||
suite.enableTimeouts(this.enableTimeouts());
|
||||
suite.slow(this.slow());
|
||||
suite.bail(this.bail());
|
||||
this.suites.push(suite);
|
||||
this.emit('suite', suite);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a `test` to this suite.
|
||||
*
|
||||
* @api private
|
||||
* @param {Test} test
|
||||
* @return {Suite} for chaining
|
||||
*/
|
||||
Suite.prototype.addTest = function(test) {
|
||||
test.parent = this;
|
||||
test.timeout(this.timeout());
|
||||
test.retries(this.retries());
|
||||
test.enableTimeouts(this.enableTimeouts());
|
||||
test.slow(this.slow());
|
||||
test.ctx = this.ctx;
|
||||
this.tests.push(test);
|
||||
this.emit('test', test);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the full title generated by recursively concatenating the parent's
|
||||
* full title.
|
||||
*
|
||||
* @memberof Mocha.Suite
|
||||
* @public
|
||||
* @api public
|
||||
* @return {string}
|
||||
*/
|
||||
Suite.prototype.fullTitle = function() {
|
||||
return this.titlePath().join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the title path generated by recursively concatenating the parent's
|
||||
* title path.
|
||||
*
|
||||
* @memberof Mocha.Suite
|
||||
* @public
|
||||
* @api public
|
||||
* @return {string}
|
||||
*/
|
||||
Suite.prototype.titlePath = function() {
|
||||
var result = [];
|
||||
if (this.parent) {
|
||||
result = result.concat(this.parent.titlePath());
|
||||
}
|
||||
if (!this.root) {
|
||||
result.push(this.title);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the total number of tests.
|
||||
*
|
||||
* @memberof Mocha.Suite
|
||||
* @public
|
||||
* @api public
|
||||
* @return {number}
|
||||
*/
|
||||
Suite.prototype.total = function() {
|
||||
return (
|
||||
this.suites.reduce(function(sum, suite) {
|
||||
return sum + suite.total();
|
||||
}, 0) + this.tests.length
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates through each suite recursively to find all tests. Applies a
|
||||
* function in the format `fn(test)`.
|
||||
*
|
||||
* @api private
|
||||
* @param {Function} fn
|
||||
* @return {Suite}
|
||||
*/
|
||||
Suite.prototype.eachTest = function(fn) {
|
||||
this.tests.forEach(fn);
|
||||
this.suites.forEach(function(suite) {
|
||||
suite.eachTest(fn);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* This will run the root suite if we happen to be running in delayed mode.
|
||||
*/
|
||||
Suite.prototype.run = function run() {
|
||||
if (this.root) {
|
||||
this.emit('run');
|
||||
}
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mocha</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="mocha.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script src="mocha.js"></script>
|
||||
<script>mocha.setup('bdd');</script>
|
||||
<script src="tests.js"></script>
|
||||
<script>
|
||||
mocha.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
var Runnable = require('./runnable');
|
||||
var utils = require('./utils');
|
||||
var isString = utils.isString;
|
||||
|
||||
module.exports = Test;
|
||||
|
||||
/**
|
||||
* Initialize a new `Test` with the given `title` and callback `fn`.
|
||||
*
|
||||
* @class
|
||||
* @extends Runnable
|
||||
* @param {String} title
|
||||
* @param {Function} fn
|
||||
*/
|
||||
function Test(title, fn) {
|
||||
if (!isString(title)) {
|
||||
throw new Error(
|
||||
'Test `title` should be a "string" but "' +
|
||||
typeof title +
|
||||
'" was given instead.'
|
||||
);
|
||||
}
|
||||
Runnable.call(this, title, fn);
|
||||
this.pending = !fn;
|
||||
this.type = 'test';
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Runnable.prototype`.
|
||||
*/
|
||||
utils.inherits(Test, Runnable);
|
||||
|
||||
Test.prototype.clone = function() {
|
||||
var test = new Test(this.title, this.fn);
|
||||
test.timeout(this.timeout());
|
||||
test.slow(this.slow());
|
||||
test.enableTimeouts(this.enableTimeouts());
|
||||
test.retries(this.retries());
|
||||
test.currentRetry(this.currentRetry());
|
||||
test.globals(this.globals());
|
||||
test.parent = this.parent;
|
||||
test.file = this.file;
|
||||
test.ctx = this.ctx;
|
||||
return test;
|
||||
};
|
||||
+670
@@ -0,0 +1,670 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('mocha:watch');
|
||||
var fs = require('fs');
|
||||
var glob = require('glob');
|
||||
var path = require('path');
|
||||
var join = path.join;
|
||||
var he = require('he');
|
||||
|
||||
/**
|
||||
* Ignored directories.
|
||||
*/
|
||||
|
||||
var ignore = ['node_modules', '.git'];
|
||||
|
||||
exports.inherits = require('util').inherits;
|
||||
|
||||
/**
|
||||
* Escape special characters in the given string of html.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} html
|
||||
* @return {string}
|
||||
*/
|
||||
exports.escape = function(html) {
|
||||
return he.encode(String(html), {useNamedReferences: false});
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if the given obj is type of string.
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
exports.isString = function(obj) {
|
||||
return typeof obj === 'string';
|
||||
};
|
||||
|
||||
/**
|
||||
* Watch the given `files` for changes
|
||||
* and invoke `fn(file)` on modification.
|
||||
*
|
||||
* @api private
|
||||
* @param {Array} files
|
||||
* @param {Function} fn
|
||||
*/
|
||||
exports.watch = function(files, fn) {
|
||||
var options = {interval: 100};
|
||||
files.forEach(function(file) {
|
||||
debug('file %s', file);
|
||||
fs.watchFile(file, options, function(curr, prev) {
|
||||
if (prev.mtime < curr.mtime) {
|
||||
fn(file);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Ignored files.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} path
|
||||
* @return {boolean}
|
||||
*/
|
||||
function ignored(path) {
|
||||
return !~ignore.indexOf(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup files in the given `dir`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} dir
|
||||
* @param {string[]} [ext=['.js']]
|
||||
* @param {Array} [ret=[]]
|
||||
* @return {Array}
|
||||
*/
|
||||
exports.files = function(dir, ext, ret) {
|
||||
ret = ret || [];
|
||||
ext = ext || ['js'];
|
||||
|
||||
var re = new RegExp('\\.(' + ext.join('|') + ')$');
|
||||
|
||||
fs
|
||||
.readdirSync(dir)
|
||||
.filter(ignored)
|
||||
.forEach(function(path) {
|
||||
path = join(dir, path);
|
||||
if (fs.lstatSync(path).isDirectory()) {
|
||||
exports.files(path, ext, ret);
|
||||
} else if (path.match(re)) {
|
||||
ret.push(path);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute a slug from the given `str`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
*/
|
||||
exports.slug = function(str) {
|
||||
return str
|
||||
.toLowerCase()
|
||||
.replace(/ +/g, '-')
|
||||
.replace(/[^-\w]/g, '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip the function definition from `str`, and re-indent for pre whitespace.
|
||||
*
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
*/
|
||||
exports.clean = function(str) {
|
||||
str = str
|
||||
.replace(/\r\n?|[\n\u2028\u2029]/g, '\n')
|
||||
.replace(/^\uFEFF/, '')
|
||||
// (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content
|
||||
.replace(
|
||||
/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/,
|
||||
'$1$2$3'
|
||||
);
|
||||
|
||||
var spaces = str.match(/^\n?( *)/)[1].length;
|
||||
var tabs = str.match(/^\n?(\t*)/)[1].length;
|
||||
var re = new RegExp(
|
||||
'^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs || spaces) + '}',
|
||||
'gm'
|
||||
);
|
||||
|
||||
str = str.replace(re, '');
|
||||
|
||||
return str.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the given `qs`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} qs
|
||||
* @return {Object}
|
||||
*/
|
||||
exports.parseQuery = function(qs) {
|
||||
return qs
|
||||
.replace('?', '')
|
||||
.split('&')
|
||||
.reduce(function(obj, pair) {
|
||||
var i = pair.indexOf('=');
|
||||
var key = pair.slice(0, i);
|
||||
var val = pair.slice(++i);
|
||||
|
||||
// Due to how the URLSearchParams API treats spaces
|
||||
obj[key] = decodeURIComponent(val.replace(/\+/g, '%20'));
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlight the given string of `js`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} js
|
||||
* @return {string}
|
||||
*/
|
||||
function highlight(js) {
|
||||
return js
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
|
||||
.replace(/('.*?')/gm, '<span class="string">$1</span>')
|
||||
.replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
|
||||
.replace(/(\d+)/gm, '<span class="number">$1</span>')
|
||||
.replace(
|
||||
/\bnew[ \t]+(\w+)/gm,
|
||||
'<span class="keyword">new</span> <span class="init">$1</span>'
|
||||
)
|
||||
.replace(
|
||||
/\b(function|new|throw|return|var|if|else)\b/gm,
|
||||
'<span class="keyword">$1</span>'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight the contents of tag `name`.
|
||||
*
|
||||
* @api private
|
||||
* @param {string} name
|
||||
*/
|
||||
exports.highlightTags = function(name) {
|
||||
var code = document.getElementById('mocha').getElementsByTagName(name);
|
||||
for (var i = 0, len = code.length; i < len; ++i) {
|
||||
code[i].innerHTML = highlight(code[i].innerHTML);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If a value could have properties, and has none, this function is called,
|
||||
* which returns a string representation of the empty value.
|
||||
*
|
||||
* Functions w/ no properties return `'[Function]'`
|
||||
* Arrays w/ length === 0 return `'[]'`
|
||||
* Objects w/ no properties return `'{}'`
|
||||
* All else: return result of `value.toString()`
|
||||
*
|
||||
* @api private
|
||||
* @param {*} value The value to inspect.
|
||||
* @param {string} typeHint The type of the value
|
||||
* @returns {string}
|
||||
*/
|
||||
function emptyRepresentation(value, typeHint) {
|
||||
switch (typeHint) {
|
||||
case 'function':
|
||||
return '[Function]';
|
||||
case 'object':
|
||||
return '{}';
|
||||
case 'array':
|
||||
return '[]';
|
||||
default:
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes some variable and asks `Object.prototype.toString()` what it thinks it
|
||||
* is.
|
||||
*
|
||||
* @api private
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
|
||||
* @param {*} value The value to test.
|
||||
* @returns {string} Computed type
|
||||
* @example
|
||||
* type({}) // 'object'
|
||||
* type([]) // 'array'
|
||||
* type(1) // 'number'
|
||||
* type(false) // 'boolean'
|
||||
* type(Infinity) // 'number'
|
||||
* type(null) // 'null'
|
||||
* type(new Date()) // 'date'
|
||||
* type(/foo/) // 'regexp'
|
||||
* type('type') // 'string'
|
||||
* type(global) // 'global'
|
||||
* type(new String('foo') // 'object'
|
||||
*/
|
||||
var type = (exports.type = function type(value) {
|
||||
if (value === undefined) {
|
||||
return 'undefined';
|
||||
} else if (value === null) {
|
||||
return 'null';
|
||||
} else if (Buffer.isBuffer(value)) {
|
||||
return 'buffer';
|
||||
}
|
||||
return Object.prototype.toString
|
||||
.call(value)
|
||||
.replace(/^\[.+\s(.+?)]$/, '$1')
|
||||
.toLowerCase();
|
||||
});
|
||||
|
||||
/**
|
||||
* Stringify `value`. Different behavior depending on type of value:
|
||||
*
|
||||
* - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
|
||||
* - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
|
||||
* - If `value` is an *empty* object, function, or array, return result of function
|
||||
* {@link emptyRepresentation}.
|
||||
* - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
|
||||
* JSON.stringify().
|
||||
*
|
||||
* @api private
|
||||
* @see exports.type
|
||||
* @param {*} value
|
||||
* @return {string}
|
||||
*/
|
||||
exports.stringify = function(value) {
|
||||
var typeHint = type(value);
|
||||
|
||||
if (!~['object', 'array', 'function'].indexOf(typeHint)) {
|
||||
if (typeHint === 'buffer') {
|
||||
var json = Buffer.prototype.toJSON.call(value);
|
||||
// Based on the toJSON result
|
||||
return jsonStringify(
|
||||
json.data && json.type ? json.data : json,
|
||||
2
|
||||
).replace(/,(\n|$)/g, '$1');
|
||||
}
|
||||
|
||||
// IE7/IE8 has a bizarre String constructor; needs to be coerced
|
||||
// into an array and back to obj.
|
||||
if (typeHint === 'string' && typeof value === 'object') {
|
||||
value = value.split('').reduce(function(acc, char, idx) {
|
||||
acc[idx] = char;
|
||||
return acc;
|
||||
}, {});
|
||||
typeHint = 'object';
|
||||
} else {
|
||||
return jsonStringify(value);
|
||||
}
|
||||
}
|
||||
|
||||
for (var prop in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, prop)) {
|
||||
return jsonStringify(
|
||||
exports.canonicalize(value, null, typeHint),
|
||||
2
|
||||
).replace(/,(\n|$)/g, '$1');
|
||||
}
|
||||
}
|
||||
|
||||
return emptyRepresentation(value, typeHint);
|
||||
};
|
||||
|
||||
/**
|
||||
* like JSON.stringify but more sense.
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} object
|
||||
* @param {number=} spaces
|
||||
* @param {number=} depth
|
||||
* @returns {*}
|
||||
*/
|
||||
function jsonStringify(object, spaces, depth) {
|
||||
if (typeof spaces === 'undefined') {
|
||||
// primitive types
|
||||
return _stringify(object);
|
||||
}
|
||||
|
||||
depth = depth || 1;
|
||||
var space = spaces * depth;
|
||||
var str = Array.isArray(object) ? '[' : '{';
|
||||
var end = Array.isArray(object) ? ']' : '}';
|
||||
var length =
|
||||
typeof object.length === 'number'
|
||||
? object.length
|
||||
: Object.keys(object).length;
|
||||
// `.repeat()` polyfill
|
||||
function repeat(s, n) {
|
||||
return new Array(n).join(s);
|
||||
}
|
||||
|
||||
function _stringify(val) {
|
||||
switch (type(val)) {
|
||||
case 'null':
|
||||
case 'undefined':
|
||||
val = '[' + val + ']';
|
||||
break;
|
||||
case 'array':
|
||||
case 'object':
|
||||
val = jsonStringify(val, spaces, depth + 1);
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'regexp':
|
||||
case 'symbol':
|
||||
case 'number':
|
||||
val =
|
||||
val === 0 && 1 / val === -Infinity // `-0`
|
||||
? '-0'
|
||||
: val.toString();
|
||||
break;
|
||||
case 'date':
|
||||
var sDate = isNaN(val.getTime()) ? val.toString() : val.toISOString();
|
||||
val = '[Date: ' + sDate + ']';
|
||||
break;
|
||||
case 'buffer':
|
||||
var json = val.toJSON();
|
||||
// Based on the toJSON result
|
||||
json = json.data && json.type ? json.data : json;
|
||||
val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']';
|
||||
break;
|
||||
default:
|
||||
val =
|
||||
val === '[Function]' || val === '[Circular]'
|
||||
? val
|
||||
: JSON.stringify(val); // string
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
for (var i in object) {
|
||||
if (!Object.prototype.hasOwnProperty.call(object, i)) {
|
||||
continue; // not my business
|
||||
}
|
||||
--length;
|
||||
str +=
|
||||
'\n ' +
|
||||
repeat(' ', space) +
|
||||
(Array.isArray(object) ? '' : '"' + i + '": ') + // key
|
||||
_stringify(object[i]) + // value
|
||||
(length ? ',' : ''); // comma
|
||||
}
|
||||
|
||||
return (
|
||||
str +
|
||||
// [], {}
|
||||
(str.length !== 1 ? '\n' + repeat(' ', --space) + end : end)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new Thing that has the keys in sorted order. Recursive.
|
||||
*
|
||||
* If the Thing...
|
||||
* - has already been seen, return string `'[Circular]'`
|
||||
* - is `undefined`, return string `'[undefined]'`
|
||||
* - is `null`, return value `null`
|
||||
* - is some other primitive, return the value
|
||||
* - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
|
||||
* - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
|
||||
* - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
|
||||
*
|
||||
* @api private
|
||||
* @see {@link exports.stringify}
|
||||
* @param {*} value Thing to inspect. May or may not have properties.
|
||||
* @param {Array} [stack=[]] Stack of seen values
|
||||
* @param {string} [typeHint] Type hint
|
||||
* @return {(Object|Array|Function|string|undefined)}
|
||||
*/
|
||||
exports.canonicalize = function canonicalize(value, stack, typeHint) {
|
||||
var canonicalizedObj;
|
||||
/* eslint-disable no-unused-vars */
|
||||
var prop;
|
||||
/* eslint-enable no-unused-vars */
|
||||
typeHint = typeHint || type(value);
|
||||
function withStack(value, fn) {
|
||||
stack.push(value);
|
||||
fn();
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
stack = stack || [];
|
||||
|
||||
if (stack.indexOf(value) !== -1) {
|
||||
return '[Circular]';
|
||||
}
|
||||
|
||||
switch (typeHint) {
|
||||
case 'undefined':
|
||||
case 'buffer':
|
||||
case 'null':
|
||||
canonicalizedObj = value;
|
||||
break;
|
||||
case 'array':
|
||||
withStack(value, function() {
|
||||
canonicalizedObj = value.map(function(item) {
|
||||
return exports.canonicalize(item, stack);
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'function':
|
||||
/* eslint-disable guard-for-in */
|
||||
for (prop in value) {
|
||||
canonicalizedObj = {};
|
||||
break;
|
||||
}
|
||||
/* eslint-enable guard-for-in */
|
||||
if (!canonicalizedObj) {
|
||||
canonicalizedObj = emptyRepresentation(value, typeHint);
|
||||
break;
|
||||
}
|
||||
/* falls through */
|
||||
case 'object':
|
||||
canonicalizedObj = canonicalizedObj || {};
|
||||
withStack(value, function() {
|
||||
Object.keys(value)
|
||||
.sort()
|
||||
.forEach(function(key) {
|
||||
canonicalizedObj[key] = exports.canonicalize(value[key], stack);
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'date':
|
||||
case 'number':
|
||||
case 'regexp':
|
||||
case 'boolean':
|
||||
case 'symbol':
|
||||
canonicalizedObj = value;
|
||||
break;
|
||||
default:
|
||||
canonicalizedObj = value + '';
|
||||
}
|
||||
|
||||
return canonicalizedObj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup file names at the given `path`.
|
||||
*
|
||||
* @memberof Mocha.utils
|
||||
* @public
|
||||
* @api public
|
||||
* @param {string} filepath Base path to start searching from.
|
||||
* @param {string[]} extensions File extensions to look for.
|
||||
* @param {boolean} recursive Whether or not to recurse into subdirectories.
|
||||
* @return {string[]} An array of paths.
|
||||
*/
|
||||
exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
|
||||
var files = [];
|
||||
|
||||
if (!fs.existsSync(filepath)) {
|
||||
if (fs.existsSync(filepath + '.js')) {
|
||||
filepath += '.js';
|
||||
} else {
|
||||
files = glob.sync(filepath);
|
||||
if (!files.length) {
|
||||
throw new Error("cannot resolve path (or pattern) '" + filepath + "'");
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
var stat = fs.statSync(filepath);
|
||||
if (stat.isFile()) {
|
||||
return filepath;
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore error
|
||||
return;
|
||||
}
|
||||
|
||||
fs.readdirSync(filepath).forEach(function(file) {
|
||||
file = path.join(filepath, file);
|
||||
try {
|
||||
var stat = fs.statSync(file);
|
||||
if (stat.isDirectory()) {
|
||||
if (recursive) {
|
||||
files = files.concat(lookupFiles(file, extensions, recursive));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore error
|
||||
return;
|
||||
}
|
||||
if (!extensions) {
|
||||
throw new Error(
|
||||
'extensions parameter required when filepath is a directory'
|
||||
);
|
||||
}
|
||||
var re = new RegExp('\\.(?:' + extensions.join('|') + ')$');
|
||||
if (!stat.isFile() || !re.test(file) || path.basename(file)[0] === '.') {
|
||||
return;
|
||||
}
|
||||
files.push(file);
|
||||
});
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate an undefined error with a message warning the user.
|
||||
*
|
||||
* @return {Error}
|
||||
*/
|
||||
|
||||
exports.undefinedError = function() {
|
||||
return new Error(
|
||||
'Caught undefined error, did you throw without specifying what?'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate an undefined error if `err` is not defined.
|
||||
*
|
||||
* @param {Error} err
|
||||
* @return {Error}
|
||||
*/
|
||||
|
||||
exports.getError = function(err) {
|
||||
return err || exports.undefinedError();
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary
|
||||
* This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
|
||||
* @description
|
||||
* When invoking this function you get a filter function that get the Error.stack as an input,
|
||||
* and return a prettify output.
|
||||
* (i.e: strip Mocha and internal node functions from stack trace).
|
||||
* @returns {Function}
|
||||
*/
|
||||
exports.stackTraceFilter = function() {
|
||||
// TODO: Replace with `process.browser`
|
||||
var is = typeof document === 'undefined' ? {node: true} : {browser: true};
|
||||
var slash = path.sep;
|
||||
var cwd;
|
||||
if (is.node) {
|
||||
cwd = process.cwd() + slash;
|
||||
} else {
|
||||
cwd = (typeof location === 'undefined'
|
||||
? window.location
|
||||
: location
|
||||
).href.replace(/\/[^/]*$/, '/');
|
||||
slash = '/';
|
||||
}
|
||||
|
||||
function isMochaInternal(line) {
|
||||
return (
|
||||
~line.indexOf('node_modules' + slash + 'mocha' + slash) ||
|
||||
~line.indexOf('node_modules' + slash + 'mocha.js') ||
|
||||
~line.indexOf('bower_components' + slash + 'mocha.js') ||
|
||||
~line.indexOf(slash + 'mocha.js')
|
||||
);
|
||||
}
|
||||
|
||||
function isNodeInternal(line) {
|
||||
return (
|
||||
~line.indexOf('(timers.js:') ||
|
||||
~line.indexOf('(events.js:') ||
|
||||
~line.indexOf('(node.js:') ||
|
||||
~line.indexOf('(module.js:') ||
|
||||
~line.indexOf('GeneratorFunctionPrototype.next (native)') ||
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
return function(stack) {
|
||||
stack = stack.split('\n');
|
||||
|
||||
stack = stack.reduce(function(list, line) {
|
||||
if (isMochaInternal(line)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
if (is.node && isNodeInternal(line)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// Clean up cwd(absolute)
|
||||
if (/\(?.+:\d+:\d+\)?$/.test(line)) {
|
||||
line = line.replace('(' + cwd, '(');
|
||||
}
|
||||
|
||||
list.push(line);
|
||||
return list;
|
||||
}, []);
|
||||
|
||||
return stack.join('\n');
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Crude, but effective.
|
||||
* @api
|
||||
* @param {*} value
|
||||
* @returns {boolean} Whether or not `value` is a Promise
|
||||
*/
|
||||
exports.isPromise = function isPromise(value) {
|
||||
return typeof value === 'object' && typeof value.then === 'function';
|
||||
};
|
||||
|
||||
/**
|
||||
* It's a noop.
|
||||
* @api
|
||||
*/
|
||||
exports.noop = function() {};
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
@charset "utf-8";
|
||||
|
||||
body {
|
||||
margin:0;
|
||||
}
|
||||
|
||||
#mocha {
|
||||
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin: 60px 50px;
|
||||
}
|
||||
|
||||
#mocha ul,
|
||||
#mocha li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#mocha ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#mocha h1,
|
||||
#mocha h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#mocha h1 {
|
||||
margin-top: 15px;
|
||||
font-size: 1em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
#mocha h1 a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#mocha h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#mocha .suite .suite h1 {
|
||||
margin-top: 0;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
#mocha .hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#mocha .suite {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#mocha .test {
|
||||
margin-left: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#mocha .test.pending:hover h2::after {
|
||||
content: '(pending)';
|
||||
font-family: arial, sans-serif;
|
||||
}
|
||||
|
||||
#mocha .test.pass.medium .duration {
|
||||
background: #c09853;
|
||||
}
|
||||
|
||||
#mocha .test.pass.slow .duration {
|
||||
background: #b94a48;
|
||||
}
|
||||
|
||||
#mocha .test.pass::before {
|
||||
content: '✓';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #00d6b2;
|
||||
}
|
||||
|
||||
#mocha .test.pass .duration {
|
||||
font-size: 9px;
|
||||
margin-left: 5px;
|
||||
padding: 2px 5px;
|
||||
color: #fff;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-ms-border-radius: 5px;
|
||||
-o-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#mocha .test.pass.fast .duration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha .test.pending {
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.pending::before {
|
||||
content: '◦';
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.fail {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test.fail pre {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#mocha .test.fail::before {
|
||||
content: '✖';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test pre.error {
|
||||
color: #c00;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#mocha .test .html-error {
|
||||
overflow: auto;
|
||||
color: black;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
float: left;
|
||||
clear: left;
|
||||
font: 12px/1.5 monaco, monospace;
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
max-width: 85%; /*(1)*/
|
||||
max-width: -webkit-calc(100% - 42px);
|
||||
max-width: -moz-calc(100% - 42px);
|
||||
max-width: calc(100% - 42px); /*(2)*/
|
||||
max-height: 300px;
|
||||
word-wrap: break-word;
|
||||
border-bottom-color: #ddd;
|
||||
-webkit-box-shadow: 0 1px 3px #eee;
|
||||
-moz-box-shadow: 0 1px 3px #eee;
|
||||
box-shadow: 0 1px 3px #eee;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#mocha .test .html-error pre.error {
|
||||
border: none;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: 0;
|
||||
-moz-box-shadow: 0;
|
||||
box-shadow: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 18px;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* (1): approximate for browsers not supporting calc
|
||||
* (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
|
||||
* ^^ seriously
|
||||
*/
|
||||
#mocha .test pre {
|
||||
display: block;
|
||||
float: left;
|
||||
clear: left;
|
||||
font: 12px/1.5 monaco, monospace;
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
max-width: 85%; /*(1)*/
|
||||
max-width: -webkit-calc(100% - 42px);
|
||||
max-width: -moz-calc(100% - 42px);
|
||||
max-width: calc(100% - 42px); /*(2)*/
|
||||
word-wrap: break-word;
|
||||
border-bottom-color: #ddd;
|
||||
-webkit-box-shadow: 0 1px 3px #eee;
|
||||
-moz-box-shadow: 0 1px 3px #eee;
|
||||
box-shadow: 0 1px 3px #eee;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#mocha .test h2 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#mocha .test a.replay {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 0;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
background: #eee;
|
||||
font-size: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
-moz-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
-webkit-transition:opacity 200ms;
|
||||
-moz-transition:opacity 200ms;
|
||||
-o-transition:opacity 200ms;
|
||||
transition: opacity 200ms;
|
||||
opacity: 0.3;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#mocha .test:hover a.replay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#mocha-report.pass .test.fail {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha-report.fail .test.pass {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha-report.pending .test.pass,
|
||||
#mocha-report.pending .test.fail {
|
||||
display: none;
|
||||
}
|
||||
#mocha-report.pending .test.pass.pending {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#mocha-error {
|
||||
color: #c00;
|
||||
font-size: 1.5em;
|
||||
font-weight: 100;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
#mocha-stats {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
color: #888;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#mocha-stats .progress {
|
||||
float: right;
|
||||
padding-top: 0;
|
||||
|
||||
/**
|
||||
* Set safe initial values, so mochas .progress does not inherit these
|
||||
* properties from Bootstrap .progress (which causes .progress height to
|
||||
* equal line height set in Bootstrap).
|
||||
*/
|
||||
height: auto;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
background-color: initial;
|
||||
}
|
||||
|
||||
#mocha-stats em {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#mocha-stats a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#mocha-stats a:hover {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#mocha-stats li {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
list-style: none;
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
#mocha-stats canvas {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#mocha code .comment { color: #ddd; }
|
||||
#mocha code .init { color: #2f6fad; }
|
||||
#mocha code .string { color: #5890ad; }
|
||||
#mocha code .keyword { color: #8a6343; }
|
||||
#mocha code .number { color: #2f6fad; }
|
||||
|
||||
@media screen and (max-device-width: 480px) {
|
||||
#mocha {
|
||||
margin: 60px 0px;
|
||||
}
|
||||
|
||||
#mocha #stats {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
+16046
File diff suppressed because it is too large.
Load diff
+1848
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user