diff --git a/.eslintrc.yaml b/.eslintrc.yaml new file mode 100644 index 0000000000..d5673232e7 --- /dev/null +++ b/.eslintrc.yaml @@ -0,0 +1,15 @@ +root: true +env: + node: true + browser: true +parserOptions: + ecmaVersion: 5 + sourceType: script +extends: semistandard +rules: + strict: + - error + - safe + linebreak-style: + - error + - unix diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..4e63d0ac3d --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,51 @@ +# Mocha Code of Conduct + +## Be friendly and patient +We understand that everyone has different levels of experience or knowledge in many diverse fields, be it technical or +non-technical in nature. We also have areas of knowledge we are eager to expand; we want to be a community where people +can not only contribute, but feel comfortable to ask questions as well and learn along the way. If someone says something +wrong, or says something accidentally offensive, respond with patience and try to keep it polite and civil. Remember that +we all were newbies at one point. + +## Be welcoming +We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not +limited to, members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, +educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, +religion, and mental and physical ability. + +## Be considerate +Your work will be used by other people, and you in turn will depend on the work of others. Any decision you make will affect +users and colleagues, and you should take those consequences into account when making decisions. Remember that we’re a world-wide +community, so you might not be communicating in someone else’s primary language. + +## Be respectful +Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all +experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important +to remember that a community where people feel uncomfortable or threatened is not a productive one. Members of the JS Foundation +community should be respectful when dealing with other members as well as with people outside the JS Foundation community. + +## Be careful in the words that you choose +We are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put +down other participants. Harassment and other exclusionary behavior aren’t acceptable. This includes, but is not limited to: + +* Violent threats or language directed against another person. +* Discriminatory jokes and language. +* Posting sexually explicit or violent material. +* Posting (or threatening to post) other people’s personally identifying information (“doxing”). +* Personal insults, especially those using racist or sexist terms. +* Unwelcome sexual attention. +* Advocating for, or encouraging, any of the above behavior. +* Repeated harassment of others. In general, if someone asks you to stop, then stop. + +## When we disagree, try to understand why +Disagreements, both social and technical, happen all the time and JS Foundation projects are no exception. It is important +that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of the JS +Foundation comes from its varied community, people from a wide range of backgrounds. Different people have different +perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t +forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues +and learning from mistakes. + +Original text courtesy of the Speak Up! project and Django Project. + +## QUESTIONS? +If you have questions, please see the FAQ. If that doesn’t answer your questions, feel free to email conduct@js.foundation. diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..748e2845f7 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,39 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 120 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 14 +# Issues with these labels will never be considered stale +exemptLabels: + - pr-please + - confirmed + - future + - bug + - chore + - feature + - unconfirmed + - usability + - to-merge + - browser + - reporter + - feature + - documentation + - nice-to-have + - needs-review + - qa + - usability + - semver-minor + - semver-major + - semver-patch + - reporter + - common-mistake +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + I am a bot that watches issues for inactivity. + + This issue hasn't had any recent activity, and I'm labeling it `stale`. In 14 days, if there are no further comments or activity, I will close this issue. + + Thanks for contributing to Mocha! +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.gitignore b/.gitignore index 9b94c5bee0..da8160085d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ npm-debug.log* .nyc_output/ coverage/ BUILDTMP/ +yarn.lock +package-lock.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..b15cbc2c02 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +package-lock=false + diff --git a/.travis.yml b/.travis.yml index 3f03be785a..8a76015bdd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,10 @@ env: matrix: fast_finish: true include: - - node_js: '7' + - node_js: '8' env: TARGET=test-node COVERAGE=true + - node_js: '7' + env: TARGET=test-node - node_js: '6' env: TARGET=test-node - node_js: '5' @@ -31,37 +33,37 @@ matrix: env: TARGET=test-node - node_js: '0.10' env: TARGET=test-node - - node_js: '7' + - node_js: '8' env: TARGET=lint # phantomjs - - node_js: '7' + - node_js: '8' env: TARGET=test-browser # chrome - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="chrome@latest" PLATFORM="Windows 8" # edge - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="MicrosoftEdge@latest" PLATFORM="Windows 10" # ie11 - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="internet explorer@11.0" PLATFORM="Windows 8.1" # ie10 - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="internet explorer@10.0" PLATFORM="Windows 8" # ie9 - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="internet explorer@9.0" PLATFORM="Windows 7" # ie8 - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="internet explorer@8.0" PLATFORM="Windows 7" # ie7 - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="internet explorer@7.0" PLATFORM="Windows XP" # firefox - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="firefox@latest" PLATFORM="Windows 8.1" # safari - - node_js: '7' + - node_js: '8' env: TARGET=test-browser BROWSER="safari@latest" PLATFORM="OS X 10.11" before_install: scripts/travis-before-install.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index b659a4efa8..295971edde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,150 @@ +# 3.4.2 / 2017-05-24 + +## :bug: Fixes + +- [#2802]: Remove call to deprecated os.tmpDir ([@makepanic]) +- [#2820]: Eagerly set process.exitCode ([@chrisleck]) + +## :nut_and_bolt: Other + +- [#2778]: Move linting into an npm script ([@Munter]) + +[@chrisleck]: https://github.com/chrisleck +[@makepanic]: https://github.com/makepanic +[@Munter]: https://github.com/Munter + +[#2778]: https://github.com/mochajs/mocha/pulls/2778 +[#2802]: https://github.com/mochajs/mocha/issues/2802 +[#2820]: https://github.com/mochajs/mocha/pull/2820 + +# 3.4.1 / 2017-05-14 + +Fixed a publishing mishap with git's autocrlf settings. + +# 3.4.0 / 2017-05-14 + +Mocha is now moving to a quicker release schedule: when non-breaking changes are merged, a release should happen that week. + +This week's highlights: + +- `allowUncaught` added to commandline as `--allow-uncaught` (and bugfixed) +- warning-related Node flags + +## :tada: Enhancements + +- [#2793], [#2697]: add --allowUncaught to Node.js ([@lrowe]) +- [#2733]: Add `--no-warnings` and `--trace-warnings` flags ([@sonicdoe]) + +## :bug: Fixes + +- [#2793], [#2697]: fix broken allowUncaught ([@lrowe]) + +## :nut_and_bolt: Other + +- [#2778]: Add license report and scan status ([@xizhao]) +- [#2794]: no special case for macOS running Karma locally ([@boneskull]) +- [#2795]: reverts use of semistandard directly ([#2648]) ([@boneskull]) + +[@lrowe]: https://github.com/lrowe +[@sonicdoe]: https://github.com/sonicdoe +[@xizhao]: https://github.com/xizhao +[@boneskull]: https://github.com/boneskull + +[#2795]: https://github.com/mochajs/mocha/pull/2795 +[#2733]: https://github.com/mochajs/mocha/pull/2733 +[#2793]: https://github.com/mochajs/mocha/pull/2793 +[#2697]: https://github.com/mochajs/mocha/pull/2697 +[#2778]: https://github.com/mochajs/mocha/pull/2778 +[#2794]: https://github.com/mochajs/mocha/pull/2794 + +# 3.3.0 / 2017-04-24 + +Thanks to all our contributors, maintainers, sponsors, and users! ❤️ + +As highlights: + +- We've got coverage now! +- Testing is looking less flaky \o/. +- No more nitpicking about "mocha.js" build on PRs. + +## :tada: Enhancements + +- [#2659]: Adds support for loading reporter from an absolute or relative path ([@sul4bh]) +- [#2769]: Support `--inspect-brk` on command-line ([@igwejk]) + +## :bug: Fixes + +- [#2662]: Replace unicode chars w/ hex codes in HTML reporter ([@rotemdan]) + +## :mag: Coverage + +- [#2672]: Add coverage for node tests ([@c089], [@Munter]) +- [#2680]: Increase tests coverage for base reporter ([@epallerols]) +- [#2690]: Increase tests coverage for doc reporter ([@craigtaub]) +- [#2701]: Increase tests coverage for landing, min, tap and list reporters ([@craigtaub]) +- [#2691]: Increase tests coverage for spec + dot reporters ([@craigtaub]) +- [#2698]: Increase tests coverage for xunit reporter ([@craigtaub]) +- [#2699]: Increase tests coverage for json-stream, markdown and progress reporters ([@craigtaub]) +- [#2703]: Cover .some() function in utils.js with tests ([@seppevs]) +- [#2773]: Add tests for loading reporters w/ relative/absolute paths ([@sul4bh]) + +## :nut_and_bolt: Other + +- Remove bin/.eslintrc; ensure execs are linted ([@boneskull]) +- [#2542]: Expand CONTRIBUTING.md ([@boneskull]) +- [#2660]: Double timeouts on integration tests ([@Munter]) +- [#2653]: Update copyright year ([@Scottkao85], [@Munter]) +- [#2621]: Update dependencies to enable Greenkeeper ([@boneskull], [@greenkeeper]) +- [#2625]: Use trusty container in travis-ci; use "artifacts" addon ([@boneskull]) +- [#2670]: doc(CONTRIBUTING): fix link to org members ([@coderbyheart]) +- Add Mocha propaganda to README.md ([@boneskull]) +- [#2470]: Avoid test flake in "delay" test ([@boneskull]) +- [#2675]: Limit browser concurrency on sauce ([@boneskull]) +- [#2669]: Use temporary test-only build of mocha.js for browsers tests ([@Munter]) +- Fix "projects" link in README.md ([@boneskull]) +- [#2678]: Chore(Saucelabs): test on IE9, IE10 and IE11 ([@coderbyheart]) +- [#2648]: Use `semistandard` directly ([@kt3k]) +- [#2727]: Make the build reproducible ([@lamby]) + +[@boneskull]: https://github.com/boneskull +[@c089]: https://github.com/c089 +[@coderbyheart]: https://github.com/coderbyheart +[@craigtaub]: https://github.com/craigtaub +[@epallerols]: https://github.com/epallerols +[@greenkeeper]: https://github.com/greenkeeper +[@igwejk]: https://github.com/igwejk +[@kt3k]: https://github.com/kt3k +[@lamby]: https://github.com/lamby +[@Munter]: https://github.com/Munter +[@rotemdan]: https://github.com/rotemdan +[@seppevs]: https://github.com/seppevs +[@sul4bh]: https://github.com/sul4bh + +[#2470]: https://github.com/mochajs/mocha/pull/2470 +[#2542]: https://github.com/mochajs/mocha/issues/2542 +[#2621]: https://github.com/mochajs/mocha/pull/2621 +[#2625]: https://github.com/mochajs/mocha/pull/2625 +[#2648]: https://github.com/mochajs/mocha/pull/2648 +[#2653]: https://github.com/mochajs/mocha/pull/2653 +[#2659]: https://github.com/mochajs/mocha/pull/2659 +[#2660]: https://github.com/mochajs/mocha/pull/2660 +[#2662]: https://github.com/mochajs/mocha/pull/2662 +[#2669]: https://github.com/mochajs/mocha/pull/2669 +[#2670]: https://github.com/mochajs/mocha/pull/2670 +[#2672]: https://github.com/mochajs/mocha/pull/2672 +[#2675]: https://github.com/mochajs/mocha/pull/2675 +[#2678]: https://github.com/mochajs/mocha/pull/2678 +[#2680]: https://github.com/mochajs/mocha/pull/2680 +[#2690]: https://github.com/mochajs/mocha/pull/2690 +[#2691]: https://github.com/mochajs/mocha/pull/2691 +[#2698]: https://github.com/mochajs/mocha/pull/2698 +[#2699]: https://github.com/mochajs/mocha/pull/2699 +[#2701]: https://github.com/mochajs/mocha/pull/2701 +[#2703]: https://github.com/mochajs/mocha/pull/2703 +[#2727]: https://github.com/mochajs/mocha/pull/2727 +[#2769]: https://github.com/mochajs/mocha/pull/2769 +[#2773]: https://github.com/mochajs/mocha/pull/2773 + # 3.2.0 / 2016-11-24 ## :newspaper: News diff --git a/Makefile b/Makefile index 7b8ae94cb4..2fa367e8ff 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ BROWSERIFY := "node_modules/.bin/browserify" -SEMISTANDARD:= "node_modules/.bin/semistandard" KARMA := "node_modules/.bin/karma" MOCHA := "bin/mocha" NYC := "node_modules/.bin/nyc" @@ -34,9 +33,7 @@ clean: lint: @printf "==> [Test :: Lint]\n" - $(SEMISTANDARD) $(SRC) - $(SEMISTANDARD) --env mocha --global assert --global expect --global run $(TESTS) - $(SEMISTANDARD) bin/* scripts/*.js *.js + npm run lint test-node: test-bdd test-tdd test-qunit test-exports test-unit test-integration test-jsapi test-compilers test-glob test-requires test-reporters test-only test-global-only @@ -66,79 +63,79 @@ test-jsapi: test-unit: @printf "==> [Test :: Unit]\n" - $(call test_node,unit) test/acceptance/*.js \ - --growl \ - test/*.js + $(call test_node,unit) test/unit/*.spec.js \ + test/node-unit/*.spec.js \ + --growl test-integration: @printf "==> [Test :: Integrations]\n" $(call test_node,integration) --timeout 5000 \ - test/integration/*.js + test/integration/*.spec.js test-compilers: @printf "==> [Test :: Compilers]\n" $(call test_node,compilers) --compilers coffee:coffee-script/register,foo:./test/compiler/foo \ - test/acceptance/test.coffee \ - test/acceptance/test.foo + test/compiler/test.coffee \ + test/compiler/test.foo test-requires: @printf "==> [Test :: Requires]\n" $(call test_node,requires) --compilers coffee:coffee-script/register \ - --require test/acceptance/require/a.js \ - --require test/acceptance/require/b.coffee \ - --require test/acceptance/require/c.js \ - --require test/acceptance/require/d.coffee \ - test/acceptance/require/require.spec.js + --require test/require/a.js \ + --require test/require/b.coffee \ + --require test/require/c.js \ + --require test/require/d.coffee \ + test/require/require.spec.js test-bdd: @printf "==> [Test :: BDD]\n" $(call test_node,bdd) --ui bdd \ - test/acceptance/interfaces/bdd.spec + test/interfaces/bdd.spec test-tdd: @printf "==> [Test :: TDD]\n" $(call test_node,tdd) --ui tdd \ - test/acceptance/interfaces/tdd.spec + test/interfaces/tdd.spec test-qunit: @printf "==> [Test :: QUnit]\n" $(call test_node,qunit) --ui qunit \ - test/acceptance/interfaces/qunit.spec + test/interfaces/qunit.spec test-exports: @printf "==> [Test :: Exports]\n" $(call test_node,exports) --ui exports \ - test/acceptance/interfaces/exports.spec + test/interfaces/exports.spec test-glob: @printf "==> [Test :: Glob]\n" - bash ./test/acceptance/glob/glob.sh + bash ./test/glob/glob.sh test-reporters: @printf "==> [Test :: Reporters]\n" - $(call test_node,reporters) test/reporters/*.js + $(call test_node,reporters) test/reporters/*.spec.js test-only: @printf "==> [Test :: Only]\n" $(call test_node,only-tdd) --ui tdd \ - test/acceptance/misc/only/tdd.spec + test/misc/only/tdd.spec $(call test_node,only-bdd) --ui bdd \ - test/acceptance/misc/only/bdd.spec + test/misc/only/bdd.spec $(call test_node,only-bdd-require) --ui qunit \ - test/acceptance/misc/only/bdd-require.spec + test/misc/only/bdd-require.spec test-global-only: @printf "==> [Test :: Global Only]\n" $(call test_node,global-only-tdd) --ui tdd \ - test/acceptance/misc/only/global/tdd.spec + test/misc/only/global/tdd.spec $(call test_node,global-only-bdd) --ui bdd \ - test/acceptance/misc/only/global/bdd.spec + test/misc/only/global/bdd.spec $(call test_node,global-only-qunit) --ui qunit \ - test/acceptance/misc/only/global/qunit.spec + test/misc/only/global/qunit.spec test-mocha: @printf "==> [Test :: Mocha]\n" @@ -147,19 +144,19 @@ test-mocha: non-tty: @printf "==> [Test :: Non-TTY]\n" $(call test_node,non-tty-dot) --reporter dot \ - test/acceptance/interfaces/bdd.spec 2>&1 > /tmp/dot.out + test/interfaces/bdd.spec 2>&1 > /tmp/dot.out @echo dot: @cat /tmp/dot.out $(call test_node,non-tty-list) --reporter list \ - test/acceptance/interfaces/bdd.spec 2>&1 > /tmp/list.out + test/interfaces/bdd.spec 2>&1 > /tmp/list.out @echo list: @cat /tmp/list.out $(call test_node,non-tty-spec) --reporter spec \ - test/acceptance/interfaces/bdd.spec 2>&1 > /tmp/spec.out + test/interfaces/bdd.spec 2>&1 > /tmp/spec.out @echo spec: @cat /tmp/spec.out diff --git a/README.md b/README.md index d3a57cffe9..9e4d4dcbea 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ 1. Increase test coverage on Node.js and browser - Increase integration coverage for all reporters - `html` reporter must be tested in browser - - Basic console reporters (*not* `nyan`, `landing`, etc.) must be tested in **both** browser and Node.js contexts; PhantomJS can consume all console reporters - - Filesystem-based reporters must be tested in Node.js context + - ~~Basic console reporters (*not* `nyan`, `landing`, etc.) must be tested in **both** browser and Node.js contexts; PhantomJS can consume all console reporters~~ + - ~~Filesystem-based reporters must be tested in Node.js context~~ + - **UPDATE - May 24 2017**: Thanks to [community contributions](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md#mag-coverage), the coverage on most reporters has increased dramatically! The `html` reporter is still in [dire need of coverage](https://coveralls.io/builds/11674428/source?filename=lib%2Freporters%2Fhtml.js). - Increase coverage against all interfaces (`exports` in particular). Ideally this becomes a "matrix" where we repeat sets of integration tests across all interfaces. - Refactor non-Node.js-specific tests to allow them to run in a browser context. Node.js-specific tests include those which *require* the CLI or filesystem. Most everything else is fair game. 2. Review current open pull requests @@ -20,7 +21,7 @@ - Pull requests **must** have supporting tests. The only exceptions are pure cosmetic or non-functional changes. - Pull request contributors must sign the CLA. 3. Close old, inactive issues and pull requests - - A bot should do this. We need a bot. Got a bot? + - ~~A bot should do this. We need a bot. Got a bot?~~ We now use GitHub's own [probot-stale](https://www.npmjs.com/package/probot-stale). 4. Triage issues - If we run into "critical" bugs, they need fixing. - "Critical" means Mocha is broken w/o workarounds for a *large percentage* of users @@ -39,6 +40,7 @@ Once we gain ground on the above items, we can work together formalize our contr [![Build Status](https://api.travis-ci.org/mochajs/mocha.svg?branch=master)](http://travis-ci.org/mochajs/mocha) [![Coverage Status](https://coveralls.io/repos/github/mochajs/mocha/badge.svg)](https://coveralls.io/github/mochajs/mocha) +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_shield) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mochajs/mocha?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![OpenCollective](https://opencollective.com/mochajs/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/mochajs/sponsors/badge.svg)](#sponsors) @@ -118,3 +120,5 @@ Does your company use Mocha? Ask your manager or marketing team if your company ## License [MIT](LICENSE) + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_large) diff --git a/appveyor.yml b/appveyor.yml index 6a2a6e1811..ff832db835 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,6 @@ environment: matrix: + - nodejs_version: '8' - nodejs_version: '7' - nodejs_version: '6' - nodejs_version: '4' diff --git a/bin/_mocha b/bin/_mocha index 61aac40d3a..c37d49fef5 100755 --- a/bin/_mocha +++ b/bin/_mocha @@ -91,12 +91,15 @@ program .option('--icu-data-dir', 'include ICU data') .option('--inline-diffs', 'display actual/expected differences inline within each string') .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('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit') .option('--no-timeouts', 'disables timeouts, given implicitly with --debug') + .option('--no-warnings', 'silence all node process warnings') .option('--opts ', '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') @@ -105,9 +108,13 @@ program .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 ,...', 'additional extensions to monitor with --watch', list, []) - .option('--delay', 'wait for async suite definition'); + .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'); program._name = 'mocha'; @@ -313,6 +320,12 @@ if (program.delay) { mocha.delay(); } +// --allow-uncaught + +if (program.allowUncaught) { + mocha.allowUncaught(); +} + // --globals mocha.globals(globals); @@ -323,6 +336,14 @@ 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 var extensions = ['js']; @@ -466,12 +487,18 @@ function exitLater (code) { } function exit (code) { + var clampedCode = Math.min(code, 255); + + // 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 function done () { if (!(draining--)) { - process.exit(Math.min(code, 255)); + process.exit(clampedCode); } } diff --git a/bin/mocha b/bin/mocha index c6aaa97b00..d632fbc394 100755 --- a/bin/mocha +++ b/bin/mocha @@ -28,6 +28,7 @@ process.argv.slice(2).forEach(function (arg) { case '--debug': case '--debug-brk': case '--inspect': + case '--inspect-brk': args.unshift(arg); args.push('--no-timeouts'); break; @@ -38,13 +39,16 @@ process.argv.slice(2).forEach(function (arg) { 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: diff --git a/karma.conf.js b/karma.conf.js index fa4996317a..c99150e2f9 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -18,14 +18,7 @@ module.exports = function (config) { // we use the BDD interface for all of the tests that // aren't interface-specific. 'test/browser-fixtures/bdd.fixture.js', - 'test/acceptance/*.spec.js' - ], - exclude: [ - 'test/acceptance/http.spec.js', - 'test/acceptance/fs.spec.js', - 'test/acceptance/file-utils.spec.js', - 'test/acceptance/require/**/*.js', - 'test/acceptance/misc/**/*.js' + 'test/unit/*.spec.js' ], preprocessors: { 'test/**/*.js': ['browserify'] @@ -97,13 +90,14 @@ module.exports = function (config) { console.error('Local/unknown environment detected'); bundleDirpath = path.join(baseBundleDirpath, 'local'); // don't need to run sauce from appveyor b/c travis does it. - if (!(env.SAUCE_USERNAME || env.SAUCE_ACCESS_KEY)) { - console.error('No SauceLabs credentials present'); - } else { + if (env.SAUCE_USERNAME || env.SAUCE_ACCESS_KEY) { sauceConfig = { - build: require('os').hostname() + ' (' + Date.now() + ')' + build: require('os') + .hostname() + ' (' + Date.now() + ')' }; console.error('Configured SauceLabs'); + } else { + console.error('No SauceLabs credentials present'); } } mkdirp.sync(bundleDirpath); @@ -117,7 +111,7 @@ module.exports = function (config) { } // the MOCHA_UI env var will determine if we're running interface-specific - // tets. since you can only load one at a time, each must be run separately. + // tests. since you can only load one at a time, each must be run separately. // each has its own set of acceptance tests and a fixture. // the "bdd" fixture is used by default. var ui = env.MOCHA_UI; @@ -127,7 +121,7 @@ module.exports = function (config) { } cfg.files = [ 'test/browser-fixtures/' + ui + '.fixture.js', - 'test/acceptance/interfaces/' + ui + '.spec.js' + 'test/interfaces/' + ui + '.spec.js' ]; } else if (cfg.sauceLabs) { cfg.sauceLabs.testName = 'Unit Tests'; diff --git a/lib/browser/.eslintrc.yaml b/lib/browser/.eslintrc.yaml new file mode 100644 index 0000000000..d85f537d3b --- /dev/null +++ b/lib/browser/.eslintrc.yaml @@ -0,0 +1,4 @@ +env: + node: false + browser: false + commonjs: true diff --git a/lib/mocha.js b/lib/mocha.js index 8bacf27b17..bfc0238d46 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -483,6 +483,24 @@ Mocha.prototype.delay = function delay () { 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. * @@ -504,6 +522,8 @@ Mocha.prototype.run = function (fn) { runner.hasOnly = options.hasOnly; 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); } diff --git a/lib/reporters/html.js b/lib/reporters/html.js index 3e755d2d63..e29aa36a5a 100644 --- a/lib/reporters/html.js +++ b/lib/reporters/html.js @@ -41,6 +41,8 @@ var statsTemplate = ''; +var playIcon = '‣'; + /** * Initialize a new `HTML` reporter. * @@ -136,7 +138,7 @@ function HTML (runner) { runner.on('pass', function (test) { var url = self.testURL(test); var markup = '
  • %e%ems ' + - '

  • '; + '' + playIcon + ''; var el = fragment(markup, test.speed, test.title, test.duration, url); self.addCodeToggle(el, test.body); appendToStack(el); @@ -144,7 +146,7 @@ function HTML (runner) { }); runner.on('fail', function (test) { - var el = fragment('
  • %e

  • ', + var el = fragment('
  • %e ' + playIcon + '

  • ', test.title, self.testURL(test)); var stackString; // Note: Includes leading newline var message = test.err.toString(); diff --git a/lib/runnable.js b/lib/runnable.js index e7b6aa12f0..ee8c4a3467 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -324,8 +324,11 @@ Runnable.prototype.run = function (fn) { } if (this.allowUncaught) { - callFn(this.fn); - done(); + if (this.isPending()) { + done(); + } else { + callFn(this.fn); + } return; } diff --git a/lib/runner.js b/lib/runner.js index 1d3dd959da..039065a2e0 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -432,15 +432,14 @@ Runner.prototype.runTest = function (fn) { if (this.asyncOnly) { test.asyncOnly = true; } - + test.on('error', function (err) { + self.fail(test, err); + }); if (this.allowUncaught) { test.allowUncaught = true; return test.run(fn); } try { - test.on('error', function (err) { - self.fail(test, err); - }); test.run(fn); } catch (err) { fn(err); @@ -821,6 +820,12 @@ Runner.prototype.run = function (fn) { // callback this.on('end', function () { + if (self.forbidOnly && self.hasOnly) { + self.failures += self.stats.tests; + } + if (self.forbidPending) { + self.failures += self.stats.pending; + } debug('end'); process.removeListener('uncaughtException', uncaught); fn(self.failures); diff --git a/lib/utils.js b/lib/utils.js index 7395750dcc..d66eba3bc3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -322,7 +322,9 @@ exports.parseQuery = function (qs) { 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; }, {}); }; diff --git a/mocha.js b/mocha.js index 390f5907a9..4e59d156cf 100644 --- a/mocha.js +++ b/mocha.js @@ -189,7 +189,7 @@ global.mocha = mocha; module.exports = global; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./lib/mocha":14,"_process":67,"browser-stdout":41}],2:[function(require,module,exports){ +},{"./lib/mocha":14,"_process":82,"browser-stdout":41}],2:[function(require,module,exports){ 'use strict'; function noop () {} @@ -647,7 +647,7 @@ Context.prototype.inspect = function () { }, 2); }; -},{"json3":54}],7:[function(require,module,exports){ +},{"json3":69}],7:[function(require,module,exports){ 'use strict'; /** @@ -1412,9 +1412,17 @@ Mocha.prototype.reporter = function (reporter, reporterOptions) { try { _reporter = require(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); + 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') { @@ -1782,7 +1790,7 @@ Mocha.prototype.run = function (fn) { }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/lib") -},{"./context":6,"./hook":7,"./interfaces":11,"./reporters":21,"./runnable":33,"./runner":34,"./suite":35,"./test":36,"./utils":38,"_process":67,"escape-string-regexp":47,"growl":49,"path":42}],15:[function(require,module,exports){ +},{"./context":6,"./hook":7,"./interfaces":11,"./reporters":21,"./runnable":33,"./runner":34,"./suite":35,"./test":36,"./utils":38,"_process":82,"escape-string-regexp":62,"growl":64,"path":42}],15:[function(require,module,exports){ 'use strict'; /** @@ -2427,7 +2435,7 @@ function sameType (a, b) { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../ms":15,"../utils":38,"_process":67,"diff":46,"supports-color":42,"tty":5}],18:[function(require,module,exports){ +},{"../ms":15,"../utils":38,"_process":82,"diff":56,"supports-color":42,"tty":5}],18:[function(require,module,exports){ 'use strict'; /** @@ -2565,7 +2573,7 @@ function Dot (runner) { inherits(Dot, Base); }).call(this,require('_process')) -},{"../utils":38,"./base":17,"_process":67}],20:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82}],20:[function(require,module,exports){ (function (global){ 'use strict'; @@ -2610,6 +2618,8 @@ var statsTemplate = ''; +var playIcon = '‣'; + /** * Initialize a new `HTML` reporter. * @@ -2705,7 +2715,7 @@ function HTML (runner) { runner.on('pass', function (test) { var url = self.testURL(test); var markup = '
  • %e%ems ' + - '

  • '; + '' + playIcon + ''; var el = fragment(markup, test.speed, test.title, test.duration, url); self.addCodeToggle(el, test.body); appendToStack(el); @@ -2713,7 +2723,7 @@ function HTML (runner) { }); runner.on('fail', function (test) { - var el = fragment('
  • %e

  • ', + var el = fragment('
  • %e ' + playIcon + '

  • ', test.title, self.testURL(test)); var stackString; // Note: Includes leading newline var message = test.err.toString(); @@ -2915,7 +2925,7 @@ function on (el, event, fn) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../browser/progress":4,"../utils":38,"./base":17,"escape-string-regexp":47}],21:[function(require,module,exports){ +},{"../browser/progress":4,"../utils":38,"./base":17,"escape-string-regexp":62}],21:[function(require,module,exports){ 'use strict'; // Alias exports to a their normalized format Mocha#reporter to prevent a need @@ -3003,7 +3013,7 @@ function clean (test) { } }).call(this,require('_process')) -},{"./base":17,"_process":67,"json3":54}],23:[function(require,module,exports){ +},{"./base":17,"_process":82,"json3":69}],23:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3099,7 +3109,7 @@ function errorJSON (err) { } }).call(this,require('_process')) -},{"./base":17,"_process":67}],24:[function(require,module,exports){ +},{"./base":17,"_process":82}],24:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3197,7 +3207,7 @@ function Landing (runner) { inherits(Landing, Base); }).call(this,require('_process')) -},{"../utils":38,"./base":17,"_process":67}],25:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82}],25:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3264,7 +3274,7 @@ function List (runner) { inherits(List, Base); }).call(this,require('_process')) -},{"../utils":38,"./base":17,"_process":67}],26:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82}],26:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3367,7 +3377,7 @@ function Markdown (runner) { } }).call(this,require('_process')) -},{"../utils":38,"./base":17,"_process":67}],27:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82}],27:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3409,7 +3419,7 @@ function Min (runner) { inherits(Min, Base); }).call(this,require('_process')) -},{"../utils":38,"./base":17,"_process":67}],28:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82}],28:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3676,7 +3686,7 @@ function write (string) { } }).call(this,require('_process')) -},{"../utils":38,"./base":17,"_process":67}],29:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82}],29:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3771,7 +3781,7 @@ function Progress (runner, options) { inherits(Progress, Base); }).call(this,require('_process')) -},{"../utils":38,"./base":17,"_process":67}],30:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82}],30:[function(require,module,exports){ 'use strict'; /** @@ -4098,7 +4108,7 @@ function tag (name, attrs, close, content) { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../utils":38,"./base":17,"_process":67,"fs":42,"mkdirp":64,"path":42}],33:[function(require,module,exports){ +},{"../utils":38,"./base":17,"_process":82,"fs":42,"mkdirp":79,"path":42}],33:[function(require,module,exports){ (function (global){ 'use strict'; @@ -4426,8 +4436,11 @@ Runnable.prototype.run = function (fn) { } if (this.allowUncaught) { - callFn(this.fn); - done(); + if (this.isPending()) { + done(); + } else { + callFn(this.fn); + } return; } @@ -4488,7 +4501,7 @@ Runnable.prototype.run = function (fn) { }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./ms":15,"./pending":16,"./utils":38,"debug":2,"events":3,"json3":54,"lodash.create":60}],34:[function(require,module,exports){ +},{"./ms":15,"./pending":16,"./utils":38,"debug":2,"events":3,"json3":69,"lodash.create":75}],34:[function(require,module,exports){ (function (process,global){ 'use strict'; @@ -4924,15 +4937,14 @@ Runner.prototype.runTest = function (fn) { if (this.asyncOnly) { test.asyncOnly = true; } - + test.on('error', function (err) { + self.fail(test, err); + }); if (this.allowUncaught) { test.allowUncaught = true; return test.run(fn); } try { - test.on('error', function (err) { - self.fail(test, err); - }); test.run(fn); } catch (err) { fn(err); @@ -5173,9 +5185,9 @@ Runner.prototype.runSuite = function (suite, fn) { */ Runner.prototype.uncaught = function (err) { if (err) { - debug('uncaught exception %s', err !== function () { + debug('uncaught exception %s', err === (function () { return this; - }.call(err) ? err : (err.message || err)); + }.call(err)) ? (err.message || err) : err); } else { debug('uncaught undefined exception'); err = undefinedError(); @@ -5455,7 +5467,7 @@ function extraGlobals () { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./pending":16,"./runnable":33,"./utils":38,"_process":67,"debug":2,"events":3}],35:[function(require,module,exports){ +},{"./pending":16,"./runnable":33,"./utils":38,"_process":82,"debug":2,"events":3}],35:[function(require,module,exports){ 'use strict'; /** @@ -5913,7 +5925,7 @@ Test.prototype.clone = function () { return test; }; -},{"./runnable":33,"./utils":38,"lodash.create":60}],37:[function(require,module,exports){ +},{"./runnable":33,"./utils":38,"lodash.create":75}],37:[function(require,module,exports){ 'use strict'; /** @@ -6278,7 +6290,9 @@ exports.parseQuery = function (qs) { var key = pair.slice(0, i); var val = pair.slice(++i); - obj[key] = decodeURIComponent(val); + // Due to how the URLSearchParams API treats spaces + obj[key] = decodeURIComponent(val.replace(/\+/g, '%20')); + return obj; }, {}); }; @@ -6372,7 +6386,7 @@ var type = exports.type = function type (value) { return 'buffer'; } return Object.prototype.toString.call(value) - .replace(/^\[.+\s(.+?)\]$/, '$1') + .replace(/^\[.+\s(.+?)]$/, '$1') .toLowerCase(); }; @@ -6697,7 +6711,9 @@ exports.stackTraceFilter = function () { if (is.node) { cwd = process.cwd() + slash; } else { - cwd = (typeof location === 'undefined' ? window.location : location).href.replace(/\/[^\/]*$/, '/'); + cwd = (typeof location === 'undefined' + ? window.location + : location).href.replace(/\/[^/]*$/, '/'); slash = '/'; } @@ -6759,7 +6775,7 @@ exports.isPromise = function isPromise (value) { exports.noop = function () {}; }).call(this,require('_process'),require("buffer").Buffer) -},{"./to-iso-string":37,"_process":67,"buffer":44,"debug":2,"fs":42,"glob":42,"json3":54,"path":42,"util":84}],39:[function(require,module,exports){ +},{"./to-iso-string":37,"_process":82,"buffer":44,"debug":2,"fs":42,"glob":42,"json3":69,"path":42,"util":99}],39:[function(require,module,exports){ 'use strict' exports.byteLength = byteLength @@ -6906,7 +6922,7 @@ BrowserStdout.prototype._write = function(chunks, encoding, cb) { } }).call(this,require('_process')) -},{"_process":67,"stream":79,"util":84}],42:[function(require,module,exports){ +},{"_process":82,"stream":94,"util":99}],42:[function(require,module,exports){ arguments[4][40][0].apply(exports,arguments) },{"dup":40}],43:[function(require,module,exports){ (function (global){ @@ -8813,7 +8829,7 @@ function isnan (val) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"base64-js":39,"ieee754":50,"isarray":53}],45:[function(require,module,exports){ +},{"base64-js":39,"ieee754":65,"isarray":68}],45:[function(require,module,exports){ (function (Buffer){ // Copyright Joyent, Inc. and other Node contributors. // @@ -8924,43 +8940,249 @@ function objectToString(o) { } }).call(this,{"isBuffer":require("../../is-buffer/index.js")}) -},{"../../is-buffer/index.js":52}],46:[function(require,module,exports){ -/* See LICENSE file for terms of use */ +},{"../../is-buffer/index.js":67}],46:[function(require,module,exports){ +/*istanbul ignore start*/"use strict"; + +exports.__esModule = true; +exports. /*istanbul ignore end*/convertChangesToDMP = convertChangesToDMP; +// See: http://code.google.com/p/google-diff-match-patch/wiki/API +function convertChangesToDMP(changes) { + var ret = [], + change = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + operation = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (var i = 0; i < changes.length; i++) { + change = changes[i]; + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; + } -/* - * Text diff implementation. - * - * This library supports the following APIS: - * JsDiff.diffChars: Character by character diff - * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace - * JsDiff.diffLines: Line based diff - * - * JsDiff.diffCss: Diff targeted at CSS content - * - * These methods are based on the implementation proposed in - * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). - * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - */ -(function(global, undefined) { - var objectPrototypeToString = Object.prototype.toString; + ret.push([operation, change.value]); + } + return ret; +} + + +},{}],47:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; - /*istanbul ignore next*/ - function map(arr, mapper, that) { - if (Array.prototype.map) { - return Array.prototype.map.call(arr, mapper, that); +exports.__esModule = true; +exports. /*istanbul ignore end*/convertChangesToXML = convertChangesToXML; +function convertChangesToXML(changes) { + var ret = []; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); } - var other = new Array(arr.length); + ret.push(escapeHTML(change.value)); - for (var i = 0, n = arr.length; i < n; i++) { - other[i] = mapper.call(that, arr[i], i, arr); + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); } - return other; } - function clonePath(path) { - return { newPos: path.newPos, components: path.components.slice(0) }; - } - function removeEmpty(array) { + return ret.join(''); +} + +function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; +} + + +},{}],48:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.arrayDiff = undefined; +exports. /*istanbul ignore end*/diffArrays = diffArrays; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var arrayDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/; +arrayDiff.tokenize = arrayDiff.join = function (value) { + return value.slice(); +}; + +function diffArrays(oldArr, newArr, callback) { + return arrayDiff.diff(oldArr, newArr, callback); +} + + +},{"./base":49}],49:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports['default'] = /*istanbul ignore end*/Diff; +function Diff() {} + +Diff.prototype = { /*istanbul ignore start*/ + /*istanbul ignore end*/diff: function diff(oldString, newString) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + + var callback = options.callback; + if (typeof options === 'function') { + callback = options; + options = {}; + } + this.options = options; + + var self = this; + + function done(value) { + if (callback) { + setTimeout(function () { + callback(undefined, value); + }, 0); + return true; + } else { + return value; + } + } + + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString); + newString = this.castInput(newString); + + oldString = this.removeEmpty(this.tokenize(oldString)); + newString = this.removeEmpty(this.tokenize(newString)); + + var newLen = newString.length, + oldLen = oldString.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0, i.e. the content starts with the same values + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + // Identity per the equality and tokenizer + return done([{ value: this.join(newString), count: newString.length }]); + } + + // Main worker method. checks all permutations of a given edit length for acceptance. + function execEditLength() { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { + var basePath = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + var addPath = bestPath[diagonalPath - 1], + removePath = bestPath[diagonalPath + 1], + _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } + + var canAdd = addPath && addPath.newPos + 1 < newLen, + canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen; + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || canRemove && addPath.newPos < removePath.newPos) { + basePath = clonePath(removePath); + self.pushComponent(basePath.components, undefined, true); + } else { + basePath = addPath; // No need to clone, we've pulled it from the list + basePath.newPos++; + self.pushComponent(basePath.components, true, undefined); + } + + _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + + // If we have hit the end of both strings, then we are done + if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) { + return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken)); + } else { + // Otherwise track this path as a potential candidate and continue. + bestPath[diagonalPath] = basePath; + } + } + + editLength++; + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the + // sync and async mode which is never fun. Loops over execEditLength until a value + // is produced. + if (callback) { + (function exec() { + setTimeout(function () { + // This should not happen, but we want to be safe. + /* istanbul ignore next */ + if (editLength > maxEditLength) { + return callback(); + } + + if (!execEditLength()) { + exec(); + } + }, 0); + })(); + } else { + while (editLength <= maxEditLength) { + var ret = execEditLength(); + if (ret) { + return ret; + } + } + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/pushComponent: function pushComponent(components, added, removed) { + var last = components[components.length - 1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length - 1] = { count: last.count + 1, added: added, removed: removed }; + } else { + components.push({ count: 1, added: added, removed: removed }); + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } + + if (commonCount) { + basePath.components.push({ count: commonCount }); + } + + basePath.newPos = newPos; + return oldPos; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/equals: function equals(left, right) { + return left === right; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/removeEmpty: function removeEmpty(array) { var ret = []; for (var i = 0; i < array.length; i++) { if (array[i]) { @@ -8968,584 +9190,1008 @@ function objectToString(o) { } } return ret; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/castInput: function castInput(value) { + return value; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/tokenize: function tokenize(value) { + return value.split(''); + }, + /*istanbul ignore start*/ /*istanbul ignore end*/join: function join(chars) { + return chars.join(''); } - function escapeHTML(s) { - var n = s; - n = n.replace(/&/g, '&'); - n = n.replace(//g, '>'); - n = n.replace(/"/g, '"'); - - return n; - } +}; - // This function handles the presence of circular references by bailing out when encountering an - // object that is already on the "stack" of items being processed. - function canonicalize(obj, stack, replacementStack) { - stack = stack || []; - replacementStack = replacementStack || []; +function buildValues(diff, components, newString, oldString, useLongestToken) { + var componentPos = 0, + componentLen = components.length, + newPos = 0, + oldPos = 0; - var i; + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + if (!component.removed) { + if (!component.added && useLongestToken) { + var value = newString.slice(newPos, newPos + component.count); + value = value.map(function (value, i) { + var oldValue = oldString[oldPos + i]; + return oldValue.length > value.length ? oldValue : value; + }); - for (i = 0; i < stack.length; i += 1) { - if (stack[i] === obj) { - return replacementStack[i]; + component.value = diff.join(value); + } else { + component.value = diff.join(newString.slice(newPos, newPos + component.count)); } - } - - var canonicalizedObj; + newPos += component.count; - if ('[object Array]' === objectPrototypeToString.call(obj)) { - stack.push(obj); - canonicalizedObj = new Array(obj.length); - replacementStack.push(canonicalizedObj); - for (i = 0; i < obj.length; i += 1) { - canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack); - } - stack.pop(); - replacementStack.pop(); - } else if (typeof obj === 'object' && obj !== null) { - stack.push(obj); - canonicalizedObj = {}; - replacementStack.push(canonicalizedObj); - var sortedKeys = [], - key; - for (key in obj) { - sortedKeys.push(key); - } - sortedKeys.sort(); - for (i = 0; i < sortedKeys.length; i += 1) { - key = sortedKeys[i]; - canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack); + // Common case + if (!component.added) { + oldPos += component.count; } - stack.pop(); - replacementStack.pop(); } else { - canonicalizedObj = obj; + component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); + oldPos += component.count; + + // Reverse add and remove so removes are output first to match common convention + // The diffing algorithm is tied to add then remove output and this is the simplest + // route to get the desired output with minimal overhead. + if (componentPos && components[componentPos - 1].added) { + var tmp = components[componentPos - 1]; + components[componentPos - 1] = components[componentPos]; + components[componentPos] = tmp; + } } - return canonicalizedObj; } - function buildValues(components, newString, oldString, useLongestToken) { - var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; + // Special case handle for when one terminal is ignored. For this case we merge the + // terminal into the prior string and drop the change. + var lastComponent = components[componentLen - 1]; + if (componentLen > 1 && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) { + components[componentLen - 2].value += lastComponent.value; + components.pop(); + } - for (; componentPos < componentLen; componentPos++) { - var component = components[componentPos]; - if (!component.removed) { - if (!component.added && useLongestToken) { - var value = newString.slice(newPos, newPos + component.count); - value = map(value, function(value, i) { - var oldValue = oldString[oldPos + i]; - return oldValue.length > value.length ? oldValue : value; - }); + return components; +} - component.value = value.join(''); - } else { - component.value = newString.slice(newPos, newPos + component.count).join(''); - } - newPos += component.count; +function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; +} - // Common case - if (!component.added) { - oldPos += component.count; - } - } else { - component.value = oldString.slice(oldPos, oldPos + component.count).join(''); - oldPos += component.count; - // Reverse add and remove so removes are output first to match common convention - // The diffing algorithm is tied to add then remove output and this is the simplest - // route to get the desired output with minimal overhead. - if (componentPos && components[componentPos - 1].added) { - var tmp = components[componentPos - 1]; - components[componentPos - 1] = components[componentPos]; - components[componentPos] = tmp; - } - } +},{}],50:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.characterDiff = undefined; +exports. /*istanbul ignore end*/diffChars = diffChars; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var characterDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/characterDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/; +function diffChars(oldStr, newStr, callback) { + return characterDiff.diff(oldStr, newStr, callback); +} + + +},{"./base":49}],51:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.cssDiff = undefined; +exports. /*istanbul ignore end*/diffCss = diffCss; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var cssDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/cssDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/; +cssDiff.tokenize = function (value) { + return value.split(/([{}:;,]|\s+)/); +}; + +function diffCss(oldStr, newStr, callback) { + return cssDiff.diff(oldStr, newStr, callback); +} + + +},{"./base":49}],52:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.jsonDiff = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; + +exports. /*istanbul ignore end*/diffJson = diffJson; +/*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = canonicalize; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); + +/*istanbul ignore end*/ +var /*istanbul ignore start*/_line = require('./line') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/ + +var objectPrototypeToString = Object.prototype.toString; + +var jsonDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/jsonDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/; +// Discriminate between two lines of pretty-printed, serialized JSON where one of them has a +// dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: +jsonDiff.useLongestToken = true; + +jsonDiff.tokenize = /*istanbul ignore start*/_line.lineDiff. /*istanbul ignore end*/tokenize; +jsonDiff.castInput = function (value) { + /*istanbul ignore start*/var /*istanbul ignore end*/undefinedReplacement = this.options.undefinedReplacement; + + + return typeof value === 'string' ? value : JSON.stringify(canonicalize(value), function (k, v) { + if (typeof v === 'undefined') { + return undefinedReplacement; } - return components; + return v; + }, ' '); +}; +jsonDiff.equals = function (left, right) { + return (/*istanbul ignore start*/_base2['default']. /*istanbul ignore end*/prototype.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')) + ); +}; + +function diffJson(oldObj, newObj, options) { + return jsonDiff.diff(oldObj, newObj, options); +} + +// This function handles the presence of circular references by bailing out when encountering an +// object that is already on the "stack" of items being processed. +function canonicalize(obj, stack, replacementStack) { + stack = stack || []; + replacementStack = replacementStack || []; + + var i = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + for (i = 0; i < stack.length; i += 1) { + if (stack[i] === obj) { + return replacementStack[i]; + } } - function Diff(ignoreWhitespace) { - this.ignoreWhitespace = ignoreWhitespace; + var canonicalizedObj = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + if ('[object Array]' === objectPrototypeToString.call(obj)) { + stack.push(obj); + canonicalizedObj = new Array(obj.length); + replacementStack.push(canonicalizedObj); + for (i = 0; i < obj.length; i += 1) { + canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack); + } + stack.pop(); + replacementStack.pop(); + return canonicalizedObj; } - Diff.prototype = { - diff: function(oldString, newString, callback) { - var self = this; - function done(value) { - if (callback) { - setTimeout(function() { callback(undefined, value); }, 0); - return true; - } else { - return value; - } - } + if (obj && obj.toJSON) { + obj = obj.toJSON(); + } - // Handle the identity case (this is due to unrolling editLength == 0 - if (newString === oldString) { - return done([{ value: newString }]); - } - if (!newString) { - return done([{ value: oldString, removed: true }]); - } - if (!oldString) { - return done([{ value: newString, added: true }]); + if ( /*istanbul ignore start*/(typeof /*istanbul ignore end*/obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + replacementStack.push(canonicalizedObj); + var sortedKeys = [], + key = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (key in obj) { + /* istanbul ignore else */ + if (obj.hasOwnProperty(key)) { + sortedKeys.push(key); } + } + sortedKeys.sort(); + for (i = 0; i < sortedKeys.length; i += 1) { + key = sortedKeys[i]; + canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack); + } + stack.pop(); + replacementStack.pop(); + } else { + canonicalizedObj = obj; + } + return canonicalizedObj; +} - newString = this.tokenize(newString); - oldString = this.tokenize(oldString); - var newLen = newString.length, oldLen = oldString.length; - var editLength = 1; - var maxEditLength = newLen + oldLen; - var bestPath = [{ newPos: -1, components: [] }]; +},{"./base":49,"./line":53}],53:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; - // Seed editLength = 0, i.e. the content starts with the same values - var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); - if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { - // Identity per the equality and tokenizer - return done([{value: newString.join('')}]); - } +exports.__esModule = true; +exports.lineDiff = undefined; +exports. /*istanbul ignore end*/diffLines = diffLines; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = diffTrimmedLines; - // Main worker method. checks all permutations of a given edit length for acceptance. - function execEditLength() { - for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { - var basePath; - var addPath = bestPath[diagonalPath - 1], - removePath = bestPath[diagonalPath + 1], - oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; - if (addPath) { - // No one else is going to attempt to use this value, clear it - bestPath[diagonalPath - 1] = undefined; - } +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; - var canAdd = addPath && addPath.newPos + 1 < newLen, - canRemove = removePath && 0 <= oldPos && oldPos < oldLen; - if (!canAdd && !canRemove) { - // If this path is a terminal then prune - bestPath[diagonalPath] = undefined; - continue; - } +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); - // Select the diagonal that we want to branch from. We select the prior - // path whose position in the new string is the farthest from the origin - // and does not pass the bounds of the diff graph - if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { - basePath = clonePath(removePath); - self.pushComponent(basePath.components, undefined, true); - } else { - basePath = addPath; // No need to clone, we've pulled it from the list - basePath.newPos++; - self.pushComponent(basePath.components, true, undefined); - } +/*istanbul ignore end*/ +var /*istanbul ignore start*/_params = require('../util/params') /*istanbul ignore end*/; - oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); +/*istanbul ignore start*/ +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - // If we have hit the end of both strings, then we are done - if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { - return done(buildValues(basePath.components, newString, oldString, self.useLongestToken)); - } else { - // Otherwise track this path as a potential candidate and continue. - bestPath[diagonalPath] = basePath; - } - } +/*istanbul ignore end*/var lineDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/lineDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/; +lineDiff.tokenize = function (value) { + var retLines = [], + linesAndNewlines = value.split(/(\n|\r\n)/); - editLength++; - } + // Ignore the final empty token that occurs if the string ends with a new line + if (!linesAndNewlines[linesAndNewlines.length - 1]) { + linesAndNewlines.pop(); + } - // Performs the length of edit iteration. Is a bit fugly as this has to support the - // sync and async mode which is never fun. Loops over execEditLength until a value - // is produced. - if (callback) { - (function exec() { - setTimeout(function() { - // This should not happen, but we want to be safe. - /*istanbul ignore next */ - if (editLength > maxEditLength) { - return callback(); - } + // Merge the content and line separators into single tokens + for (var i = 0; i < linesAndNewlines.length; i++) { + var line = linesAndNewlines[i]; - if (!execEditLength()) { - exec(); - } - }, 0); - }()); - } else { - while (editLength <= maxEditLength) { - var ret = execEditLength(); - if (ret) { - return ret; - } - } + if (i % 2 && !this.options.newlineIsToken) { + retLines[retLines.length - 1] += line; + } else { + if (this.options.ignoreWhitespace) { + line = line.trim(); } - }, + retLines.push(line); + } + } - pushComponent: function(components, added, removed) { - var last = components[components.length - 1]; - if (last && last.added === added && last.removed === removed) { - // We need to clone here as the component clone operation is just - // as shallow array clone - components[components.length - 1] = {count: last.count + 1, added: added, removed: removed }; - } else { - components.push({count: 1, added: added, removed: removed }); - } - }, - extractCommon: function(basePath, newString, oldString, diagonalPath) { - var newLen = newString.length, - oldLen = oldString.length, - newPos = basePath.newPos, - oldPos = newPos - diagonalPath, - - commonCount = 0; - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { - newPos++; - oldPos++; - commonCount++; - } + return retLines; +}; - if (commonCount) { - basePath.components.push({count: commonCount}); - } +function diffLines(oldStr, newStr, callback) { + return lineDiff.diff(oldStr, newStr, callback); +} +function diffTrimmedLines(oldStr, newStr, callback) { + var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true }); + return lineDiff.diff(oldStr, newStr, options); +} - basePath.newPos = newPos; - return oldPos; - }, - equals: function(left, right) { - var reWhitespace = /\S/; - return left === right || (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)); - }, - tokenize: function(value) { - return value.split(''); +},{"../util/params":61,"./base":49}],54:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.sentenceDiff = undefined; +exports. /*istanbul ignore end*/diffSentences = diffSentences; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var sentenceDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/sentenceDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/; +sentenceDiff.tokenize = function (value) { + return value.split(/(\S.+?[.!?])(?=\s+|$)/); +}; + +function diffSentences(oldStr, newStr, callback) { + return sentenceDiff.diff(oldStr, newStr, callback); +} + + +},{"./base":49}],55:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.wordDiff = undefined; +exports. /*istanbul ignore end*/diffWords = diffWords; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = diffWordsWithSpace; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); + +/*istanbul ignore end*/ +var /*istanbul ignore start*/_params = require('../util/params') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/ + +// Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode +// +// Ranges and exceptions: +// Latin-1 Supplement, 0080–00FF +// - U+00D7 × Multiplication sign +// - U+00F7 ÷ Division sign +// Latin Extended-A, 0100–017F +// Latin Extended-B, 0180–024F +// IPA Extensions, 0250–02AF +// Spacing Modifier Letters, 02B0–02FF +// - U+02C7 ˇ ˇ Caron +// - U+02D8 ˘ ˘ Breve +// - U+02D9 ˙ ˙ Dot Above +// - U+02DA ˚ ˚ Ring Above +// - U+02DB ˛ ˛ Ogonek +// - U+02DC ˜ ˜ Small Tilde +// - U+02DD ˝ ˝ Double Acute Accent +// Latin Extended Additional, 1E00–1EFF +var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; + +var reWhitespace = /\S/; + +var wordDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/wordDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/; +wordDiff.equals = function (left, right) { + return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); +}; +wordDiff.tokenize = function (value) { + var tokens = value.split(/(\s+|\b)/); + + // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. + for (var i = 0; i < tokens.length - 1; i++) { + // If we have an empty string in the next field and we have only word chars before and after, merge + if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { + tokens[i] += tokens[i + 2]; + tokens.splice(i + 1, 2); + i--; } - }; + } - var CharDiff = new Diff(); + return tokens; +}; - var WordDiff = new Diff(true); - var WordWithSpaceDiff = new Diff(); - WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\s+|\b)/)); - }; +function diffWords(oldStr, newStr, callback) { + var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true }); + return wordDiff.diff(oldStr, newStr, options); +} +function diffWordsWithSpace(oldStr, newStr, callback) { + return wordDiff.diff(oldStr, newStr, callback); +} - var CssDiff = new Diff(true); - CssDiff.tokenize = function(value) { - return removeEmpty(value.split(/([{}:;,]|\s+)/)); - }; - var LineDiff = new Diff(); +},{"../util/params":61,"./base":49}],56:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; - var TrimmedLineDiff = new Diff(); - TrimmedLineDiff.ignoreTrim = true; +exports.__esModule = true; +exports.canonicalize = exports.convertChangesToXML = exports.convertChangesToDMP = exports.parsePatch = exports.applyPatches = exports.applyPatch = exports.createPatch = exports.createTwoFilesPatch = exports.structuredPatch = exports.diffArrays = exports.diffJson = exports.diffCss = exports.diffSentences = exports.diffTrimmedLines = exports.diffLines = exports.diffWordsWithSpace = exports.diffWords = exports.diffChars = exports.Diff = undefined; +/*istanbul ignore end*/ +var /*istanbul ignore start*/_base = require('./diff/base') /*istanbul ignore end*/; - LineDiff.tokenize = TrimmedLineDiff.tokenize = function(value) { - var retLines = [], - lines = value.split(/^/m); - for (var i = 0; i < lines.length; i++) { - var line = lines[i], - lastLine = lines[i - 1], - lastLineLastChar = lastLine && lastLine[lastLine.length - 1]; +/*istanbul ignore start*/ +var _base2 = _interopRequireDefault(_base); - // Merge lines that may contain windows new lines - if (line === '\n' && lastLineLastChar === '\r') { - retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n'; - } else { - if (this.ignoreTrim) { - line = line.trim(); - // add a newline unless this is the last line. - if (i < lines.length - 1) { - line += '\n'; +/*istanbul ignore end*/ +var /*istanbul ignore start*/_character = require('./diff/character') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_word = require('./diff/word') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_line = require('./diff/line') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_sentence = require('./diff/sentence') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_css = require('./diff/css') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_json = require('./diff/json') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_array = require('./diff/array') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_apply = require('./patch/apply') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_parse = require('./patch/parse') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_create = require('./patch/create') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_dmp = require('./convert/dmp') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_xml = require('./convert/xml') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +exports. /*istanbul ignore end*/Diff = _base2['default']; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffChars = _character.diffChars; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWords = _word.diffWords; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = _word.diffWordsWithSpace; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffLines = _line.diffLines; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = _line.diffTrimmedLines; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffSentences = _sentence.diffSentences; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffCss = _css.diffCss; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffJson = _json.diffJson; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffArrays = _array.diffArrays; +/*istanbul ignore start*/exports. /*istanbul ignore end*/structuredPatch = _create.structuredPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = _create.createTwoFilesPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = _create.createPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatch = _apply.applyPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = _apply.applyPatches; +/*istanbul ignore start*/exports. /*istanbul ignore end*/parsePatch = _parse.parsePatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToDMP = _dmp.convertChangesToDMP; +/*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToXML = _xml.convertChangesToXML; +/*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = _json.canonicalize; /* See LICENSE file for terms of use */ + +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ + + +},{"./convert/dmp":46,"./convert/xml":47,"./diff/array":48,"./diff/base":49,"./diff/character":50,"./diff/css":51,"./diff/json":52,"./diff/line":53,"./diff/sentence":54,"./diff/word":55,"./patch/apply":57,"./patch/create":58,"./patch/parse":59}],57:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/applyPatch = applyPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = applyPatches; + +var /*istanbul ignore start*/_parse = require('./parse') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_distanceIterator = require('../util/distance-iterator') /*istanbul ignore end*/; + +/*istanbul ignore start*/ +var _distanceIterator2 = _interopRequireDefault(_distanceIterator); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/function applyPatch(source, uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } + + if (Array.isArray(uniDiff)) { + if (uniDiff.length > 1) { + throw new Error('applyPatch only works with a single input.'); + } + + uniDiff = uniDiff[0]; + } + + // Apply the diff to the input + var lines = source.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) /*istanbul ignore start*/{ + return (/*istanbul ignore end*/line === patchContent + ); + }, + errorCount = 0, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0, + offset = 0, + removeEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + addEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + /** + * Checks if the hunk exactly fits on the provided location + */ + function hunkFits(hunk, toPos) { + for (var j = 0; j < hunk.lines.length; j++) { + var line = hunk.lines[j], + operation = line[0], + content = line.substr(1); + + if (operation === ' ' || operation === '-') { + // Context sanity check + if (!compareLine(toPos + 1, lines[toPos], operation, content)) { + errorCount++; + + if (errorCount > fuzzFactor) { + return false; } } - retLines.push(line); + toPos++; } } - return retLines; - }; + return true; + } + + // Search best fit offsets for each hunk based on the previous ones + for (var i = 0; i < hunks.length; i++) { + var hunk = hunks[i], + maxLine = lines.length - hunk.oldLines, + localOffset = 0, + toPos = offset + hunk.oldStart - 1; - var PatchDiff = new Diff(); - PatchDiff.tokenize = function(value) { - var ret = [], - linesAndNewlines = value.split(/(\n|\r\n)/); + var iterator = /*istanbul ignore start*/(0, _distanceIterator2['default']) /*istanbul ignore end*/(toPos, minLine, maxLine); - // Ignore the final empty token that occurs if the string ends with a new line - if (!linesAndNewlines[linesAndNewlines.length - 1]) { - linesAndNewlines.pop(); + for (; localOffset !== undefined; localOffset = iterator()) { + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + break; + } } - // Merge the content and line separators into single tokens - for (var i = 0; i < linesAndNewlines.length; i++) { - var line = linesAndNewlines[i]; + if (localOffset === undefined) { + return false; + } - if (i % 2) { - ret[ret.length - 1] += line; - } else { - ret.push(line); - } + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; + } + + // Apply patch hunks + for (var _i = 0; _i < hunks.length; _i++) { + var _hunk = hunks[_i], + _toPos = _hunk.offset + _hunk.newStart - 1; + if (_hunk.newLines == 0) { + _toPos++; + } + + for (var j = 0; j < _hunk.lines.length; j++) { + var line = _hunk.lines[j], + operation = line[0], + content = line.substr(1), + delimiter = _hunk.linedelimiters[j]; + + if (operation === ' ') { + _toPos++; + } else if (operation === '-') { + lines.splice(_toPos, 1); + delimiters.splice(_toPos, 1); + /* istanbul ignore else */ + } else if (operation === '+') { + lines.splice(_toPos, 0, content); + delimiters.splice(_toPos, 0, delimiter); + _toPos++; + } else if (operation === '\\') { + var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; + if (previousOperation === '+') { + removeEOFNL = true; + } else if (previousOperation === '-') { + addEOFNL = true; + } + } } - return ret; - }; + } - var SentenceDiff = new Diff(); - SentenceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\S.+?[.!?])(?=\s+|$)/)); - }; + // Handle EOFNL insertion/removal + if (removeEOFNL) { + while (!lines[lines.length - 1]) { + lines.pop(); + delimiters.pop(); + } + } else if (addEOFNL) { + lines.push(''); + delimiters.push('\n'); + } + for (var _k = 0; _k < lines.length - 1; _k++) { + lines[_k] = lines[_k] + delimiters[_k]; + } + return lines.join(''); +} - var JsonDiff = new Diff(); - // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a - // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - JsonDiff.useLongestToken = true; - JsonDiff.tokenize = LineDiff.tokenize; - JsonDiff.equals = function(left, right) { - return LineDiff.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); - }; +// Wrapper that supports multiple file patches via callbacks. +function applyPatches(uniDiff, options) { + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } - var JsDiff = { - Diff: Diff, + var currentIndex = 0; + function processIndex() { + var index = uniDiff[currentIndex++]; + if (!index) { + return options.complete(); + } - diffChars: function(oldStr, newStr, callback) { return CharDiff.diff(oldStr, newStr, callback); }, - diffWords: function(oldStr, newStr, callback) { return WordDiff.diff(oldStr, newStr, callback); }, - diffWordsWithSpace: function(oldStr, newStr, callback) { return WordWithSpaceDiff.diff(oldStr, newStr, callback); }, - diffLines: function(oldStr, newStr, callback) { return LineDiff.diff(oldStr, newStr, callback); }, - diffTrimmedLines: function(oldStr, newStr, callback) { return TrimmedLineDiff.diff(oldStr, newStr, callback); }, + options.loadFile(index, function (err, data) { + if (err) { + return options.complete(err); + } - diffSentences: function(oldStr, newStr, callback) { return SentenceDiff.diff(oldStr, newStr, callback); }, + var updatedContent = applyPatch(data, index, options); + options.patched(index, updatedContent, function (err) { + if (err) { + return options.complete(err); + } - diffCss: function(oldStr, newStr, callback) { return CssDiff.diff(oldStr, newStr, callback); }, - diffJson: function(oldObj, newObj, callback) { - return JsonDiff.diff( - typeof oldObj === 'string' ? oldObj : JSON.stringify(canonicalize(oldObj), undefined, ' '), - typeof newObj === 'string' ? newObj : JSON.stringify(canonicalize(newObj), undefined, ' '), - callback - ); - }, + processIndex(); + }); + }); + } + processIndex(); +} - createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) { - var ret = []; - if (oldFileName == newFileName) { - ret.push('Index: ' + oldFileName); - } - ret.push('==================================================================='); - ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); - ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); +},{"../util/distance-iterator":60,"./parse":59}],58:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; - var diff = PatchDiff.diff(oldStr, newStr); - diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier +exports.__esModule = true; +exports. /*istanbul ignore end*/structuredPatch = structuredPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = createTwoFilesPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = createPatch; - // Formats a given set of lines for printing as context lines in a patch - function contextLines(lines) { - return map(lines, function(entry) { return ' ' + entry; }); - } +var /*istanbul ignore start*/_line = require('../diff/line') /*istanbul ignore end*/; - // Outputs the no newline at end of file warning if needed - function eofNL(curRange, i, current) { - var last = diff[diff.length - 2], - isLast = i === diff.length - 2, - isLastOfType = i === diff.length - 3 && current.added !== last.added; +/*istanbul ignore start*/ +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } - // Figure out if this is the last line for the given file and missing NL - if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) { - curRange.push('\\ No newline at end of file'); +/*istanbul ignore end*/function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + if (!options) { + options = {}; + } + if (typeof options.context === 'undefined') { + options.context = 4; + } + + var diff = /*istanbul ignore start*/(0, _line.diffLines) /*istanbul ignore end*/(oldStr, newStr, options); + diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + + var hunks = []; + var oldRangeStart = 0, + newRangeStart = 0, + curRange = [], + oldLine = 1, + newLine = 1; + /*istanbul ignore start*/ + var _loop = function _loop( /*istanbul ignore end*/i) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + /*istanbul ignore start*/ + var _curRange; + + /*istanbul ignore end*/ + // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; } } - var oldRangeStart = 0, newRangeStart = 0, curRange = [], - oldLine = 1, newLine = 1; - for (var i = 0; i < diff.length; i++) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - // If we have previous context, start with that - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = contextLines(prev.lines.slice(-4)); - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; - } - } - - // Output our changes - curRange.push.apply(curRange, map(lines, function(entry) { - return (current.added ? '+' : '-') + entry; - })); - eofNL(curRange, i, current); + // Output our changes + /*istanbul ignore start*/(_curRange = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); - // Track the updated file position - if (current.added) { - newLine += lines.length; - } else { - oldLine += lines.length; - } + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + /*istanbul ignore start*/ + var _curRange2; + + /*istanbul ignore end*/ + // Overlapping + /*istanbul ignore start*/(_curRange2 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines))); } else { - // Identical context lines. Track line changes - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= 8 && i < diff.length - 2) { - // Overlapping - curRange.push.apply(curRange, contextLines(lines)); - } else { - // end the range and output - var contextSize = Math.min(lines.length, 4); - ret.push( - '@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize) - + ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize) - + ' @@'); - ret.push.apply(ret, curRange); - ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); - if (lines.length <= 4) { - eofNL(ret, i, current); - } - - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; + /*istanbul ignore start*/ + var _curRange3; + + /*istanbul ignore end*/ + // end the range and output + var contextSize = Math.min(lines.length, options.context); + /*istanbul ignore start*/(_curRange3 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines.slice(0, contextSize)))); + + var hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + if (i >= diff.length - 2 && lines.length <= options.context) { + // EOF is inside this hunk + var oldEOFNewline = /\n$/.test(oldStr); + var newEOFNewline = /\n$/.test(newStr); + if (lines.length == 0 && !oldEOFNewline) { + // special case: old has no eol and no trailing context; no-nl can end up before adds + curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); + } else if (!oldEOFNewline || !newEOFNewline) { + curRange.push('\\ No newline at end of file'); } } - oldLine += lines.length; - newLine += lines.length; + hunks.push(hunk); + + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; } } + oldLine += lines.length; + newLine += lines.length; + } + }; - return ret.join('\n') + '\n'; - }, + for (var i = 0; i < diff.length; i++) { + /*istanbul ignore start*/ + _loop( /*istanbul ignore end*/i); + } - createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { - return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader); - }, + return { + oldFileName: oldFileName, newFileName: newFileName, + oldHeader: oldHeader, newHeader: newHeader, + hunks: hunks + }; +} - applyPatch: function(oldStr, uniDiff) { - var diffstr = uniDiff.split('\n'), - hunks = [], - i = 0, - remEOFNL = false, - addEOFNL = false; +function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); - // Skip to the first change hunk - while (i < diffstr.length && !(/^@@/.test(diffstr[i]))) { - i++; + var ret = []; + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } + ret.push('==================================================================='); + ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); + ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); + + for (var i = 0; i < diff.hunks.length; i++) { + var hunk = diff.hunks[i]; + ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); + ret.push.apply(ret, hunk.lines); + } + + return ret.join('\n') + '\n'; +} + +function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { + return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); +} + + +},{"../diff/line":53}],59:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/parsePatch = parsePatch; +function parsePatch(uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], + list = [], + i = 0; + + function parseIndex() { + var index = {}; + list.push(index); + + // Parse diff metadata + while (i < diffstr.length) { + var line = diffstr[i]; + + // File header found, end parsing diff metadata + if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { + break; } - // Parse the unified diff - for (; i < diffstr.length; i++) { - if (diffstr[i][0] === '@') { - var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); - hunks.unshift({ - start: chnukHeader[3], - oldlength: +chnukHeader[2], - removed: [], - newlength: chnukHeader[4], - added: [] - }); - } else if (diffstr[i][0] === '+') { - hunks[0].added.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === '-') { - hunks[0].removed.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === ' ') { - hunks[0].added.push(diffstr[i].substr(1)); - hunks[0].removed.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === '\\') { - if (diffstr[i - 1][0] === '+') { - remEOFNL = true; - } else if (diffstr[i - 1][0] === '-') { - addEOFNL = true; - } - } + // Diff index + var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); + if (header) { + index.index = header[1]; } - // Apply the diff to the input - var lines = oldStr.split('\n'); - for (i = hunks.length - 1; i >= 0; i--) { - var hunk = hunks[i]; - // Sanity check the input string. Bail if we don't match. - for (var j = 0; j < hunk.oldlength; j++) { - if (lines[hunk.start - 1 + j] !== hunk.removed[j]) { - return false; - } - } - Array.prototype.splice.apply(lines, [hunk.start - 1, hunk.oldlength].concat(hunk.added)); + i++; + } + + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); + parseFileHeader(index); + + // Parse hunks + index.hunks = []; + + while (i < diffstr.length) { + var _line = diffstr[i]; + + if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + break; + } else if (/^@@/.test(_line)) { + index.hunks.push(parseHunk()); + } else if (_line && options.strict) { + // Ignore unexpected content unless in strict mode + throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); + } else { + i++; } + } + } + + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. + function parseFileHeader(index) { + var headerPattern = /^(---|\+\+\+)\s+([\S ]*)(?:\t(.*?)\s*)?$/; + var fileHeader = headerPattern.exec(diffstr[i]); + if (fileHeader) { + var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; + index[keyPrefix + 'FileName'] = fileHeader[2]; + index[keyPrefix + 'Header'] = fileHeader[3]; - // Handle EOFNL insertion/removal - if (remEOFNL) { - while (!lines[lines.length - 1]) { - lines.pop(); + i++; + } + } + + // Parses a hunk + // This assumes that we are at the start of a hunk. + function parseHunk() { + var chunkHeaderIndex = i, + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + + var hunk = { + oldStart: +chunkHeader[1], + oldLines: +chunkHeader[2] || 1, + newStart: +chunkHeader[3], + newLines: +chunkHeader[4] || 1, + lines: [], + linedelimiters: [] + }; + + var addCount = 0, + removeCount = 0; + for (; i < diffstr.length; i++) { + // Lines starting with '---' could be mistaken for the "remove line" operation + // But they could be the header for the next file. Therefore prune such cases out. + if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { + break; + } + var operation = diffstr[i][0]; + + if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { + hunk.lines.push(diffstr[i]); + hunk.linedelimiters.push(delimiters[i] || '\n'); + + if (operation === '+') { + addCount++; + } else if (operation === '-') { + removeCount++; + } else if (operation === ' ') { + addCount++; + removeCount++; } - } else if (addEOFNL) { - lines.push(''); + } else { + break; } - return lines.join('\n'); - }, + } - convertChangesToXML: function(changes) { - var ret = []; - for (var i = 0; i < changes.length; i++) { - var change = changes[i]; - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } + // Handle the empty block count case + if (!addCount && hunk.newLines === 1) { + hunk.newLines = 0; + } + if (!removeCount && hunk.oldLines === 1) { + hunk.oldLines = 0; + } - ret.push(escapeHTML(change.value)); + // Perform optional sanity checking + if (options.strict) { + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + } - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } + return hunk; + } + + while (i < diffstr.length) { + parseIndex(); + } + + return list; +} + + +},{}],60:[function(require,module,exports){ +/*istanbul ignore start*/"use strict"; + +exports.__esModule = true; + +exports["default"] = /*istanbul ignore end*/function (start, minLine, maxLine) { + var wantForward = true, + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; + + return function iterator() { + if (wantForward && !forwardExhausted) { + if (backwardExhausted) { + localOffset++; + } else { + wantForward = false; } - return ret.join(''); - }, - // See: http://code.google.com/p/google-diff-match-patch/wiki/API - convertChangesToDMP: function(changes) { - var ret = [], - change, - operation; - for (var i = 0; i < changes.length; i++) { - change = changes[i]; - if (change.added) { - operation = 1; - } else if (change.removed) { - operation = -1; - } else { - operation = 0; - } + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) + if (start + localOffset <= maxLine) { + return localOffset; + } + + forwardExhausted = true; + } - ret.push([operation, change.value]); + if (!backwardExhausted) { + if (!forwardExhausted) { + wantForward = true; + } + + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location + if (minLine <= start - localOffset) { + return -localOffset++; } - return ret; - }, - canonicalize: canonicalize + backwardExhausted = true; + return iterator(); + } + + // We tried to fit hunk before text beginning and beyond text lenght, then + // hunk can't fit on the text. Return undefined }; +}; + - /*istanbul ignore next */ - /*global module */ - if (typeof module !== 'undefined' && module.exports) { - module.exports = JsDiff; - } else if (false) { - /*global define */ - define([], function() { return JsDiff; }); - } else if (typeof global.JsDiff === 'undefined') { - global.JsDiff = JsDiff; +},{}],61:[function(require,module,exports){ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/generateOptions = generateOptions; +function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } } -}(this)); + return defaults; +} -},{}],47:[function(require,module,exports){ + +},{}],62:[function(require,module,exports){ 'use strict'; var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; @@ -9558,7 +10204,7 @@ module.exports = function (str) { return str.replace(matchOperatorsRe, '\\$&'); }; -},{}],48:[function(require,module,exports){ +},{}],63:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -9862,7 +10508,7 @@ function isUndefined(arg) { return arg === void 0; } -},{}],49:[function(require,module,exports){ +},{}],64:[function(require,module,exports){ (function (process){ // Growl - Copyright TJ Holowaychuk (MIT Licensed) @@ -10156,7 +10802,7 @@ function growl(msg, options, fn) { }; }).call(this,require('_process')) -},{"_process":67,"child_process":42,"fs":42,"os":65,"path":42}],50:[function(require,module,exports){ +},{"_process":82,"child_process":42,"fs":42,"os":80,"path":42}],65:[function(require,module,exports){ exports.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m var eLen = nBytes * 8 - mLen - 1 @@ -10242,7 +10888,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { buffer[offset + i - d] |= s * 128 } -},{}],51:[function(require,module,exports){ +},{}],66:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -10267,7 +10913,7 @@ if (typeof Object.create === 'function') { } } -},{}],52:[function(require,module,exports){ +},{}],67:[function(require,module,exports){ /*! * Determine if an object is a Buffer * @@ -10290,14 +10936,14 @@ function isSlowBuffer (obj) { return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) } -},{}],53:[function(require,module,exports){ +},{}],68:[function(require,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; -},{}],54:[function(require,module,exports){ +},{}],69:[function(require,module,exports){ (function (global){ /*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ ;(function () { @@ -11203,7 +11849,7 @@ module.exports = Array.isArray || function (arr) { }).call(this); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],55:[function(require,module,exports){ +},{}],70:[function(require,module,exports){ /** * lodash 3.2.0 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11232,7 +11878,7 @@ function baseAssign(object, source) { module.exports = baseAssign; -},{"lodash._basecopy":56,"lodash.keys":63}],56:[function(require,module,exports){ +},{"lodash._basecopy":71,"lodash.keys":78}],71:[function(require,module,exports){ /** * lodash 3.0.1 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11266,7 +11912,7 @@ function baseCopy(source, props, object) { module.exports = baseCopy; -},{}],57:[function(require,module,exports){ +},{}],72:[function(require,module,exports){ /** * lodash 3.0.3 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11325,7 +11971,7 @@ function isObject(value) { module.exports = baseCreate; -},{}],58:[function(require,module,exports){ +},{}],73:[function(require,module,exports){ /** * lodash 3.9.1 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11464,7 +12110,7 @@ function isNative(value) { module.exports = getNative; -},{}],59:[function(require,module,exports){ +},{}],74:[function(require,module,exports){ /** * lodash 3.0.9 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11598,7 +12244,7 @@ function isObject(value) { module.exports = isIterateeCall; -},{}],60:[function(require,module,exports){ +},{}],75:[function(require,module,exports){ /** * lodash 3.1.1 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -11655,7 +12301,7 @@ function create(prototype, properties, guard) { module.exports = create; -},{"lodash._baseassign":55,"lodash._basecreate":57,"lodash._isiterateecall":59}],61:[function(require,module,exports){ +},{"lodash._baseassign":70,"lodash._basecreate":72,"lodash._isiterateecall":74}],76:[function(require,module,exports){ /** * lodash (Custom Build) * Build: `lodash modularize exports="npm" -o ./` @@ -11886,7 +12532,7 @@ function isObjectLike(value) { module.exports = isArguments; -},{}],62:[function(require,module,exports){ +},{}],77:[function(require,module,exports){ /** * lodash 3.0.4 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -12068,7 +12714,7 @@ function isNative(value) { module.exports = isArray; -},{}],63:[function(require,module,exports){ +},{}],78:[function(require,module,exports){ /** * lodash 3.1.2 (Custom Build) * Build: `lodash modern modularize exports="npm" -o ./` @@ -12306,7 +12952,7 @@ function keysIn(object) { module.exports = keys; -},{"lodash._getnative":58,"lodash.isarguments":61,"lodash.isarray":62}],64:[function(require,module,exports){ +},{"lodash._getnative":73,"lodash.isarguments":76,"lodash.isarray":77}],79:[function(require,module,exports){ (function (process){ var path = require('path'); var fs = require('fs'); @@ -12408,7 +13054,7 @@ mkdirP.sync = function sync (p, opts, made) { }; }).call(this,require('_process')) -},{"_process":67,"fs":42,"path":42}],65:[function(require,module,exports){ +},{"_process":82,"fs":42,"path":42}],80:[function(require,module,exports){ exports.endianness = function () { return 'LE' }; exports.hostname = function () { @@ -12455,7 +13101,7 @@ exports.tmpdir = exports.tmpDir = function () { exports.EOL = '\n'; -},{}],66:[function(require,module,exports){ +},{}],81:[function(require,module,exports){ (function (process){ 'use strict'; @@ -12502,7 +13148,7 @@ function nextTick(fn, arg1, arg2, arg3) { } }).call(this,require('_process')) -},{"_process":67}],67:[function(require,module,exports){ +},{"_process":82}],82:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -12684,10 +13330,10 @@ process.chdir = function (dir) { }; process.umask = function() { return 0; }; -},{}],68:[function(require,module,exports){ +},{}],83:[function(require,module,exports){ module.exports = require("./lib/_stream_duplex.js") -},{"./lib/_stream_duplex.js":69}],69:[function(require,module,exports){ +},{"./lib/_stream_duplex.js":84}],84:[function(require,module,exports){ // a duplex stream is just a stream that is both readable and writable. // Since JS doesn't have multiple prototypal inheritance, this class // prototypally inherits from Readable, and then parasitically from @@ -12763,7 +13409,7 @@ function forEach(xs, f) { f(xs[i], i); } } -},{"./_stream_readable":71,"./_stream_writable":73,"core-util-is":45,"inherits":51,"process-nextick-args":66}],70:[function(require,module,exports){ +},{"./_stream_readable":86,"./_stream_writable":88,"core-util-is":45,"inherits":66,"process-nextick-args":81}],85:[function(require,module,exports){ // a passthrough stream. // basically just the most minimal sort of Transform stream. // Every written chunk gets output as-is. @@ -12790,7 +13436,7 @@ function PassThrough(options) { PassThrough.prototype._transform = function (chunk, encoding, cb) { cb(null, chunk); }; -},{"./_stream_transform":72,"core-util-is":45,"inherits":51}],71:[function(require,module,exports){ +},{"./_stream_transform":87,"core-util-is":45,"inherits":66}],86:[function(require,module,exports){ (function (process){ 'use strict'; @@ -13730,7 +14376,7 @@ function indexOf(xs, x) { return -1; } }).call(this,require('_process')) -},{"./_stream_duplex":69,"./internal/streams/BufferList":74,"_process":67,"buffer":44,"buffer-shims":43,"core-util-is":45,"events":48,"inherits":51,"isarray":53,"process-nextick-args":66,"string_decoder/":80,"util":40}],72:[function(require,module,exports){ +},{"./_stream_duplex":84,"./internal/streams/BufferList":89,"_process":82,"buffer":44,"buffer-shims":43,"core-util-is":45,"events":63,"inherits":66,"isarray":68,"process-nextick-args":81,"string_decoder/":95,"util":40}],87:[function(require,module,exports){ // a transform stream is a readable/writable stream where you do // something with the data. Sometimes it's called a "filter", // but that's not a great name for it, since that implies a thing where @@ -13911,7 +14557,7 @@ function done(stream, er) { return stream.push(null); } -},{"./_stream_duplex":69,"core-util-is":45,"inherits":51}],73:[function(require,module,exports){ +},{"./_stream_duplex":84,"core-util-is":45,"inherits":66}],88:[function(require,module,exports){ (function (process){ // A bit simpler than readable streams. // Implement an async ._write(chunk, encoding, cb), and it'll handle all @@ -14440,7 +15086,7 @@ function CorkedRequest(state) { }; } }).call(this,require('_process')) -},{"./_stream_duplex":69,"_process":67,"buffer":44,"buffer-shims":43,"core-util-is":45,"events":48,"inherits":51,"process-nextick-args":66,"util-deprecate":81}],74:[function(require,module,exports){ +},{"./_stream_duplex":84,"_process":82,"buffer":44,"buffer-shims":43,"core-util-is":45,"events":63,"inherits":66,"process-nextick-args":81,"util-deprecate":96}],89:[function(require,module,exports){ 'use strict'; var Buffer = require('buffer').Buffer; @@ -14505,10 +15151,10 @@ BufferList.prototype.concat = function (n) { } return ret; }; -},{"buffer":44,"buffer-shims":43}],75:[function(require,module,exports){ +},{"buffer":44,"buffer-shims":43}],90:[function(require,module,exports){ module.exports = require("./lib/_stream_passthrough.js") -},{"./lib/_stream_passthrough.js":70}],76:[function(require,module,exports){ +},{"./lib/_stream_passthrough.js":85}],91:[function(require,module,exports){ (function (process){ var Stream = (function (){ try { @@ -14528,13 +15174,13 @@ if (!process.browser && process.env.READABLE_STREAM === 'disable' && Stream) { } }).call(this,require('_process')) -},{"./lib/_stream_duplex.js":69,"./lib/_stream_passthrough.js":70,"./lib/_stream_readable.js":71,"./lib/_stream_transform.js":72,"./lib/_stream_writable.js":73,"_process":67}],77:[function(require,module,exports){ +},{"./lib/_stream_duplex.js":84,"./lib/_stream_passthrough.js":85,"./lib/_stream_readable.js":86,"./lib/_stream_transform.js":87,"./lib/_stream_writable.js":88,"_process":82}],92:[function(require,module,exports){ module.exports = require("./lib/_stream_transform.js") -},{"./lib/_stream_transform.js":72}],78:[function(require,module,exports){ +},{"./lib/_stream_transform.js":87}],93:[function(require,module,exports){ module.exports = require("./lib/_stream_writable.js") -},{"./lib/_stream_writable.js":73}],79:[function(require,module,exports){ +},{"./lib/_stream_writable.js":88}],94:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -14663,7 +15309,7 @@ Stream.prototype.pipe = function(dest, options) { return dest; }; -},{"events":48,"inherits":51,"readable-stream/duplex.js":68,"readable-stream/passthrough.js":75,"readable-stream/readable.js":76,"readable-stream/transform.js":77,"readable-stream/writable.js":78}],80:[function(require,module,exports){ +},{"events":63,"inherits":66,"readable-stream/duplex.js":83,"readable-stream/passthrough.js":90,"readable-stream/readable.js":91,"readable-stream/transform.js":92,"readable-stream/writable.js":93}],95:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -14886,7 +15532,7 @@ function base64DetectIncompleteChar(buffer) { this.charLength = this.charReceived ? 3 : 0; } -},{"buffer":44}],81:[function(require,module,exports){ +},{"buffer":44}],96:[function(require,module,exports){ (function (global){ /** @@ -14957,16 +15603,16 @@ function config (name) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],82:[function(require,module,exports){ -arguments[4][51][0].apply(exports,arguments) -},{"dup":51}],83:[function(require,module,exports){ +},{}],97:[function(require,module,exports){ +arguments[4][66][0].apply(exports,arguments) +},{"dup":66}],98:[function(require,module,exports){ module.exports = function isBuffer(arg) { return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function'; } -},{}],84:[function(require,module,exports){ +},{}],99:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // @@ -15556,4 +16202,4 @@ function hasOwnProperty(obj, prop) { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./support/isBuffer":83,"_process":67,"inherits":82}]},{},[1]); +},{"./support/isBuffer":98,"_process":82,"inherits":97}]},{},[1]); diff --git a/package.json b/package.json index 2e1353c727..562a182a8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mocha", - "version": "3.2.0", + "version": "3.4.2", "description": "simple, flexible, fun test framework", "keywords": [ "mocha", @@ -299,11 +299,12 @@ "npm": ">= 1.4.x" }, "scripts": { + "lint": "eslint . bin/*", "test": "make test && make clean", "precoverage": "rm -rf coverage", "coverage": "COVERAGE=true npm run test", "postcoverage": "istanbul-combine -d coverage -r lcov -r html coverage/reports/*/*.json", - "preversion": "make test && make mocha.js && git add mocha.js" + "preversion": "make test && rm mocha.js && make mocha.js && git add mocha.js" }, "dependencies": { "browser-stdout": "1.3.0", @@ -323,6 +324,11 @@ "browserify": "^13.0.0", "coffee-script": "^1.10.0", "coveralls": "^2.11.15", + "eslint": "^3.11.1", + "eslint-config-semistandard": "^7.0.0", + "eslint-config-standard": "^6.2.1", + "eslint-plugin-promise": "^3.4.0", + "eslint-plugin-standard": "2.0.1", "expect.js": "^0.3.1", "istanbul-combine": "^0.3.0", "karma": "1.3.0", @@ -330,14 +336,13 @@ "karma-chrome-launcher": "^2.0.0", "karma-expect": "^1.1.2", "karma-mocha": "^1.3.0", - "karma-phantomjs-launcher": "^1.0.2", + "karma-phantomjs-launcher": "0.2.3", "karma-sauce-launcher": "coderbyheart/karma-sauce-launcher", "karma-spec-reporter": "0.0.26", "nyc": "^10.0.0", "os-name": "^2.0.1", "phantomjs": "1.9.8", "rimraf": "^2.5.2", - "semistandard": "^9.2.1", "should": "^11.1.1", "through2": "^2.0.1", "watchify": "^3.7.0" @@ -364,17 +369,5 @@ "supports-color": false }, "homepage": "https://mochajs.org", - "logo": "https://cldup.com/S9uQ-cOLYz.svg", - "greenkeeper": { - "ignore": [ - "phantomjs", - "lodash.create" - ] - }, - "semistandard": { - "ignore": [ - "/mocha.js", - "/lib/to-iso-string/**/*.js" - ] - } + "logo": "https://cldup.com/S9uQ-cOLYz.svg" } diff --git a/test/.eslintrc.yaml b/test/.eslintrc.yaml new file mode 100644 index 0000000000..a32fd84d48 --- /dev/null +++ b/test/.eslintrc.yaml @@ -0,0 +1,5 @@ +env: + mocha: true +globals: + expect: false + assert: false diff --git a/test/browser-fixtures/bdd.fixture.js b/test/browser-fixtures/bdd.fixture.js index d19993676a..5fe802f74a 100644 --- a/test/browser-fixtures/bdd.fixture.js +++ b/test/browser-fixtures/bdd.fixture.js @@ -2,5 +2,7 @@ /* eslint-env browser */ +process.stdout = require('browser-stdout')(); + window.mocha.timeout(200) .ui('bdd'); diff --git a/test/browser-fixtures/exports.fixture.js b/test/browser-fixtures/exports.fixture.js index 97b8b97cbe..a4c25cff99 100644 --- a/test/browser-fixtures/exports.fixture.js +++ b/test/browser-fixtures/exports.fixture.js @@ -2,5 +2,7 @@ /* eslint-env browser */ +process.stdout = require('browser-stdout')(); + window.mocha.timeout(200) .ui('exports'); diff --git a/test/browser-fixtures/qunit.fixture.js b/test/browser-fixtures/qunit.fixture.js index a3fd887940..79b49498d5 100644 --- a/test/browser-fixtures/qunit.fixture.js +++ b/test/browser-fixtures/qunit.fixture.js @@ -2,5 +2,7 @@ /* eslint-env browser */ +process.stdout = require('browser-stdout')(); + window.mocha.timeout(200) .ui('qunit'); diff --git a/test/browser-fixtures/tdd.fixture.js b/test/browser-fixtures/tdd.fixture.js index 0c36f0c54b..2fdc8f758c 100644 --- a/test/browser-fixtures/tdd.fixture.js +++ b/test/browser-fixtures/tdd.fixture.js @@ -2,5 +2,7 @@ /* eslint-env browser */ +process.stdout = require('browser-stdout')(); + window.mocha.timeout(200) .ui('tdd'); diff --git a/test/browser/README.md b/test/browser/README.md new file mode 100644 index 0000000000..15e4178b3e --- /dev/null +++ b/test/browser/README.md @@ -0,0 +1,3 @@ +These files need to be run manually by loading the `.html` file(s) in a browser. + +It would be awesome if we could automate that! diff --git a/test/browser/grep.html b/test/browser/grep.html index 0ba47c8533..44bff517df 100644 --- a/test/browser/grep.html +++ b/test/browser/grep.html @@ -11,7 +11,7 @@ if (!expr) throw new Error(msg || 'failed'); } - +
    diff --git a/test/browser/index.html b/test/browser/index.html index 9200b977e5..d8b7cc9f72 100644 --- a/test/browser/index.html +++ b/test/browser/index.html @@ -11,10 +11,10 @@ if (!expr) throw new Error(msg || 'failed'); } - - - - + + + + - - + + - + - +
    diff --git a/test/acceptance/test.coffee b/test/compiler/test.coffee similarity index 100% rename from test/acceptance/test.coffee rename to test/compiler/test.coffee diff --git a/test/acceptance/test.foo b/test/compiler/test.foo similarity index 100% rename from test/acceptance/test.foo rename to test/compiler/test.foo diff --git a/test/acceptance/glob/glob.sh b/test/glob/glob.sh similarity index 84% rename from test/acceptance/glob/glob.sh rename to test/glob/glob.sh index 823ba07fb9..c0cb21d3f8 100755 --- a/test/acceptance/glob/glob.sh +++ b/test/glob/glob.sh @@ -7,7 +7,7 @@ cd $SCRIPT_DIR || { exit 1 } -../../../bin/mocha -R json-stream ./*.js > /tmp/mocha-glob.txt || { +../../bin/mocha -R json-stream ./*.js > /tmp/mocha-glob.txt || { echo Globbing ./*.js in `pwd` failed. exit 1 } @@ -17,7 +17,7 @@ cat /tmp/mocha-glob.txt | grep -q -F '["end",{"suites":1,"tests":1,"passes":1,"p exit 1 } -../../../bin/mocha -R json-stream ./*-none.js 2> /tmp/mocha-glob.txt && { +../../bin/mocha -R json-stream ./*-none.js 2> /tmp/mocha-glob.txt && { echo Globbing './*-none.js' in `pwd` failed. exit 1 } @@ -27,7 +27,7 @@ cat /tmp/mocha-glob.txt | grep -q -F 'Could not find any test files matching pat exit 1 } -../../../bin/mocha -R json-stream ./*.js ./*-none.js >& /tmp/mocha-glob.txt || { +../../bin/mocha -R json-stream ./*.js ./*-none.js >& /tmp/mocha-glob.txt || { echo Globbing ./*.js ./*-none.js in `pwd` failed. exit 1 } @@ -43,7 +43,7 @@ cat /tmp/mocha-glob.txt | grep -q -F 'Could not find any test files matching pat # In windows, the shell passes globs unexpanded, executables do expansion if they support it. # Adding single-quotes around the glob below makes bash pass glob unexpanded, # allowing us to test windows-style globbing in bash. -../../../bin/mocha -R json-stream './*.js' > /tmp/mocha-glob.txt || { +../../bin/mocha -R json-stream './*.js' > /tmp/mocha-glob.txt || { echo Globbing './*.js' in `pwd` failed. exit 1 } @@ -53,7 +53,7 @@ cat /tmp/mocha-glob.txt | grep -q -F '["end",{"suites":1,"tests":1,"passes":1,"p exit 1 } -../../../bin/mocha -R json-stream './*-none.js' 2> /tmp/mocha-glob.txt && { +../../bin/mocha -R json-stream './*-none.js' 2> /tmp/mocha-glob.txt && { echo Globbing './*-none.js' in `pwd` failed. exit 1 } diff --git a/test/acceptance/glob/glob.spec.js b/test/glob/glob.spec.js similarity index 100% rename from test/acceptance/glob/glob.spec.js rename to test/glob/glob.spec.js diff --git a/test/integration/fixtures/options/forbid-only/only.js b/test/integration/fixtures/options/forbid-only/only.js new file mode 100644 index 0000000000..c09e804126 --- /dev/null +++ b/test/integration/fixtures/options/forbid-only/only.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('forbid only - test marked with only', function () { + it('test1', function () {}); + it.only('test2', function () {}); + it('test3', function () {}); +}); diff --git a/test/integration/fixtures/options/forbid-only/passed.js b/test/integration/fixtures/options/forbid-only/passed.js new file mode 100644 index 0000000000..6c4d4ac1d4 --- /dev/null +++ b/test/integration/fixtures/options/forbid-only/passed.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('forbid only - `.only` is not used', function () { + it('test1', function () {}); + it('test2', function () {}); + it('test3', function () {}); +}); diff --git a/test/integration/fixtures/options/forbid-pending/passed.js b/test/integration/fixtures/options/forbid-pending/passed.js new file mode 100644 index 0000000000..cd12668f4d --- /dev/null +++ b/test/integration/fixtures/options/forbid-pending/passed.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('forbid pending - all test pass', function () { + it('test1', function () {}); + it('test2', function () {}); + it('test3', function () {}); +}); diff --git a/test/integration/fixtures/options/forbid-pending/pending.js b/test/integration/fixtures/options/forbid-pending/pending.js new file mode 100644 index 0000000000..96abd0bf47 --- /dev/null +++ b/test/integration/fixtures/options/forbid-pending/pending.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('forbid pending - test without function', function () { + it('test1', function () {}); + it('test2'); + it('test3', function () {}); +}); diff --git a/test/integration/fixtures/options/forbid-pending/skip.js b/test/integration/fixtures/options/forbid-pending/skip.js new file mode 100644 index 0000000000..c6fd31d303 --- /dev/null +++ b/test/integration/fixtures/options/forbid-pending/skip.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('forbid pending - test marked with skip', function () { + it('test1', function () {}); + it.skip('test2', function () {}); + it('test3', function () {}); +}); diff --git a/test/integration/fixtures/simple-reporter.js b/test/integration/fixtures/simple-reporter.js new file mode 100644 index 0000000000..d172b2ae66 --- /dev/null +++ b/test/integration/fixtures/simple-reporter.js @@ -0,0 +1,28 @@ +'use strict'; + +var baseReporter = require('../../../lib/reporters/base'); +module.exports = simplereporter; + +function simplereporter (runner) { + baseReporter.call(this, runner); + + runner.on('suite', function (suite) { + console.log('on(\'suite\') called'); + }); + + runner.on('fail', function (test, err) { + console.log('on(\'fail\') called'); + }); + + runner.on('pass', function (test) { + console.log('on(\'pass\') called'); + }); + + runner.on('test end', function (test, err) { + console.log('on(\'test end\') called'); + }); + + runner.on('end', function () { + console.log('on(\'end\') called'); + }); +} diff --git a/test/integration/options.spec.js b/test/integration/options.spec.js index 7e5fe79306..087ec9fdef 100644 --- a/test/integration/options.spec.js +++ b/test/integration/options.spec.js @@ -207,4 +207,56 @@ describe('options', function () { done(); }); }); + + describe('--forbid-only', function () { + before(function () { + args = ['--forbid-only']; + }); + + it('succeeds if there are only passed tests', function (done) { + run('options/forbid-only/passed.js', args, function (err, res) { + assert(!err); + assert.equal(res.code, 0); + done(); + }); + }); + + it('fails if there are tests marked only', function (done) { + run('options/forbid-only/only.js', args, function (err, res) { + assert(!err); + assert.equal(res.code, 1); + done(); + }); + }); + }); + + describe('--forbid-pending', function () { + before(function () { + args = ['--forbid-pending']; + }); + + it('succeeds if there are only passed tests', function (done) { + run('options/forbid-pending/passed.js', args, function (err, res) { + assert(!err); + assert.equal(res.code, 0); + done(); + }); + }); + + it('fails if there are tests marked skip', function (done) { + run('options/forbid-pending/skip.js', args, function (err, res) { + assert(!err); + assert.equal(res.code, 1); + done(); + }); + }); + + it('fails if there are pending tests', function (done) { + run('options/forbid-pending/pending.js', args, function (err, res) { + assert(!err); + assert.equal(res.code, 1); + done(); + }); + }); + }); }); diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 5cfee57037..19b4d8e30c 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -36,7 +36,7 @@ describe('reporters', function () { describe('xunit', function () { it('prints test cases with --reporter-options output (issue: 1864)', function (done) { var randomStr = crypto.randomBytes(8).toString('hex'); - var tmpDir = os.tmpDir().replace(new RegExp(path.sep + '$'), ''); + var tmpDir = os.tmpdir().replace(new RegExp(path.sep + '$'), ''); var tmpFile = tmpDir + path.sep + 'test-issue-1864-' + randomStr + '.xml'; var args = ['--reporter=xunit', '--reporter-options', 'output=' + tmpFile]; @@ -60,4 +60,31 @@ describe('reporters', function () { }); }); }); + + describe('loader', function () { + it('loads a reporter from a path relative to the current working directory', function (done) { + var reporterAtARelativePath = 'test/integration/fixtures/simple-reporter.js'; + + var args = ['--reporter=' + reporterAtARelativePath]; + + run('passing.fixture.js', args, function (err, result) { + assert(!err); + assert.equal(result.code, 0); + done(); + }); + }); + + it('loads a reporter from an absolute path', function (done) { + // Generates an absolute path string + var reporterAtAnAbsolutePath = path.join(process.cwd(), 'test/integration/fixtures/simple-reporter.js'); + + var args = ['--reporter=' + reporterAtAnAbsolutePath]; + + run('passing.fixture.js', args, function (err, result) { + assert(!err); + assert.equal(result.code, 0); + done(); + }); + }); + }); }); diff --git a/test/acceptance/interfaces/bdd.spec.js b/test/interfaces/bdd.spec.js similarity index 100% rename from test/acceptance/interfaces/bdd.spec.js rename to test/interfaces/bdd.spec.js diff --git a/test/acceptance/interfaces/exports.spec.js b/test/interfaces/exports.spec.js similarity index 100% rename from test/acceptance/interfaces/exports.spec.js rename to test/interfaces/exports.spec.js diff --git a/test/acceptance/interfaces/qunit.spec.js b/test/interfaces/qunit.spec.js similarity index 100% rename from test/acceptance/interfaces/qunit.spec.js rename to test/interfaces/qunit.spec.js diff --git a/test/acceptance/interfaces/tdd.spec.js b/test/interfaces/tdd.spec.js similarity index 100% rename from test/acceptance/interfaces/tdd.spec.js rename to test/interfaces/tdd.spec.js diff --git a/test/jsapi/index.js b/test/jsapi/index.js index 42133578e0..430a205250 100644 --- a/test/jsapi/index.js +++ b/test/jsapi/index.js @@ -12,16 +12,16 @@ var mocha = new Mocha({ // mocha.reporter('spec'); require('should'); -mocha.addFile('test/suite.spec.js'); -mocha.addFile('test/runner.spec.js'); -mocha.addFile('test/runnable.spec.js'); -mocha.addFile('test/hook-sync.spec.js'); -mocha.addFile('test/hook-sync-nested.spec.js'); -mocha.addFile('test/hook-async.spec.js'); -mocha.addFile('test/acceptance/duration.spec.js'); -mocha.addFile('test/acceptance/fs.spec.js'); -mocha.addFile('test/acceptance/globals.spec.js'); -mocha.addFile('test/acceptance/timeout.spec.js'); +mocha.addFile('test/unit/suite.spec.js'); +mocha.addFile('test/unit/runner.spec.js'); +mocha.addFile('test/unit/runnable.spec.js'); +mocha.addFile('test/unit/hook-sync.spec.js'); +mocha.addFile('test/unit/hook-sync-nested.spec.js'); +mocha.addFile('test/unit/hook-async.spec.js'); +mocha.addFile('test/unit/duration.spec.js'); +mocha.addFile('test/node-unit/fs.spec.js'); +mocha.addFile('test/unit/globals.spec.js'); +mocha.addFile('test/unit/timeout.spec.js'); mocha.run(function () { console.log('done'); diff --git a/test/acceptance/misc/exit.spec.js b/test/misc/exit.spec.js similarity index 100% rename from test/acceptance/misc/exit.spec.js rename to test/misc/exit.spec.js diff --git a/test/acceptance/misc/many.spec.js b/test/misc/many.spec.js similarity index 100% rename from test/acceptance/misc/many.spec.js rename to test/misc/many.spec.js diff --git a/test/acceptance/misc/nontty.spec.js b/test/misc/nontty.spec.js similarity index 100% rename from test/acceptance/misc/nontty.spec.js rename to test/misc/nontty.spec.js diff --git a/test/acceptance/misc/only/bdd-require.spec.js b/test/misc/only/bdd-require.spec.js similarity index 90% rename from test/acceptance/misc/only/bdd-require.spec.js rename to test/misc/only/bdd-require.spec.js index 14710c4cd2..1c90d7acda 100644 --- a/test/acceptance/misc/only/bdd-require.spec.js +++ b/test/misc/only/bdd-require.spec.js @@ -2,7 +2,7 @@ /* jshint node: true */ -var mocha = require('../../../../lib/mocha'); +var mocha = require('../../../lib/mocha'); var beforeEach = mocha.beforeEach; var it = mocha.it; diff --git a/test/acceptance/misc/only/bdd.spec.js b/test/misc/only/bdd.spec.js similarity index 100% rename from test/acceptance/misc/only/bdd.spec.js rename to test/misc/only/bdd.spec.js diff --git a/test/acceptance/misc/only/global/bdd.spec.js b/test/misc/only/global/bdd.spec.js similarity index 100% rename from test/acceptance/misc/only/global/bdd.spec.js rename to test/misc/only/global/bdd.spec.js diff --git a/test/acceptance/misc/only/global/qunit.spec.js b/test/misc/only/global/qunit.spec.js similarity index 100% rename from test/acceptance/misc/only/global/qunit.spec.js rename to test/misc/only/global/qunit.spec.js diff --git a/test/acceptance/misc/only/global/tdd.spec.js b/test/misc/only/global/tdd.spec.js similarity index 100% rename from test/acceptance/misc/only/global/tdd.spec.js rename to test/misc/only/global/tdd.spec.js diff --git a/test/acceptance/misc/only/qunit.spec.js b/test/misc/only/qunit.spec.js similarity index 100% rename from test/acceptance/misc/only/qunit.spec.js rename to test/misc/only/qunit.spec.js diff --git a/test/acceptance/misc/only/tdd.spec.js b/test/misc/only/tdd.spec.js similarity index 100% rename from test/acceptance/misc/only/tdd.spec.js rename to test/misc/only/tdd.spec.js diff --git a/test/color.spec.js b/test/node-unit/color.spec.js similarity index 100% rename from test/color.spec.js rename to test/node-unit/color.spec.js diff --git a/test/acceptance/file-utils.spec.js b/test/node-unit/file-utils.spec.js similarity index 98% rename from test/acceptance/file-utils.spec.js rename to test/node-unit/file-utils.spec.js index 12dbddf878..540589821d 100644 --- a/test/acceptance/file-utils.spec.js +++ b/test/node-unit/file-utils.spec.js @@ -8,7 +8,7 @@ var mkdirp = require('mkdirp'); var rimraf = require('rimraf'); describe('file utils', function () { - var tmpDir = path.join(os.tmpDir(), 'mocha-file-lookup'); + var tmpDir = path.join(os.tmpdir(), 'mocha-file-lookup'); var existsSync = fs.existsSync; var tmpFile = path.join.bind(path, tmpDir); var symlinkSupported = false; diff --git a/test/acceptance/fs.spec.js b/test/node-unit/fs.spec.js similarity index 100% rename from test/acceptance/fs.spec.js rename to test/node-unit/fs.spec.js diff --git a/test/http-meta-2.spec.js b/test/node-unit/http-meta-2.spec.js similarity index 97% rename from test/http-meta-2.spec.js rename to test/node-unit/http-meta-2.spec.js index 4530969041..18b4158467 100644 --- a/test/http-meta-2.spec.js +++ b/test/node-unit/http-meta-2.spec.js @@ -73,6 +73,10 @@ describe('http server', function () { server.listen(PORT, done); }); + beforeEach(function () { + this.timeout(2000); + }); + after(function () { server.close(); }); diff --git a/test/http-meta.spec.js b/test/node-unit/http-meta.spec.js similarity index 95% rename from test/http-meta.spec.js rename to test/node-unit/http-meta.spec.js index c92dbd2307..2ed7ed9204 100644 --- a/test/http-meta.spec.js +++ b/test/node-unit/http-meta.spec.js @@ -46,6 +46,10 @@ describe('http requests', function () { server.listen(PORT, done); }); + beforeEach(function () { + this.timeout(2000); + }); + after(function () { server.close(); }); diff --git a/test/acceptance/http.spec.js b/test/node-unit/http.spec.js similarity index 100% rename from test/acceptance/http.spec.js rename to test/node-unit/http.spec.js diff --git a/test/node-unit/stack-trace-filter.spec.js b/test/node-unit/stack-trace-filter.spec.js new file mode 100644 index 0000000000..72ce67a206 --- /dev/null +++ b/test/node-unit/stack-trace-filter.spec.js @@ -0,0 +1,156 @@ +'use strict'; + +var path = require('path'); +var utils = require('../../lib/utils'); + +describe('stackTraceFilter()', function () { + describe('on node', function () { + var filter = utils.stackTraceFilter(); + + describe('on POSIX OS', function () { + before(function () { + if (path.sep !== '/') { + this.skip(); + } + }); + + it('should get a stack-trace as a string and prettify it', function () { + var stack = [ + 'AssertionError: foo bar', + 'at EventEmitter. (/usr/local/dev/test.js:16:12)', + 'at Context. (/usr/local/dev/test.js:19:5)', + 'Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:244:7)', + 'Runner.runTest (/usr/local/lib/node_modules/mocha/lib/runner.js:374:10)', + '/usr/local/lib/node_modules/mocha/lib/runner.js:452:12', + 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:299:14)', + '/usr/local/lib/node_modules/mocha/lib/runner.js:309:7', + 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)', + 'Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)', + 'at processImmediate [as _immediateCallback] (timers.js:321:17)' + ]; + filter(stack.join('\n')) + .should + .equal(stack.slice(0, 3) + .join('\n')); + + stack = [ + 'AssertionError: bar baz', + 'at /usr/local/dev/some-test-file.js:25:8', + 'at tryCatcher (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/util.js:24:31)', + 'at Promise._resolveFromResolver (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:439:31)', + 'at new Promise (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:53:37)', + 'at yourFunction (/usr/local/dev/own/tmp/test1.js:24:13)', + 'at Context. (/usr/local/dev/some-test-file:30:4)', + 'Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:218:15)', + 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)', + 'Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)', + 'at processImmediate [as _immediateCallback] (timers.js:321:17)' + ]; + + filter(stack.join('\n')) + .should + .equal(stack.slice(0, 7) + .join('\n')); + }); + + it('does not ignore other bower_components and components', function () { + var stack = [ + 'Error: failed', + 'at assert (index.html:11:26)', + 'at Context. (test.js:17:18)', + 'at bower_components/should/should.js:4827:7', + 'at next (file:///.../bower_components/should/should.js:4766:23)', + 'at components/should/5.0.0/should.js:4827:7', + 'at next (file:///.../components/should/5.0.0/should.js:4766:23)', + 'at file:///.../bower_components/mocha/mocha.js:4794:5', + 'at timeslice (.../components/mocha/mocha.js:6218:27)', + 'at Test.require.register.Runnable.run (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4463:15)', + 'at Runner.require.register.Runner.runTest (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4892:10)', + 'at file:///.../components/mochajs/mocha/2.1.0/mocha.js:4970:12', + 'at next (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4817:14)' + ]; + filter(stack.join('\n')) + .should + .equal(stack.slice(0, 7) + .join('\n')); + }); + + it('should replace absolute with relative paths', function () { + var stack = [ + 'Error: ' + process.cwd() + '/bla.js has a problem', + 'at foo (' + process.cwd() + '/foo/index.js:13:226)', + 'at bar (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:11:26)' + ]; + + var expected = [ + 'Error: ' + process.cwd() + '/bla.js has a problem', + 'at foo (foo/index.js:13:226)', + 'at bar (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:11:26)' + ]; + + filter(stack.join('\n')) + .should + .equal(expected.join('\n')); + }); + }); + + describe('on Windows', function () { + before(function () { + if (path.sep === '/') { + this.skip(); + } + }); + + it('should work on Windows', function () { + var stack = [ + 'Error: failed', + 'at Context. (C:\\Users\\ishida\\src\\test\\test\\mytest.js:5:9)', + 'at callFn (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runnable.js:326:21)', + 'at Test.Runnable.run (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runnable.js:319:7)', + 'at Runner.runTest (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:422:10)', + 'at C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:528:12', + 'at next (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:342:14)', + 'at C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:352:7', + 'at next (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:284:14)', + 'at Immediate._onImmediate (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:320:5)' + ]; + filter(stack.join('\n')) + .should + .equal(stack.slice(0, 2) + .join('\n')); + }); + }); + }); + + describe('on browser', function () { + var filter; + before(function () { + global.document = true; + global.location = {href: 'localhost:3000/foo/bar/index.html'}; + filter = utils.stackTraceFilter(); + }); + it('does not strip out other bower_components', function () { + var stack = [ + 'Error: failed', + 'at assert (index.html:11:26)', + 'at Context. (test.js:17:18)', + 'at bower_components/should/should.js:4827:7', + 'at next (bower_components/should/should.js:4766:23)', + 'at components/should/5.0.0/should.js:4827:7', + 'at next (components/should/5.0.0/should.js:4766:23)', + 'at Runner.require.register.Runner.runTest (node_modules/mocha.js:4892:10)', + 'at localhost:3000/foo/bar/node_modules/mocha.js:4970:12', + 'at next (node_modules/mocha.js:4817:14)' + ]; + filter(stack.join('\n')) + .should + .equal(stack.slice(0, 7) + .join('\n')); + }); + + after(function () { + delete global.document; + delete global.location; + }); + }); +}); diff --git a/test/acceptance/require/a.js b/test/require/a.js similarity index 100% rename from test/acceptance/require/a.js rename to test/require/a.js diff --git a/test/acceptance/require/b.coffee b/test/require/b.coffee similarity index 100% rename from test/acceptance/require/b.coffee rename to test/require/b.coffee diff --git a/test/acceptance/require/c.js b/test/require/c.js similarity index 100% rename from test/acceptance/require/c.js rename to test/require/c.js diff --git a/test/acceptance/require/d.coffee b/test/require/d.coffee similarity index 100% rename from test/acceptance/require/d.coffee rename to test/require/d.coffee diff --git a/test/acceptance/require/require.spec.js b/test/require/require.spec.js similarity index 100% rename from test/acceptance/require/require.spec.js rename to test/require/require.spec.js diff --git a/test/acceptance/context.spec.js b/test/unit/context.spec.js similarity index 100% rename from test/acceptance/context.spec.js rename to test/unit/context.spec.js diff --git a/test/acceptance/duration.spec.js b/test/unit/duration.spec.js similarity index 100% rename from test/acceptance/duration.spec.js rename to test/unit/duration.spec.js diff --git a/test/acceptance/globals.spec.js b/test/unit/globals.spec.js similarity index 100% rename from test/acceptance/globals.spec.js rename to test/unit/globals.spec.js diff --git a/test/grep.spec.js b/test/unit/grep.spec.js similarity index 98% rename from test/grep.spec.js rename to test/unit/grep.spec.js index dd21cf0090..bb620f9ba2 100644 --- a/test/grep.spec.js +++ b/test/unit/grep.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var Mocha = require('../'); +var Mocha = require('../../lib/mocha'); describe('Mocha', function () { describe('"grep" option', function () { diff --git a/test/hook-async.spec.js b/test/unit/hook-async.spec.js similarity index 100% rename from test/hook-async.spec.js rename to test/unit/hook-async.spec.js diff --git a/test/hook-sync-nested.spec.js b/test/unit/hook-sync-nested.spec.js similarity index 100% rename from test/hook-sync-nested.spec.js rename to test/unit/hook-sync-nested.spec.js diff --git a/test/hook-sync.spec.js b/test/unit/hook-sync.spec.js similarity index 100% rename from test/hook-sync.spec.js rename to test/unit/hook-sync.spec.js diff --git a/test/hook-timeout.spec.js b/test/unit/hook-timeout.spec.js similarity index 100% rename from test/hook-timeout.spec.js rename to test/unit/hook-timeout.spec.js diff --git a/test/mocha.spec.js b/test/unit/mocha.spec.js similarity index 95% rename from test/mocha.spec.js rename to test/unit/mocha.spec.js index 618742cdbd..af0c44a730 100644 --- a/test/mocha.spec.js +++ b/test/unit/mocha.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var Mocha = require('../'); +var Mocha = require('../../lib/mocha'); var Test = Mocha.Test; describe('Mocha', function () { diff --git a/test/ms.spec.js b/test/unit/ms.spec.js similarity index 98% rename from test/ms.spec.js rename to test/unit/ms.spec.js index 26f5c30209..6ceab5d409 100644 --- a/test/ms.spec.js +++ b/test/unit/ms.spec.js @@ -1,5 +1,5 @@ 'use strict'; -var ms = require('../lib/ms'); +var ms = require('../../lib/ms'); describe('.ms()', function () { // Helpers diff --git a/test/acceptance/overspecified-async.spec.js b/test/unit/overspecified-async.spec.js similarity index 100% rename from test/acceptance/overspecified-async.spec.js rename to test/unit/overspecified-async.spec.js diff --git a/test/acceptance/required-tokens.spec.js b/test/unit/required-tokens.spec.js similarity index 100% rename from test/acceptance/required-tokens.spec.js rename to test/unit/required-tokens.spec.js diff --git a/test/acceptance/root.spec.js b/test/unit/root.spec.js similarity index 100% rename from test/acceptance/root.spec.js rename to test/unit/root.spec.js diff --git a/test/runnable.spec.js b/test/unit/runnable.spec.js similarity index 96% rename from test/runnable.spec.js rename to test/unit/runnable.spec.js index 67cfcca38a..59bffff7be 100644 --- a/test/runnable.spec.js +++ b/test/unit/runnable.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var mocha = require('../'); +var mocha = require('../../lib/mocha'); var utils = mocha.utils; var Runnable = mocha.Runnable; @@ -183,7 +183,9 @@ describe('Runnable(title, fn)', function () { describe('when timeouts are disabled', function () { it('should not error with timeout', function (done) { var test = new Runnable('foo', function (done) { - setTimeout(process.nextTick.bind(undefined, done), 2); + setTimeout(function () { + setTimeout(done); + }, 2); }); test.timeout(1); test.enableTimeouts(false); @@ -195,7 +197,7 @@ describe('Runnable(title, fn)', function () { describe('without error', function () { it('should invoke the callback', function (done) { var test = new Runnable('foo', function (done) { - process.nextTick(done); + setTimeout(done); }); test.run(done); @@ -210,9 +212,9 @@ describe('Runnable(title, fn)', function () { var test = new Runnable('foo', function (done) { process.nextTick(done); - process.nextTick(done); - process.nextTick(done); - process.nextTick(done); + setTimeout(done); + setTimeout(done); + setTimeout(done); }); test.on('error', function (err) { @@ -236,10 +238,10 @@ describe('Runnable(title, fn)', function () { var test = new Runnable('foo', function (done) { done(new Error('fail')); - process.nextTick(done); + setTimeout(done); done(new Error('fail')); - process.nextTick(done); - process.nextTick(done); + setTimeout(done); + setTimeout(done); }); test.on('error', function (err) { @@ -359,7 +361,7 @@ describe('Runnable(title, fn)', function () { describe('when the promise is fulfilled with no value', function () { var fulfilledPromise = { then: function (fulfilled, rejected) { - process.nextTick(fulfilled); + setTimeout(fulfilled); } }; @@ -375,7 +377,7 @@ describe('Runnable(title, fn)', function () { describe('when the promise is fulfilled with a value', function () { var fulfilledPromise = { then: function (fulfilled, rejected) { - process.nextTick(function () { + setTimeout(function () { fulfilled({}); }); } @@ -394,7 +396,7 @@ describe('Runnable(title, fn)', function () { var expectedErr = new Error('fail'); var rejectedPromise = { then: function (fulfilled, rejected) { - process.nextTick(function () { + setTimeout(function () { rejected(expectedErr); }); } @@ -416,7 +418,7 @@ describe('Runnable(title, fn)', function () { var expectedErr = new Error('Promise rejected with no or falsy reason'); var rejectedPromise = { then: function (fulfilled, rejected) { - process.nextTick(function () { + setTimeout(function () { rejected(); }); } diff --git a/test/runner.spec.js b/test/unit/runner.spec.js similarity index 99% rename from test/runner.spec.js rename to test/unit/runner.spec.js index 7c69846ec8..c3dc379dfb 100644 --- a/test/runner.spec.js +++ b/test/unit/runner.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var mocha = require('../'); +var mocha = require('../../lib/mocha'); var Suite = mocha.Suite; var Runner = mocha.Runner; var Test = mocha.Test; diff --git a/test/suite.spec.js b/test/unit/suite.spec.js similarity index 99% rename from test/suite.spec.js rename to test/unit/suite.spec.js index 4cb62dd290..4233f957d6 100644 --- a/test/suite.spec.js +++ b/test/unit/suite.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var mocha = require('../'); +var mocha = require('../../lib/mocha'); var Suite = mocha.Suite; var Test = mocha.Test; diff --git a/test/test.spec.js b/test/unit/test.spec.js similarity index 98% rename from test/test.spec.js rename to test/unit/test.spec.js index 73c09b3b06..455cfe04ae 100644 --- a/test/test.spec.js +++ b/test/unit/test.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var mocha = require('../'); +var mocha = require('../../lib/mocha'); var should = require('should'); var Test = mocha.Test; diff --git a/test/acceptance/throw.spec.js b/test/unit/throw.spec.js similarity index 100% rename from test/acceptance/throw.spec.js rename to test/unit/throw.spec.js diff --git a/test/acceptance/timeout.spec.js b/test/unit/timeout.spec.js similarity index 100% rename from test/acceptance/timeout.spec.js rename to test/unit/timeout.spec.js diff --git a/test/acceptance/utils.spec.js b/test/unit/utils.spec.js similarity index 71% rename from test/acceptance/utils.spec.js rename to test/unit/utils.spec.js index 545bdfd265..85b1950727 100644 --- a/test/acceptance/utils.spec.js +++ b/test/unit/utils.spec.js @@ -6,13 +6,80 @@ var JSON = require('json3'); describe('lib/utils', function () { describe('clean', function () { + it('should remove the wrapping function declaration', function () { + utils.clean('function (one, two, three) {\n//code\n}') + .should + .equal('//code'); + }); + + it('should handle newlines in the function declaration', function () { + utils.clean('function (one, two, three)\n {\n//code\n}') + .should + .equal('//code'); + }); + + it('should remove space character indentation from the function body', + function () { + utils.clean(' //line1\n //line2') + .should + .equal('//line1\n //line2'); + }); + + it('should remove tab character indentation from the function body', + function () { + utils.clean('\t//line1\n\t\t//line2') + .should + .equal('//line1\n\t//line2'); + }); + + it('should handle functions with tabs in their declarations', function () { + utils.clean('function\t(\t)\t{\n//code\n}') + .should + .equal('//code'); + }); + + it('should handle named functions without space after name', function () { + utils.clean('function withName() {\n//code\n}') + .should + .equal('//code'); + }); + + it('should handle named functions with space after name', function () { + utils.clean('function withName () {\n//code\n}') + .should + .equal('//code'); + }); + + it( + 'should handle functions with no space between the end and the closing brace', + function () { + utils.clean('function() {/*code*/}') + .should + .equal('/*code*/'); + }); + + it('should handle functions with parentheses in the same line', + function () { + utils.clean('function() { if (true) { /* code */ } }') + .should + .equal('if (true) { /* code */ }'); + }); + + it('should handle empty functions', function () { + utils.clean('function() {}') + .should + .equal(''); + }); + it('should format a single line test function', function () { var fn = [ 'function () {', ' var a = 1;', '}' ].join('\n'); - expect(utils.clean(fn)).to.equal('var a = 1;'); + expect(utils.clean(fn)) + .to + .equal('var a = 1;'); }); it('should format a multi line test indented with spaces', function () { @@ -23,7 +90,9 @@ describe('lib/utils', function () { ' var b = 2;', ' var c = 3; }' ].join('\n'); - expect(utils.clean(fn)).to.equal('var a = 1;\n var b = 2;\nvar c = 3;'); + expect(utils.clean(fn)) + .to + .equal('var a = 1;\n var b = 2;\nvar c = 3;'); }); it('should format a multi line test indented with tabs', function () { @@ -34,7 +103,9 @@ describe('lib/utils', function () { '\t}', '}' ].join('\n'); - expect(utils.clean(fn)).to.equal('if (true) {\n\tvar a = 1;\n}'); + expect(utils.clean(fn)) + .to + .equal('if (true) {\n\tvar a = 1;\n}'); }); it('should format functions saved in windows style - spaces', function () { @@ -45,7 +116,9 @@ describe('lib/utils', function () { ' } while (false);', ' }' ].join('\r\n'); - expect(utils.clean(fn)).to.equal('do {\n "nothing";\n} while (false);'); + expect(utils.clean(fn)) + .to + .equal('do {\n "nothing";\n} while (false);'); }); it('should format functions saved in windows style - tabs', function () { @@ -58,7 +131,9 @@ describe('lib/utils', function () { '\t}', '}' ].join('\r\n'); - expect(utils.clean(fn)).to.equal('if (false) {\n\tvar json = {\n\t\tone : 1\n\t};\n}'); + expect(utils.clean(fn)) + .to + .equal('if (false) {\n\tvar json = {\n\t\tone : 1\n\t};\n}'); }); it('should format es6 arrow functions', function () { @@ -67,12 +142,16 @@ describe('lib/utils', function () { ' var a = 1;', '}' ].join('\n'); - expect(utils.clean(fn)).to.equal('var a = 1;'); + expect(utils.clean(fn)) + .to + .equal('var a = 1;'); }); it('should format es6 arrow functions with implicit return', function () { var fn = '() => foo()'; - expect(utils.clean(fn)).to.equal('foo()'); + expect(utils.clean(fn)) + .to + .equal('foo()'); }); }); @@ -415,4 +494,129 @@ describe('lib/utils', function () { Object.prototype.toString = toString; }); }); + + describe('isBuffer()', function () { + var isBuffer = utils.isBuffer; + it('should test if object is a Buffer', function () { + isBuffer(new Buffer([0x01])) + .should + .equal(true); + isBuffer({}) + .should + .equal(false); + }); + }); + + describe('map()', function () { + var map = utils.map; + it('should behave same as Array.prototype.map', function () { + var arr = [ + 1, + 2, + 3 + ]; + map(arr, JSON.stringify) + .should + .eql(arr.map(JSON.stringify)); + }); + + it('should call the callback with 3 arguments[currentValue, index, array]', + function () { + var index = 0; + map([ + 1, + 2, + 3 + ], function (e, i, arr) { + e.should.equal(arr[index]); + i.should.equal(index++); + }); + }); + + it('should apply with the given scope', function () { + var scope = {}; + map([ + 'a', + 'b', + 'c' + ], function () { + this.should.equal(scope); + }, scope); + }); + }); + + describe('some()', function () { + var some = utils.some; + + it( + 'should return true when some array elements pass the check of the fn parameter', + function () { + var result = some([ + 'a', + 'b', + 'c' + ], function (e) { + return e === 'b'; + }); + result.should.eql(true); + }); + + it( + 'should return false when none of the array elements pass the check of the fn parameter', + function () { + var result = some([ + 'a', + 'b', + 'c' + ], function (e) { + return e === 'd'; + }); + result.should.eql(false); + }); + }); + + describe('parseQuery()', function () { + var parseQuery = utils.parseQuery; + it('should get queryString and return key-value object', function () { + parseQuery('?foo=1&bar=2&baz=3') + .should + .eql({ + foo: '1', + bar: '2', + baz: '3' + }); + + parseQuery('?r1=^@(?!.*\\)$)&r2=m{2}&r3=^co.*') + .should + .eql({ + r1: '^@(?!.*\\)$)', + r2: 'm{2}', + r3: '^co.*' + }); + }); + + it('should parse "+" as a space', function () { + parseQuery('?grep=foo+bar') + .should + .eql({grep: 'foo bar'}); + }); + }); + + describe('isPromise', function () { + it('should return true if the value is Promise-ish', function () { + utils.isPromise({ + then: function () { + } + }).should.be.true; + }); + + it('should return false if the value is not an object', function () { + utils.isPromise(1).should.be.false; + }); + + it('should return false if the value is an object w/o a "then" function', + function () { + utils.isPromise({}).should.be.false; + }); + }); }); diff --git a/test/utils.spec.js b/test/utils.spec.js deleted file mode 100644 index 2eaf908cd6..0000000000 --- a/test/utils.spec.js +++ /dev/null @@ -1,285 +0,0 @@ -'use strict'; - -var mocha = require('..'); -var utils = mocha.utils; -var path = require('path'); -var JSON = require('json3'); - -describe('utils', function () { - describe('.clean()', function () { - var clean = utils.clean; - it('should remove the wrapping function declaration', function () { - clean('function (one, two, three) {\n//code\n}').should.equal('//code'); - }); - - it('should handle newlines in the function declaration', function () { - clean('function (one, two, three)\n {\n//code\n}').should.equal('//code'); - }); - - it('should remove space character indentation from the function body', function () { - clean(' //line1\n //line2').should.equal('//line1\n //line2'); - }); - - it('should remove tab character indentation from the function body', function () { - clean('\t//line1\n\t\t//line2').should.equal('//line1\n\t//line2'); - }); - - it('should handle functions with tabs in their declarations', function () { - clean('function\t(\t)\t{\n//code\n}').should.equal('//code'); - }); - - it('should handle named functions without space after name', function () { - clean('function withName() {\n//code\n}').should.equal('//code'); - }); - - it('should handle named functions with space after name', function () { - clean('function withName () {\n//code\n}').should.equal('//code'); - }); - - it('should handle functions with no space between the end and the closing brace', function () { - clean('function() {/*code*/}').should.equal('/*code*/'); - }); - - it('should handle functions with parentheses in the same line', function () { - clean('function() { if (true) { /* code */ } }').should.equal('if (true) { /* code */ }'); - }); - - it('should handle empty functions', function () { - clean('function() {}').should.equal(''); - }); - }); - - describe('.isBuffer()', function () { - var isBuffer = utils.isBuffer; - it('should test if object is a Buffer', function () { - isBuffer(new Buffer([0x01])).should.equal(true); - isBuffer({}).should.equal(false); - }); - }); - - describe('.map()', function () { - var map = utils.map; - it('should behave same as Array.prototype.map', function () { - var arr = [1, 2, 3]; - map(arr, JSON.stringify).should.eql(arr.map(JSON.stringify)); - }); - - it('should call the callback with 3 arguments[currentValue, index, array]', function () { - var index = 0; - map([1, 2, 3], function (e, i, arr) { - e.should.equal(arr[index]); - i.should.equal(index++); - }); - }); - - it('should apply with the given scope', function () { - var scope = {}; - map(['a', 'b', 'c'], function () { - this.should.equal(scope); - }, scope); - }); - }); - - describe('.some()', function () { - var some = utils.some; - - it('should return true when some array elements pass the check of the fn parameter', function () { - var result = some(['a', 'b', 'c'], function (e) { - return e === 'b'; - }); - result.should.eql(true); - }); - - it('should return false when none of the array elements pass the check of the fn parameter', function () { - var result = some(['a', 'b', 'c'], function (e) { - return e === 'd'; - }); - result.should.eql(false); - }); - }); - - describe('.parseQuery()', function () { - var parseQuery = utils.parseQuery; - it('should get queryString and return key-value object', function () { - parseQuery('?foo=1&bar=2&baz=3').should.eql({ - foo: '1', - bar: '2', - baz: '3' - }); - - parseQuery('?r1=^@(?!.*\\)$)&r2=m{2}&r3=^co.*').should.eql({ - r1: '^@(?!.*\\)$)', - r2: 'm{2}', - r3: '^co.*' - }); - }); - - it('should parse "+" as a space', function () { - parseQuery('?grep=foo+bar').should.eql({grep: 'foo bar'}); - }); - }); - - describe('.stackTraceFilter()', function () { - describe('on node', function () { - var filter = utils.stackTraceFilter(); - - describe('on POSIX OS', function () { - before(function () { - if (path.sep !== '/') { - this.skip(); - } - }); - - it('should get a stack-trace as a string and prettify it', function () { - var stack = [ - 'AssertionError: foo bar', - 'at EventEmitter. (/usr/local/dev/test.js:16:12)', - 'at Context. (/usr/local/dev/test.js:19:5)', - 'Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:244:7)', - 'Runner.runTest (/usr/local/lib/node_modules/mocha/lib/runner.js:374:10)', - '/usr/local/lib/node_modules/mocha/lib/runner.js:452:12', - 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:299:14)', - '/usr/local/lib/node_modules/mocha/lib/runner.js:309:7', - 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)', - 'Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)', - 'at processImmediate [as _immediateCallback] (timers.js:321:17)' - ]; - filter(stack.join('\n')) - .should - .equal(stack.slice(0, 3) - .join('\n')); - - stack = [ - 'AssertionError: bar baz', - 'at /usr/local/dev/some-test-file.js:25:8', - 'at tryCatcher (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/util.js:24:31)', - 'at Promise._resolveFromResolver (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:439:31)', - 'at new Promise (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:53:37)', - 'at yourFunction (/usr/local/dev/own/tmp/test1.js:24:13)', - 'at Context. (/usr/local/dev/some-test-file:30:4)', - 'Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:218:15)', - 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)', - 'Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)', - 'at processImmediate [as _immediateCallback] (timers.js:321:17)' - ]; - - filter(stack.join('\n')) - .should - .equal(stack.slice(0, 7) - .join('\n')); - }); - - it('does not ignore other bower_components and components', - function () { - var stack = [ - 'Error: failed', - 'at assert (index.html:11:26)', - 'at Context. (test.js:17:18)', - 'at bower_components/should/should.js:4827:7', - 'at next (file:///.../bower_components/should/should.js:4766:23)', - 'at components/should/5.0.0/should.js:4827:7', - 'at next (file:///.../components/should/5.0.0/should.js:4766:23)', - 'at file:///.../bower_components/mocha/mocha.js:4794:5', - 'at timeslice (.../components/mocha/mocha.js:6218:27)', - 'at Test.require.register.Runnable.run (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4463:15)', - 'at Runner.require.register.Runner.runTest (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4892:10)', - 'at file:///.../components/mochajs/mocha/2.1.0/mocha.js:4970:12', - 'at next (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4817:14)' - ]; - filter(stack.join('\n')) - .should - .equal(stack.slice(0, 7) - .join('\n')); - }); - - it('should replace absolute with relative paths', function () { - var stack = [ - 'Error: ' + process.cwd() + '/bla.js has a problem', - 'at foo (' + process.cwd() + '/foo/index.js:13:226)', - 'at bar (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:11:26)' - ]; - - var expected = [ - 'Error: ' + process.cwd() + '/bla.js has a problem', - 'at foo (foo/index.js:13:226)', - 'at bar (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:11:26)' - ]; - - filter(stack.join('\n')) - .should - .equal(expected.join('\n')); - }); - }); - - describe('on Windows', function () { - before(function () { - if (path.sep === '/') { - this.skip(); - } - }); - - it('should work on Windows', function () { - var stack = [ - 'Error: failed', - 'at Context. (C:\\Users\\ishida\\src\\test\\test\\mytest.js:5:9)', - 'at callFn (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runnable.js:326:21)', - 'at Test.Runnable.run (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runnable.js:319:7)', - 'at Runner.runTest (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:422:10)', - 'at C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:528:12', - 'at next (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:342:14)', - 'at C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:352:7', - 'at next (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:284:14)', - 'at Immediate._onImmediate (C:\\Users\\ishida\\src\\test\\node_modules\\mocha\\lib\\runner.js:320:5)' - ]; - filter(stack.join('\n')) - .should - .equal(stack.slice(0, 2) - .join('\n')); - }); - }); - }); - - describe('on browser', function () { - var filter; - before(function () { - global.document = true; - global.location = { href: 'localhost:3000/foo/bar/index.html' }; - filter = utils.stackTraceFilter(); - }); - it('does not strip out other bower_components', function () { - var stack = [ - 'Error: failed', - 'at assert (index.html:11:26)', - 'at Context. (test.js:17:18)', - 'at bower_components/should/should.js:4827:7', - 'at next (bower_components/should/should.js:4766:23)', - 'at components/should/5.0.0/should.js:4827:7', - 'at next (components/should/5.0.0/should.js:4766:23)', - 'at Runner.require.register.Runner.runTest (node_modules/mocha.js:4892:10)', - 'at localhost:3000/foo/bar/node_modules/mocha.js:4970:12', - 'at next (node_modules/mocha.js:4817:14)' - ]; - filter(stack.join('\n')).should.equal(stack.slice(0, 7).join('\n')); - }); - - after(function () { - delete global.document; - delete global.location; - }); - }); - }); - - describe('.isPromise', function () { - it('should return true if the value is Promise-ish', function () { - utils.isPromise({then: function () {}}).should.be.true; - }); - - it('should return false if the value is not an object', function () { - utils.isPromise(1).should.be.false; - }); - - it('should return false if the value is an object w/o a "then" function', function () { - utils.isPromise({}).should.be.false; - }); - }); -});