diff --git a/Makefile b/Makefile index 6afdbac32c..cde16486a1 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ lib-cov: test: test-unit -test-all: test-bdd test-tdd test-qunit test-exports test-unit test-grep test-jsapi test-compilers test-sort test-glob test-requires test-reporters test-only test-failing test-regression +test-all: 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-jsapi: @node test/jsapi @@ -48,25 +48,10 @@ test-unit: --growl \ test/*.js -test-regression: test-outputs/issue1327/case-out.json +test-integration: @./bin/mocha \ --reporter $(REPORTER) \ - test/regression/issue*/control.js - -test-outputs/issue1327/case-out.json: test/regression/issue1327/case.js - @mkdir -p $(dir $@) || true - @./bin/mocha --reporter json $< > $@ || true - -test-failing: - ./bin/mocha \ - --reporter $(REPORTER) \ - test/acceptance/failing > /dev/null 2>&1 ; \ - failures="$$?" ; \ - if [ "$$failures" != '4' ] ; then \ - echo 'test-failing:' ; \ - echo " expected 4 failing tests but saw $$failures" ; \ - exit 1 ; \ - fi + test/integration/*.js test-compilers: @./bin/mocha \ @@ -109,31 +94,6 @@ test-exports: --ui exports \ test/acceptance/interfaces/exports -test-grep: - @./bin/mocha \ - --reporter $(REPORTER) \ - --grep fast \ - test/acceptance/misc/grep - -test-invert: - @./bin/mocha \ - --reporter $(REPORTER) \ - --grep slow \ - --invert \ - test/acceptance/misc/grep - -test-bail: - @./bin/mocha \ - --reporter $(REPORTER) \ - --bail \ - test/acceptance/misc/bail - -test-async-only: - @./bin/mocha \ - --reporter $(REPORTER) \ - --async-only \ - test/acceptance/misc/asyncOnly - test-glob: @./test/acceptance/glob/glob.sh @@ -142,12 +102,6 @@ test-reporters: --reporter $(REPORTER) \ test/reporters/*.js -test-delay: - @./bin/mocha \ - --reporter $(REPORTER) \ - --delay \ - test/delay/*.js - test-only: @./bin/mocha \ --reporter $(REPORTER) \ @@ -164,12 +118,6 @@ test-only: --ui qunit \ test/acceptance/misc/only/qunit -test-sort: - @./bin/mocha \ - --reporter $(REPORTER) \ - --sort \ - test/acceptance/sort - test-mocha: @./bin/mocha \ --reporter $(REPORTER) \ @@ -200,4 +148,4 @@ non-tty: tm: @open editors/$(TM_BUNDLE) -.PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit non-tty test-grep test-failing tm clean +.PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit test-integration non-tty tm clean diff --git a/test.js b/test.js new file mode 100644 index 0000000000..47de23e5e6 --- /dev/null +++ b/test.js @@ -0,0 +1,9 @@ +function usedToBeAsync (cb) { + cb() +} + +it('test', function() { + this.timeout(4294967296); + usedToBeAsync(done) +}); + diff --git a/test/acceptance/diffs.js b/test/acceptance/diffs.js deleted file mode 100644 index c87ffe0cce..0000000000 --- a/test/acceptance/diffs.js +++ /dev/null @@ -1,93 +0,0 @@ -var fs = require('fs') - , cssin = fs.readFileSync('test/acceptance/fixtures/css.in', 'ascii') - , cssout = fs.readFileSync('test/acceptance/fixtures/css.out', 'ascii'); - -describe('diffs', function(){ - // uncomment the assertions, and run with different params to check the output - // ex: --color, --no-color, --unified-diff - - it('should display a diff for small strings', function(){ - var expected = 'foo bar baz' - , actual = 'foo rar baz'; - - // expected.should.eql(actual); - }); - - it('should display a diff of canonicalized objects', function(){ - var actual = { name: 'travis j', age: 23 } - , expected = { age: 23, name: 'travis' }; - - // actual.should.eql(expected); - }); - - it('should display a diff for medium strings', function(){ - var expected = 'foo bar baz\nfoo bar baz\nfoo bar baz' - , actual = 'foo bar baz\nfoo rar baz\nfoo bar raz'; - - // expected.should.eql(actual); - }); - - it('should display a diff for entire object dumps', function(){ - var expected = { name: 'joe', age: 30, address: {city: 'new york', country: 'us' }} - , actual = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }}; - - // actual.should.eql(expected); - }); - - it('should display a diff for multi-line strings', function(){ - var expected = 'one two three\nfour five six\nseven eight nine'; - var actual = 'one two three\nfour zzzz six\nseven eight nine'; - - // actual.should.eql(expected); - }); - - it('should display a diff for entire object dumps', function(){ - var expected = { name: 'joe', age: 30, address: {city: 'new york', country: 'us' }} - var actual = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }}; - - // actual.should.eql(expected); - }); - - it('should display a full-comparison with escaped special characters', function(){ - var expected = 'one\ttab\ntwo\t\ttabs'; - var actual = 'one\ttab\ntwo\t\t\ttabs'; - - //actual.should.equal(expected); - }); - - it('should display a word diff for large strings', function(){ - // cssin.should.equal(cssout); - }); - - it('should work with objects', function(){ - var tobi = { - name: 'tobi', - species: 'ferret', - color: 'white', - age: 2 - }; - - var loki = { - name: 'loki', - species: 'ferret', - color: 'brown', - age: 2 - }; - - // tobi.should.eql(loki); - }); - - it('should show value diffs and not be affected by commas', function(){ - var obj1 = { a: 123 }; - var obj2 = { a: 123, b: 456 }; - - // obj1.should.equal(obj2); - }); - - it('should display diff by data and not like an objects', function(){ - var buf1 = new Buffer([0x01]); - var buf2 = new Buffer([0x02]); - -// buf1.should.equal(buf2); - }); -}); diff --git a/test/acceptance/failing/timeout.js b/test/acceptance/failing/timeout.js deleted file mode 100644 index e52dde4c24..0000000000 --- a/test/acceptance/failing/timeout.js +++ /dev/null @@ -1,17 +0,0 @@ -describe('timeout', function(){ - this.timeout(1); - - it('should be honored with sync suites', function(){ - sleep(2); - }); - - it('should be honored with async suites', function(done){ - sleep(2); - done(); - }); - - function sleep(ms){ - var start = Date.now(); - while(start + ms > Date.now())continue; - } -}); diff --git a/test/acceptance/misc/asyncOnly.js b/test/acceptance/misc/asyncOnly.js deleted file mode 100644 index 7b7086a7ea..0000000000 --- a/test/acceptance/misc/asyncOnly.js +++ /dev/null @@ -1,9 +0,0 @@ -describe('asyncOnly', function(){ - it('should display an error', function(){ - - }) - - it('should pass', function(done){ - done(); - }) -}) diff --git a/test/acceptance/misc/bail.js b/test/acceptance/misc/bail.js deleted file mode 100644 index a2c0c1396f..0000000000 --- a/test/acceptance/misc/bail.js +++ /dev/null @@ -1,20 +0,0 @@ -describe('bail', function(){ - it('should only display this error', function(done){ - throw new Error('this should be displayed'); - }) - - it('should not display this error', function(done){ - throw new Error('this should not be displayed'); - }) -}) - -describe('bail-2', function(){ - - before(function(done){ - throw new Error('this hook should not be displayed'); - }) - - it('should not display this error', function(done){ - throw new Error('this should not be displayed'); - }) -}) diff --git a/test/acceptance/misc/cascade.js b/test/acceptance/misc/cascade.js deleted file mode 100644 index b2dda4df11..0000000000 --- a/test/acceptance/misc/cascade.js +++ /dev/null @@ -1,57 +0,0 @@ -describe('one', function(){ - before(function(){ - console.log('before one'); - }) - - after(function(){ - console.log('after one'); - }) - - beforeEach(function(){ - console.log(' before each one'); - }) - - afterEach(function(){ - console.log(' after each one'); - }) - - describe('two', function(){ - before(function(){ - console.log(' before two'); - }) - - after(function(){ - console.log(' after two'); - }) - - beforeEach(function(){ - console.log(' before each two'); - }) - - afterEach(function(){ - console.log(' after each two'); - }) - - describe('three', function(){ - before(function(){ - console.log(' before three'); - }) - - after(function(){ - console.log(' after three'); - }) - - beforeEach(function(){ - console.log(' before each three'); - }) - - afterEach(function(){ - console.log(' after each three'); - }) - - it('should three', function(){ - console.log(' TEST three'); - }) - }) - }) -}) diff --git a/test/acceptance/misc/grep.js b/test/acceptance/misc/grep.js deleted file mode 100644 index ec9f78f3cd..0000000000 --- a/test/acceptance/misc/grep.js +++ /dev/null @@ -1,21 +0,0 @@ -describe('grep', function(){ - describe('fast', function(){ - it('should run fast', function(){ - - }) - - it('should run fast again', function(){ - - }) - }) - - describe('slow', function(){ - it('should run slow', function(done){ - setTimeout(done, 1000); - }) - - it('should run slow again', function(done){ - setTimeout(done, 1000); - }) - }) -}) diff --git a/test/acceptance/multiple.done.js b/test/acceptance/multiple.done.js deleted file mode 100644 index 79874995f5..0000000000 --- a/test/acceptance/multiple.done.js +++ /dev/null @@ -1,23 +0,0 @@ -describe('multiple calls to done()', function(){ - beforeEach(function(done){ - done() - // uncomment - // done() - }) - - it('should fail in a test-case', function(done){ - process.nextTick(function(){ - done(); - // uncomment - // done(); - }); - }) - - it('should produce a reasonable trace', function (done) { - process.nextTick(function() { - done(); - // uncomment - // done() - }) - }); -}) diff --git a/test/acceptance/pending.js b/test/acceptance/pending.js deleted file mode 100644 index b40b3c03ed..0000000000 --- a/test/acceptance/pending.js +++ /dev/null @@ -1,41 +0,0 @@ -describe('pending', function(){ - it('should be allowed') -}) - -describe('skip in test', function(){ - it('should skip immediately', function(){ - this.skip(); - throw new Error('never thrown'); - }) - - it('should run other tests in the suite', function(){ - }) -}) - -describe('skip in before', function(){ - before(function(){ - this.skip(); - }) - - it('should never run this test', function(){ - throw new Error('never thrown'); - }) - - it('should never run this test', function(){ - throw new Error('never thrown'); - }) -}) - -describe('skip in beforeEach', function(){ - beforeEach(function(){ - this.skip(); - }) - - it('should never run this test', function(){ - throw new Error('never thrown'); - }) - - it('should never run this test', function(){ - throw new Error('never thrown'); - }) -}) diff --git a/test/acceptance/uncaught.js b/test/acceptance/uncaught.js deleted file mode 100644 index f228266e45..0000000000 --- a/test/acceptance/uncaught.js +++ /dev/null @@ -1,16 +0,0 @@ -describe('uncaught', function(){ - beforeEach(function(done){ - process.nextTick(function(){ - // throw new Error('oh noes'); - done(); - }); - }) - - it('should report properly', function(done){ - process.nextTick(function(){ - // if you uncomment this :) - // throw new Error("I'm uncaught!"); - done(); - }) - }) -}) diff --git a/test/delay/delay.js b/test/delay/delay.js deleted file mode 100644 index ce74db51f8..0000000000 --- a/test/delay/delay.js +++ /dev/null @@ -1,19 +0,0 @@ -var MS = 500, - start = new Date().getTime(); - -setTimeout(function () { - describe('delayed execution', function () { - it('should have waited ' + MS + 'ms to run this suite', function () { - (new Date().getTime() - MS >= start).should.be.true; - }); - - it('should have no effect if attempted twice in the same suite', - function () { - true.should.be.true; - run(); - true.should.be.true; - }); - }); - run(); - -}, MS); diff --git a/test/integration/diffs.js b/test/integration/diffs.js new file mode 100644 index 0000000000..d8aebba964 --- /dev/null +++ b/test/integration/diffs.js @@ -0,0 +1,45 @@ +var assert = require('assert'); +var helpers = require('./helpers'); +var run = helpers.runMocha; +var fs = require('fs'); +var getDiffs = helpers.getDiffs; + +function getExpectedOutput() { + var output = fs.readFileSync('test/integration/fixtures/diffs/output', 'UTF8'); + + // Diffs are delimited in file by "// DIFF" + return output.split(/\s*\/\/ DIFF/).slice(1).map(function(diff) { + return diff.split('\n').filter(Boolean).join('\n'); + }); +} + +describe('diffs', function() { + var diffs, expected; + this.timeout(1000); + + before(function(done) { + run('diffs/diffs.js', ['-C'], function(err, res) { + expected = getExpectedOutput(); + diffs = getDiffs(res.output); + done(err); + }); + }); + + [ + 'should display a diff for small strings', + 'should display a diff of canonicalized objects', + 'should display a diff for medium strings', + 'should display a diff for entire object dumps', + 'should display a diff for multi-line strings', + 'should display a diff for entire object dumps', + 'should display a full-comparison with escaped special characters', + 'should display a word diff for large strings', + 'should work with objects', + 'should show value diffs and not be affected by commas', + 'should display diff by data and not like an objects' + ].forEach(function(title, i) { + it(title, function() { + assert.equal(diffs[i], expected[i]); + }); + }); +}); diff --git a/test/integration/fixtures/cascade.js b/test/integration/fixtures/cascade.js new file mode 100644 index 0000000000..10b1c37ffd --- /dev/null +++ b/test/integration/fixtures/cascade.js @@ -0,0 +1,57 @@ +describe('one', function() { + before(function() { + console.log('before one'); + }); + + after(function() { + console.log('after one'); + }); + + beforeEach(function() { + console.log(' before each one'); + }); + + afterEach(function() { + console.log(' after each one'); + }); + + describe('two', function() { + before(function() { + console.log(' before two'); + }); + + after(function() { + console.log(' after two'); + }); + + beforeEach(function() { + console.log(' before each two'); + }); + + afterEach(function() { + console.log(' after each two'); + }); + + describe('three', function() { + before(function() { + console.log(' before three'); + }); + + after(function() { + console.log(' after three'); + }); + + beforeEach(function() { + console.log(' before each three'); + }); + + afterEach(function() { + console.log(' after each three'); + }); + + it('should three', function() { + console.log(' TEST three'); + }); + }); + }); +}); diff --git a/test/acceptance/fixtures/css.in b/test/integration/fixtures/diffs/diffs.css.in similarity index 100% rename from test/acceptance/fixtures/css.in rename to test/integration/fixtures/diffs/diffs.css.in diff --git a/test/acceptance/fixtures/css.out b/test/integration/fixtures/diffs/diffs.css.out similarity index 100% rename from test/acceptance/fixtures/css.out rename to test/integration/fixtures/diffs/diffs.css.out diff --git a/test/integration/fixtures/diffs/diffs.js b/test/integration/fixtures/diffs/diffs.js new file mode 100644 index 0000000000..cf538fd777 --- /dev/null +++ b/test/integration/fixtures/diffs/diffs.js @@ -0,0 +1,84 @@ +var fs = require('fs'); +var assert = require('assert'); +var cssin = fs.readFileSync('test/integration/fixtures/diffs/diffs.css.in', 'ascii'); +var cssout = fs.readFileSync('test/integration/fixtures/diffs/diffs.css.out', 'ascii'); + +describe('diffs', function() { + var actual, expected; + + it('should display a diff for small strings', function() { + actual = 'foo rar baz'; + expected = 'foo bar baz'; + actual.should.equal(expected); + }); + + it('should display a diff of canonicalized objects', function() { + actual = { name: 'travis j', age: 23 }; + expected = { age: 23, name: 'travis' }; + actual.should.equal(expected); + }); + + it('should display a diff for medium strings', function() { + actual = 'foo bar baz\nfoo rar baz\nfoo bar raz'; + expected = 'foo bar baz\nfoo bar baz\nfoo bar baz'; + actual.should.equal(expected); + }); + + it('should display a diff for entire object dumps', function() { + actual = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }}; + expected = { name: 'joe', age: 30, address: {city: 'new york', country: 'us' }}; + actual.should.equal(expected); + }); + + it('should display a diff for multi-line strings', function() { + actual = 'one two three\nfour zzzz six\nseven eight nine'; + expected = 'one two three\nfour five six\nseven eight nine'; + actual.should.equal(expected); + }); + + it('should display a diff for entire object dumps', function() { + actual = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }}; + expected = { name: 'joe', age: 30, address: {city: 'new york', country: 'us' }} + actual.should.equal(expected); + }); + + it('should display a full-comparison with escaped special characters', function() { + actual = 'one\ttab\ntwo\t\t\ttabs'; + expected = 'one\ttab\ntwo\t\ttabs'; + actual.should.equal(expected); + }); + + it('should display a word diff for large strings', function() { + cssin.should.equal(cssout); + }); + + it('should work with objects', function() { + actual = { + name: 'tobi', + species: 'ferret', + color: 'white', + age: 2 + }; + + expected = { + name: 'loki', + species: 'ferret', + color: 'brown', + age: 2 + }; + + actual.should.eql(expected); + }); + + it('should show value diffs and not be affected by commas', function() { + actual = { a: 123 }; + expected = { a: 123, b: 456 }; + actual.should.equal(expected); + }); + + it('should display diff by data and not like an objects', function() { + actual = new Buffer([0x01]); + expected = new Buffer([0x02]); + actual.should.equal(expected); + }); +}); diff --git a/test/integration/fixtures/diffs/output b/test/integration/fixtures/diffs/output new file mode 100644 index 0000000000..480daca213 --- /dev/null +++ b/test/integration/fixtures/diffs/output @@ -0,0 +1,91 @@ +// DIFF + +foo bar baz + -foo rar baz + +// DIFF + { + "age": 23 + + "name": "travis" + - "name": "travis j" + } + +// DIFF + foo bar baz + +foo bar baz + +foo bar baz + -foo rar baz + -foo bar raz + +// DIFF + { + "address": { + "city": "new york" + + "country": "us" + - "country": "usa" + } + "age": 30 + + "name": "joe" + - "name": "joel" + } + +// DIFF + one two three + +four five six + -four zzzz six + seven eight nine + +// DIFF + { + "address": { + "city": "new york" + + "country": "us" + - "country": "usa" + } + "age": 30 + + "name": "joe" + - "name": "joel" + } + +// DIFF + one tab + +two tabs + -two tabs + +// DIFF + body { + font: "Helvetica Neue", Helvetica, arial, sans-serif; + background: black; + + color: #fff; + - color: white; + } + + a { + + color: blue; + - color: blue + } + + + +foo { + + bar: 'baz'; + +} + +// DIFF + { + "age": 2 + + "color": "brown" + + "name": "loki" + - "color": "white" + - "name": "tobi" + "species": "ferret" + } + +// DIFF + { + "a": 123 + + "b": 456 + } + +// DIFF + [ + + 2 + - 1 + ] diff --git a/test/integration/fixtures/multiple.done.js b/test/integration/fixtures/multiple.done.js new file mode 100644 index 0000000000..63a704061c --- /dev/null +++ b/test/integration/fixtures/multiple.done.js @@ -0,0 +1,18 @@ +// The suite below should result in an additional error, but does +// not. Uncomment once this bug is resolved. + +// describe('suite', function() { +// beforeEach(function(done) { +// done(); +// done(); +// }); + +// it('test', function() {}); +// }); + +it('should fail in a test-case', function(done) { + process.nextTick(function(){ + done(); + done(); + }); +}); diff --git a/test/integration/fixtures/options/async-only.async.js b/test/integration/fixtures/options/async-only.async.js new file mode 100644 index 0000000000..5387d169b7 --- /dev/null +++ b/test/integration/fixtures/options/async-only.async.js @@ -0,0 +1,3 @@ +it('should pass', function(done){ + done(); +}); diff --git a/test/integration/fixtures/options/async-only.sync.js b/test/integration/fixtures/options/async-only.sync.js new file mode 100644 index 0000000000..d0dd9fa828 --- /dev/null +++ b/test/integration/fixtures/options/async-only.sync.js @@ -0,0 +1 @@ +it('throws an error', function() {}); diff --git a/test/integration/fixtures/options/bail.js b/test/integration/fixtures/options/bail.js new file mode 100644 index 0000000000..bb8ad13da4 --- /dev/null +++ b/test/integration/fixtures/options/bail.js @@ -0,0 +1,25 @@ +describe('suite1', function() { + it('should display this spec', function() {}); + + it('should only display this error', function(done) { + throw new Error('this should be displayed'); + }); + + it('should not display this error', function(done) { + throw new Error('this should not be displayed'); + }); +}); + +describe('suite2', function() { + // TODO: When uncommented, the hook below is ran and throws an exception + // despite the previous failure with the bail flag. This is a bug. Uncomment + // once resolved + + // before(function(done) { + // throw new Error('this hook should not be displayed'); + // }); + + it('should not display this error', function(done) { + throw new Error('this should not be displayed'); + }); +}); diff --git a/test/integration/fixtures/options/delay.js b/test/integration/fixtures/options/delay.js new file mode 100644 index 0000000000..32b8f99383 --- /dev/null +++ b/test/integration/fixtures/options/delay.js @@ -0,0 +1,19 @@ +var assert = require('assert'); +var delay = 500; +var start = new Date().getTime(); + +setTimeout(function() { + describe('delayed execution', function() { + it('should have waited ' + delay + 'ms to run this suite', function() { + assert(new Date().getTime() - delay >= start); + }); + + it('should have no effect if attempted twice in the same suite', function() { + assert(true); + run(); + assert(true); + }); + }); + + run(); +}, delay); diff --git a/test/integration/fixtures/options/grep.js b/test/integration/fixtures/options/grep.js new file mode 100644 index 0000000000..5e163f5ee7 --- /dev/null +++ b/test/integration/fixtures/options/grep.js @@ -0,0 +1,12 @@ +describe('grep', function() { + describe('match', function() { + it('should run', function(){}); + it('should also run', function() {}); + }); + + describe('fail', function(){ + it('should not be ran', function() { + throw new Error('Spec should not run'); + }); + }); +}); diff --git a/test/acceptance/sort/alpha.js b/test/integration/fixtures/options/sort.alpha.js similarity index 100% rename from test/acceptance/sort/alpha.js rename to test/integration/fixtures/options/sort.alpha.js diff --git a/test/acceptance/sort/beta.js b/test/integration/fixtures/options/sort.beta.js similarity index 100% rename from test/acceptance/sort/beta.js rename to test/integration/fixtures/options/sort.beta.js diff --git a/test/integration/fixtures/pending/skip.sync.before.js b/test/integration/fixtures/pending/skip.sync.before.js new file mode 100644 index 0000000000..35152e27f4 --- /dev/null +++ b/test/integration/fixtures/pending/skip.sync.before.js @@ -0,0 +1,13 @@ +describe('skip in before', function() { + before(function() { + this.skip(); + }); + + it('should never run this test', function() { + throw new Error('never thrown'); + }); + + it('should never run this test', function() { + throw new Error('never thrown'); + }); +}); diff --git a/test/integration/fixtures/pending/skip.sync.beforeEach.js b/test/integration/fixtures/pending/skip.sync.beforeEach.js new file mode 100644 index 0000000000..8d1c442ce2 --- /dev/null +++ b/test/integration/fixtures/pending/skip.sync.beforeEach.js @@ -0,0 +1,13 @@ +describe('skip in beforeEach', function() { + beforeEach(function() { + this.skip(); + }); + + it('should never run this test', function() { + throw new Error('never thrown'); + }); + + it('should never run this test', function() { + throw new Error('never thrown'); + }); +}); diff --git a/test/integration/fixtures/pending/skip.sync.spec.js b/test/integration/fixtures/pending/skip.sync.spec.js new file mode 100644 index 0000000000..e2bbb739b3 --- /dev/null +++ b/test/integration/fixtures/pending/skip.sync.spec.js @@ -0,0 +1,10 @@ +describe('skip in test', function() { + it('should skip immediately', function() { + this.skip(); + throw new Error('never thrown'); + }); + + it('should run other tests in the suite', function() { + // Do nothing + }); +}); diff --git a/test/integration/fixtures/pending/spec.js b/test/integration/fixtures/pending/spec.js new file mode 100644 index 0000000000..084dd33588 --- /dev/null +++ b/test/integration/fixtures/pending/spec.js @@ -0,0 +1,3 @@ +describe('suite', function() { + it('pending spec'); +}); diff --git a/test/integration/fixtures/regression/issue-1327.js b/test/integration/fixtures/regression/issue-1327.js new file mode 100644 index 0000000000..5739edb465 --- /dev/null +++ b/test/integration/fixtures/regression/issue-1327.js @@ -0,0 +1,11 @@ +it('test 1', function() { + process.nextTick(function() { + throw 'Too bad'; + }); +}); + +it('test 2', function() {}); + +it('test 3', function() { + throw new Error('OUCH'); +}); diff --git a/test/integration/fixtures/timeout.js b/test/integration/fixtures/timeout.js new file mode 100644 index 0000000000..d8c99b2bc3 --- /dev/null +++ b/test/integration/fixtures/timeout.js @@ -0,0 +1,17 @@ +describe('timeout', function(){ + this.timeout(1); + + it('should be honored with sync suites', function() { + sleep(2); + }); + + it('should be honored with async suites', function(done) { + sleep(2); + done(); + }); + + function sleep(ms) { + var start = Date.now(); + while (start + ms > Date.now()); + } +}); diff --git a/test/integration/fixtures/uncaught.hook.js b/test/integration/fixtures/uncaught.hook.js new file mode 100644 index 0000000000..9adcb3f726 --- /dev/null +++ b/test/integration/fixtures/uncaught.hook.js @@ -0,0 +1,15 @@ +describe('uncaught', function() { + beforeEach(function(done) { + process.nextTick(function() { + throw new Error('oh noes'); + done(); + }); + }); + + it('test', function(done) { + process.nextTick(function() { + throw new Error("I'm uncaught!"); + done(); + }); + }); +}); diff --git a/test/acceptance/failing/uncaught-and-async.js b/test/integration/fixtures/uncaught.js similarity index 100% rename from test/acceptance/failing/uncaught-and-async.js rename to test/integration/fixtures/uncaught.js diff --git a/test/integration/helpers.js b/test/integration/helpers.js new file mode 100644 index 0000000000..eea8b185fb --- /dev/null +++ b/test/integration/helpers.js @@ -0,0 +1,142 @@ +var spawn = require('child_process').spawn; +var path = require('path'); + +module.exports = { + /** + * Invokes the mocha binary for the given fixture with color output disabled. + * Accepts an array of additional command line args to pass. The callback is + * invoked with a summary of the run, in addition to its output. The summary + * includes the number of passing, pending, and failing tests, as well as the + * exit code. Useful for testing different reporters. + * + * Example response: + * { + * pending: 0, + * passing: 0, + * failing: 1, + * code: 1, + * output: '...' + * } + * + * @param {string} fixturePath + * @param {array} args + * @param {function} fn + */ + runMocha: function(fixturePath, args, fn) { + var path; + + path = resolveFixturePath(fixturePath); + args = args || []; + + invokeMocha(args.concat(['-C', path]), function(err, res) { + if (err) return fn(err); + + fn(null, getSummary(res)); + }); + }, + + /** + * Invokes the mocha binary for the given fixture using the JSON reporter, + * returning the parsed output, as well as exit code. + * + * @param {string} fixturePath + * @param {array} args + * @param {function} fn + */ + runMochaJSON: function(fixturePath, args, fn) { + var path; + + path = resolveFixturePath(fixturePath); + args = args || []; + + invokeMocha(args.concat(['--reporter', 'json', path]), function(err, res) { + if (err) return fn(err); + + try { + var result = JSON.parse(res.output); + result.code = res.code; + } catch (err) { + return fn(err); + } + + fn(null, result); + }); + }, + + /** + * Returns an array of diffs corresponding to exceptions thrown from specs, + * given the plaintext output (-C) of a mocha run. + * + * @param {string} output + * returns {string[]} + */ + getDiffs: function(output) { + var diffs, i, inDiff, inStackTrace; + + diffs = []; + output.split('\n').forEach(function(line) { + if (line.match(/^ \d+\)/)) { + // New spec, e.g. "1) spec title" + diffs.push([]); + i = diffs.length - 1; + inStackTrace = false; + inDiff = false; + } else if (!diffs.length || inStackTrace) { + // Haven't encountered a spec yet + // or we're in the middle of a stack trace + return; + } else if (line.indexOf('+ expected - actual') !== -1) { + inDiff = true; + } else if (line.match(/at Context/)) { + // At the start of a stack trace + inStackTrace = true; + inDiff = false; + } else if (inDiff) { + diffs[i].push(line); + } + }); + + // Ignore empty lines before/after diff + return diffs.map(function(diff) { + return diff.slice(1, -1).join('\n'); + }); + } +}; + +function invokeMocha(args, fn) { + var output, mocha, listener; + + output = ''; + mocha = spawn('./bin/mocha', args); + + listener = function(data) { + output += data; + }; + + mocha.stdout.on('data', listener); + mocha.stderr.on('data', listener); + mocha.on('error', fn); + + mocha.on('close', function(code) { + fn(null, { + output: output.split('\n').join('\n'), + code: code + }); + }); +} + +function resolveFixturePath(fixture) { + return path.join('./test/integration/fixtures', fixture); +} + +function getSummary(res) { + return ['passing', 'pending', 'failing'].reduce(function(summary, type) { + var pattern, match; + + pattern = new RegExp(' (\\d+) ' + type + '\\s'); + match = pattern.exec(res.output); + summary[type] = (match) ? parseInt(match, 10) : 0; + + return summary; + }, res); +} diff --git a/test/integration/hooks.js b/test/integration/hooks.js new file mode 100644 index 0000000000..4236c7f82d --- /dev/null +++ b/test/integration/hooks.js @@ -0,0 +1,44 @@ +var assert = require('assert'); +var run = require('./helpers').runMocha; +var args = []; + +describe('hooks', function() { + this.timeout(1000); + + it('are ran in correct order', function(done) { + run('cascade.js', args, function(err, res) { + var lines, expected; + + assert(!err); + + lines = res.output.split(/[\n․]+/).map(function(line) { + return line.trim(); + }).filter(function(line) { + return line.length; + }).slice(0, -1); + + expected = [ + 'before one', + 'before two', + 'before three', + 'before each one', + 'before each two', + 'before each three', + 'TEST three', + 'after each three', + 'after each two', + 'after each one', + 'after three', + 'after two', + 'after one' + ]; + + expected.forEach(function(line, i) { + assert.equal(lines[i], line); + }); + + assert.equal(res.code, 0); + done(); + }); + }); +}); diff --git a/test/integration/multiple.done.js b/test/integration/multiple.done.js new file mode 100644 index 0000000000..8f9a3cdbce --- /dev/null +++ b/test/integration/multiple.done.js @@ -0,0 +1,28 @@ +var assert = require('assert'); +var run = require('./helpers').runMochaJSON; +var args = []; + +describe('multiple calls to done()', function() { + var res; + + this.timeout(1000); + + before(function(done) { + run('multiple.done.js', args, function(err, result) { + res = result; + done(err); + }); + }); + + it('results in failures', function() { + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 1); + assert.equal(res.stats.failures, 1); + assert.equal(res.code, 1); + }); + + it('throws a descriptive error', function() { + assert.equal(res.failures[0].err.message, + 'done() called multiple times'); + }); +}); diff --git a/test/integration/options.js b/test/integration/options.js new file mode 100644 index 0000000000..db3710913b --- /dev/null +++ b/test/integration/options.js @@ -0,0 +1,140 @@ +var assert = require('assert'); +var run = require('./helpers').runMochaJSON; +var args = []; + +describe('options', function() { + this.timeout(2000); + + describe('--async-only', function() { + + before(function() { + args = ['--async-only']; + }); + + it('should fail synchronous specs', function(done) { + run('options/async-only.sync.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 0); + assert.equal(res.stats.failures, 1); + + assert.equal(res.failures[0].title, 'throws an error'); + assert.equal(res.code, 1); + done(); + }); + }); + + it('should allow asynchronous specs', function(done) { + run('options/async-only.async.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 1); + assert.equal(res.stats.failures, 0); + + assert.equal(res.passes[0].title, 'should pass'); + assert.equal(res.code, 0); + done(); + }); + }); + }); + + describe('--bail', function() { + before(function() { + args = ['--bail']; + }); + + it('should stop after the first error', function(done) { + run('options/bail.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 1); + assert.equal(res.stats.failures, 1); + + assert.equal(res.passes[0].title, 'should display this spec'); + assert.equal(res.failures[0].title, 'should only display this error'); + assert.equal(res.code, 1); + done(); + }); + }); + }); + + describe('--sort', function() { + before(function() { + args = ['--sort']; + }); + + it('should sort tests in alphabetical order', function(done) { + run('options/sort*', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 2); + assert.equal(res.stats.failures, 0); + + assert.equal(res.passes[0].fullTitle, + 'alpha should be executed first'); + assert.equal(res.code, 0); + done(); + }); + }); + }); + + describe('--delay', function() { + before(function() { + args = ['--delay']; + }); + + it('should run the generated test suite', function(done) { + run('options/delay.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 2); + assert.equal(res.stats.failures, 0); + + assert.equal(res.passes[0].title, + 'should have waited 500ms to run this suite'); + assert.equal(res.code, 0); + done(); + }); + }); + }); + + describe('--grep', function() { + it('runs specs matching a string', function(done) { + args = ['--grep', 'match']; + run('options/grep.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 2); + assert.equal(res.stats.failures, 0); + assert.equal(res.code, 0); + done(); + }); + }); + + it('runs specs matching a RegExp', function(done) { + args = ['--grep', '.*']; + run('options/grep.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 2); + assert.equal(res.stats.failures, 1); + assert.equal(res.code, 1); + done(); + }); + }); + + describe('with --invert', function() { + it('runs specs that do not match the pattern', function(done) { + args = ['--grep', 'fail', '--invert']; + run('options/grep.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 2); + assert.equal(res.stats.failures, 0); + assert.equal(res.code, 0); + done(); + }); + }); + }); + }); +}); diff --git a/test/integration/pending.js b/test/integration/pending.js new file mode 100644 index 0000000000..01ebbdf6eb --- /dev/null +++ b/test/integration/pending.js @@ -0,0 +1,63 @@ +var assert = require('assert'); +var run = require('./helpers').runMochaJSON; +var args = []; + +describe('pending', function() { + describe('pending specs', function() { + this.timeout(1000); + + it('should be created by omitting a function', function(done) { + run('pending/spec.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 1); + assert.equal(res.stats.passes, 0); + assert.equal(res.stats.failures, 0); + assert.equal(res.code, 0); + done(); + }); + }); + }); + + describe('synchronous skip()', function() { + this.timeout(1000); + + describe('in spec', function() { + it('should immediately skip the spec and run all others', function(done) { + run('pending/skip.sync.spec.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 1); + assert.equal(res.stats.passes, 1); + assert.equal(res.stats.failures, 0); + assert.equal(res.code, 0); + done(); + }); + }); + }); + + describe('in before', function() { + it('should skip all suite specs', function(done) { + run('pending/skip.sync.before.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 2); + assert.equal(res.stats.passes, 0); + assert.equal(res.stats.failures, 0); + assert.equal(res.code, 0); + done(); + }); + }); + }); + + describe('in beforeEach', function() { + it('should skip all suite specs', function(done) { + run('pending/skip.sync.beforeEach.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 2); + assert.equal(res.stats.passes, 0); + assert.equal(res.stats.failures, 0); + assert.equal(res.code, 0); + done(); + }); + }); + }); + }); +}); diff --git a/test/integration/regression.js b/test/integration/regression.js new file mode 100644 index 0000000000..494176da6a --- /dev/null +++ b/test/integration/regression.js @@ -0,0 +1,22 @@ +var assert = require('assert'); +var run = require('./helpers').runMochaJSON; + +describe('regressions', function() { + this.timeout(1000); + + it('issue-1327: should run all 3 specs exactly once', function(done) { + var args = []; + run('regression/issue-1327.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 2); + assert.equal(res.stats.failures, 1); + + assert.equal(res.passes[0].title, 'test 1'); + assert.equal(res.passes[1].title, 'test 2'); + assert.equal(res.failures[0].title, 'test 3'); + assert.equal(res.code, 1); + done(); + }); + }); +}); diff --git a/test/integration/timeout.js b/test/integration/timeout.js new file mode 100644 index 0000000000..9f0985c09d --- /dev/null +++ b/test/integration/timeout.js @@ -0,0 +1,18 @@ +var assert = require('assert'); +var run = require('./helpers').runMochaJSON; +var args = []; + +describe('this.timeout()', function() { + this.timeout(1000); + + it('is respected by sync and async suites', function(done) { + run('timeout.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 0); + assert.equal(res.stats.failures, 2); + assert.equal(res.code, 2); + done(); + }); + }); +}); diff --git a/test/integration/uncaught.js b/test/integration/uncaught.js new file mode 100644 index 0000000000..cddc52b1ea --- /dev/null +++ b/test/integration/uncaught.js @@ -0,0 +1,37 @@ +var assert = require('assert'); +var run = require('./helpers').runMochaJSON; +var args = []; + +describe('uncaught exceptions', function() { + this.timeout(1000); + + it('handles uncaught exceptions from hooks', function(done) { + run('uncaught.hook.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 0); + assert.equal(res.stats.failures, 1); + + assert.equal(res.failures[0].fullTitle, + 'uncaught "before each" hook'); + assert.equal(res.code, 1); + done(); + }); + }); + + it('handles uncaught exceptions from async specs', function(done) { + run('uncaught.js', args, function(err, res) { + assert(!err); + assert.equal(res.stats.pending, 0); + assert.equal(res.stats.passes, 0); + assert.equal(res.stats.failures, 2); + + assert.equal(res.failures[0].title, + 'fails exactly once when a global error is thrown first'); + assert.equal(res.failures[1].title, + 'fails exactly once when a global error is thrown second'); + assert.equal(res.code, 2); + done(); + }); + }); +}); diff --git a/test/jsapi/index.js b/test/jsapi/index.js index 2dcdd6dc68..2dbbcb0469 100644 --- a/test/jsapi/index.js +++ b/test/jsapi/index.js @@ -20,7 +20,6 @@ mocha.addFile('test/hook.async.js'); mocha.addFile('test/acceptance/duration.js'); mocha.addFile('test/acceptance/fs.js'); mocha.addFile('test/acceptance/globals.js'); -mocha.addFile('test/acceptance/pending.js'); mocha.addFile('test/acceptance/timeout.js'); mocha.run(function(){ diff --git a/test/regression/issue1327/case.js b/test/regression/issue1327/case.js deleted file mode 100644 index 295ec12413..0000000000 --- a/test/regression/issue1327/case.js +++ /dev/null @@ -1,14 +0,0 @@ -var debug = require('debug')('mocha:issue1327'); -it("test 1", function() { - debug("This runs only once."); - process.nextTick(function() { - throw "Too bad"; - }); -}); -it("test 2", function() { - debug("This should run once - Previously wasn't called at all."); -}); -it("test 3", function() { - debug("This used to run twice."); - throw new Error("OUCH"); -}); diff --git a/test/regression/issue1327/control.js b/test/regression/issue1327/control.js deleted file mode 100644 index b77555da02..0000000000 --- a/test/regression/issue1327/control.js +++ /dev/null @@ -1,10 +0,0 @@ -var assert = require("assert"), - fs = require("fs"); - -describe("GitHub issue #1327: expected behavior of case.js", function() { - it("should have run 3 tests", function() { - var results = JSON.parse(fs.readFileSync( - "test-outputs/issue1327/case-out.json")); - results.stats.tests.should.equal(3); - }); -});