diff --git a/docs/API.md b/docs/API.md index 13cd61a59de3..85f7c53d379b 100644 --- a/docs/API.md +++ b/docs/API.md @@ -82,7 +82,6 @@ Jest uses Jasmine 2 by default. An introduction to Jasmine 2 can be found - `it(name, fn)` - `fit(name, fn)` executes only this test. Useful when investigating a failure - [`jest`](#the-jest-object) - - `pit(name, fn)` [async helper](/jest/docs/tutorial-async.html) for promises - `require(module)` - `require.requireActual(module)` - `xdescribe(name, fn)` diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 9b5ab64a21cb..61cee82bb8be 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -135,8 +135,7 @@ project. #### Async testing -Promises and even async/await can be tested easily. Jest provides a helper -called `pit` for any kind of async interaction. +Promises and even async/await can be tested easily. Assume a `user.getUserName` function that returns a promise, now consider this async test with Babel and @@ -149,14 +148,13 @@ jest.unmock('../user'); import * as user from '../user'; describe('async tests', () => { - // Use `pit` instead of `it` for testing promises. // The promise that is being tested should be returned. - pit('works with promises', () => { + it('works with promises', () => { return user.getUserName(5) .then(name => expect(name).toEqual('Paul')); }); - pit('works with async/await', async () => { + it('works with async/await', async () => { const userName = await user.getUserName(4); expect(userName).toEqual('Mark'); }); diff --git a/docs/TutorialAsync.md b/docs/TutorialAsync.md index 6bc337bb63dd..5d8b07418727 100644 --- a/docs/TutorialAsync.md +++ b/docs/TutorialAsync.md @@ -69,8 +69,7 @@ export default function request(url) { } ``` -Now let's write a test for our async functionality. Jest provides the `pit` -wrapper to test Promise based code or async/await: +Now let's write a test for our async functionality. ```js // __tests__/user-test.js jest.unmock('../user'); @@ -78,27 +77,26 @@ jest.unmock('../user'); import * as user from '../user'; describe('async tests', () => { - // Use `pit` instead of `it` for testing promises. // The promise that is being tested should be returned. - pit('works with promises', () => { + it('works with promises', () => { return user.getUserName(5) .then(name => expect(name).toEqual('Paul')); }); }); ``` -`pit` expects the return value to be a Promise that is going to be resolved. +`it` expects the return value to be a Promise that is going to be resolved. You can chain as many Promises as you like and call `expect` at any time, as long as you return a Promise at the end. ### `async`/`await` -Writing tests using the `async`/`await` syntax is easy with `pit`. Here is +Writing tests using the `async`/`await` syntax is easy. Here is how you'd write the same example from before: ```js // async/await can also be used. - pit('works with async/await', async () => { + it('works with async/await', async () => { const userName = await user.getUserName(4); expect(userName).toEqual('Mark'); }); @@ -117,7 +115,7 @@ if a Promise throws and the error is not handled, the test will fail. ```js // Testing for async errors can be done using `catch`. - pit('tests error with promises', () => { + it('tests error with promises', () => { return user.getUserName(3) .catch(e => expect(e).toEqual({ error: 'User with 3 not found.', @@ -125,7 +123,7 @@ if a Promise throws and the error is not handled, the test will fail. }); // Or try-catch. - pit('tests error with async/await', async () => { + it('tests error with async/await', async () => { try { await user.getUserName(2); } catch (object) { diff --git a/examples/async/__tests__/user-test.js b/examples/async/__tests__/user-test.js index 6e2595f4272d..61258ea6deff 100644 --- a/examples/async/__tests__/user-test.js +++ b/examples/async/__tests__/user-test.js @@ -9,19 +9,19 @@ import * as user from '../user'; describe('async tests', () => { // Use `pit` instead of `it` for testing promises. // The promise that is being tested should be returned. - pit('works with promises', () => { + it('works with promises', () => { return user.getUserName(5) .then(name => expect(name).toEqual('Paul')); }); // async/await can also be used. - pit('works with async/await', async () => { + it('works with async/await', async () => { const userName = await user.getUserName(4); expect(userName).toEqual('Mark'); }); // Testing for async errors can be done using `catch`. - pit('tests error with promises', () => { + it('tests error with promises', () => { return user.getUserName(3) .catch(e => expect(e).toEqual({ error: 'User with 3 not found.', @@ -29,7 +29,7 @@ describe('async tests', () => { }); // Or try-catch. - pit('tests error with async/await', async () => { + it('tests error with async/await', async () => { try { await user.getUserName(2); } catch (object) { diff --git a/integration_tests/__tests__/promise_it-test.js b/integration_tests/__tests__/promise_it-test.js new file mode 100644 index 000000000000..fb03c74e702d --- /dev/null +++ b/integration_tests/__tests__/promise_it-test.js @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +jest.unmock('../runJest'); + +const runJest = require('../runJest'); + +describe('promise it', () => { + it('works with fit', () => { + const result = runJest.json('promise_it', ['promise_fit-test.js']); + const json = result.json; + + expect(json.numTotalTests).toBe(3); + expect(json.numPassedTests).toBe(1); + expect(json.numFailedTests).toBe(1); + expect(json.numPendingTests).toBe(1); + expect(json.testResults[0].message).toMatch(/will run and fail/); + }); + + it('works with xit', () => { + const result = runJest.json('promise_it', ['promise_xit-test.js']); + const json = result.json; + + expect(json.numTotalTests).toBe(2); + expect(json.numPassedTests).toBe(1); + expect(json.numFailedTests).toBe(0); + expect(json.numPendingTests).toBe(1); + }); + + it('throws when not a promise is returned', () => { + const result = runJest.json('promise_it', ['returning_values-test.js']); + const json = result.json; + + expect(json.numTotalTests).toBe(11); + expect(json.numPassedTests).toBe(0); + expect(json.numFailedTests).toBe(11); + expect(json.numPendingTests).toBe(0); + }); + + it('tests async promise code', () => { + const result = runJest.json('promise_it', ['promise_it-test.js']); + const json = result.json; + const message = json.testResults[0].message; + + expect(json.numTotalTests).toBe(7); + expect(json.numPassedTests).toBe(4); + expect(json.numFailedTests).toBe(3); + + expect(message).toMatch('fails if promise is rejected'); + expect(message).toMatch('works with done.fail'); + expect(message).toMatch('fails a sync test'); + }); + + it('works with pit', () => { + const result = runJest.json('promise_it', ['pit-test.js']); + const json = result.json; + expect(json.numTotalTests).toBe(2); + expect(json.numPassedTests).toBe(1); + expect(json.numFailedTests).toBe(1); + expect(json.testResults[0].message).toMatch(/will run and fail/); + }); +}); diff --git a/integration_tests/promise_it/__tests__/pit-test.js b/integration_tests/promise_it/__tests__/pit-test.js new file mode 100644 index 000000000000..71a4f03d85e2 --- /dev/null +++ b/integration_tests/promise_it/__tests__/pit-test.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +describe('pit alias', () => { + pit('will run', () => { + return Promise.resolve(); + }); + + pit('will run and fail', () => { + return Promise.reject(); + }); +}); diff --git a/integration_tests/promise_it/__tests__/promise_fit-test.js b/integration_tests/promise_it/__tests__/promise_fit-test.js new file mode 100644 index 000000000000..9ae926d3ac26 --- /dev/null +++ b/integration_tests/promise_it/__tests__/promise_fit-test.js @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +describe('promise fit', () => { + it('fails but will be skipped', () => { + expect(true).toBe(false); + }); + + fit('will run', () => { + return Promise.resolve(); + }); + + fit('will run and fail', () => { + return Promise.reject(); + }); +}); diff --git a/integration_tests/promise_it/__tests__/promise_it-test.js b/integration_tests/promise_it/__tests__/promise_it-test.js new file mode 100644 index 000000000000..a684bd0b1317 --- /dev/null +++ b/integration_tests/promise_it/__tests__/promise_it-test.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +describe('promise it', () => { + beforeEach(() => { + this.someContextValue = 'value'; + }); + + // passing tests + it('passes a sync test', () => { + expect(1).toBe(1); + }); + + it('waits for promise to be resolved', () => { + return Promise.resolve(); + }); + + it('works with done', done => { + done(); + }); + + it('is bount to context object', () => { + return new Promise(resolve => { + if (this.someContextValue !== 'value') { + throw new Error('expected this.someContextValue to be set', this.someContextValue); + } + resolve(); + }); + }); + + // failing tests + it('fails if promise is rejected', () => { + return Promise.reject(new Error('rejected promise returned')); + }); + + it('works with done.fail', done => { + done.fail(new Error('done.fail was called')); + }); + + it('fails a sync test', () => { + expect('sync').toBe('failed'); + }); +}); diff --git a/integration_tests/promise_it/__tests__/promise_xit-test.js b/integration_tests/promise_it/__tests__/promise_xit-test.js new file mode 100644 index 000000000000..8889f23ae661 --- /dev/null +++ b/integration_tests/promise_it/__tests__/promise_xit-test.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +describe('promise xit', () => { + xit('fails but will be skipped', () => { + expect(true).toBe(false); + }); + + it('will run', () => { + return Promise.resolve(); + }); +}); diff --git a/integration_tests/promise_it/__tests__/returning_values-test.js b/integration_tests/promise_it/__tests__/returning_values-test.js new file mode 100644 index 000000000000..7821ca41899d --- /dev/null +++ b/integration_tests/promise_it/__tests__/returning_values-test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + + 'use strict'; + + describe('returning values', () => { + [1, 'string', 0.1, null, NaN, Infinity, true, false, [1], {}, () => {}] + .forEach(val => { + it(`throws if '${val}:${typeof val}' is returned`, () => { + return val; + }); + }); + }); diff --git a/integration_tests/promise_it/package.json b/integration_tests/promise_it/package.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/integration_tests/promise_it/package.json @@ -0,0 +1 @@ +{} diff --git a/integration_tests/runJest.js b/integration_tests/runJest.js index ffb4a8720cac..5ce1fbd107f8 100644 --- a/integration_tests/runJest.js +++ b/integration_tests/runJest.js @@ -13,8 +13,18 @@ const JEST_PATH = path.resolve(__dirname, '../packages/jest-cli/bin/jest.js'); // return the result of the spawned proccess: // [ 'status', 'signal', 'output', 'pid', 'stdout', 'stderr', // 'envPairs', 'options', 'args', 'file' ] -module.exports = function runJest(dir, args) { +function runJest(dir, args) { return spawnSync(JEST_PATH, args || [], { cwd: path.resolve(__dirname, dir), }); +} + +// Runs `jest` with `--json` option and adds `json` property to the result obj. +runJest.json = function(dir, args) { + args = Array.prototype.concat(args || [], '--json'); + const result = runJest(dir, args); + result.json = JSON.parse((result.stdout || '').toString()); + return result; }; + +module.exports = runJest; diff --git a/packages/jest-cli/src/__tests__/TestRunner-fs-test.js b/packages/jest-cli/src/__tests__/TestRunner-fs-test.js index 267b1cb1e7ef..b894eee64695 100644 --- a/packages/jest-cli/src/__tests__/TestRunner-fs-test.js +++ b/packages/jest-cli/src/__tests__/TestRunner-fs-test.js @@ -21,7 +21,7 @@ describe('TestRunner-fs', () => { describe('testPathsMatching', () => { - pit('finds tests with default file extensions', () => { + it('finds tests with default file extensions', () => { const rootDir = path.resolve(__dirname, 'test_root'); const runner = new TestRunner(normalizeConfig({ cacheDirectory: global.CACHE_DIRECTORY, @@ -37,7 +37,7 @@ describe('TestRunner-fs', () => { }); }); - pit('finds tests with similar but custom file extensions', () => { + it('finds tests with similar but custom file extensions', () => { const rootDir = path.resolve(__dirname, 'test_root'); const runner = new TestRunner(normalizeConfig({ cacheDirectory: global.CACHE_DIRECTORY, @@ -54,7 +54,7 @@ describe('TestRunner-fs', () => { }); }); - pit('finds tests with totally custom foobar file extensions', () => { + it('finds tests with totally custom foobar file extensions', () => { const rootDir = path.resolve(__dirname, 'test_root'); const runner = new TestRunner(normalizeConfig({ cacheDirectory: global.CACHE_DIRECTORY, @@ -71,7 +71,7 @@ describe('TestRunner-fs', () => { }); }); - pit('finds tests with many kinds of file extensions', () => { + it('finds tests with many kinds of file extensions', () => { const rootDir = path.resolve(__dirname, 'test_root'); const runner = new TestRunner(normalizeConfig({ cacheDirectory: global.CACHE_DIRECTORY, diff --git a/packages/jest-cli/src/__tests__/TestRunner-test.js b/packages/jest-cli/src/__tests__/TestRunner-test.js index bf1732582f3b..b81e9391ca6c 100644 --- a/packages/jest-cli/src/__tests__/TestRunner-test.js +++ b/packages/jest-cli/src/__tests__/TestRunner-test.js @@ -82,14 +82,14 @@ describe('TestRunner', () => { }); }); - pit('makes sure a file is related to itself', () => { + it('makes sure a file is related to itself', () => { return runner.promiseTestPathsRelatedTo(new Set([rootPath])) .then(relatedTests => { expect(relatedTests).toEqual([rootPath]); }); }); - pit('finds tests that depend directly on the path', () => { + it('finds tests that depend directly on the path', () => { const filePath = path.join(rootDir, 'RegularModule.js'); const parentDep = path.join(rootDir, 'ModuleWithSideEffects.js'); return runner.promiseTestPathsRelatedTo(new Set([filePath])) diff --git a/packages/jest-cli/src/lib/__tests__/promisify-test.js b/packages/jest-cli/src/lib/__tests__/promisify-test.js index 6b57fe697cb2..d218213cc47e 100644 --- a/packages/jest-cli/src/lib/__tests__/promisify-test.js +++ b/packages/jest-cli/src/lib/__tests__/promisify-test.js @@ -18,7 +18,7 @@ describe('promisify', () => { promisify = require('../promisify'); }); - pit('should resolve', () => { + it('should resolve', () => { const foo = promisify(callback => { callback(null, 1); }); @@ -28,7 +28,7 @@ describe('promisify', () => { }); }); - pit('should resolve with args', () => { + it('should resolve with args', () => { const foo = promisify((a, b, callback) => { callback(null, a + b); }); @@ -38,7 +38,7 @@ describe('promisify', () => { }); }); - pit('should reject with args', () => { + it('should reject with args', () => { const foo = promisify((a, b, callback) => { callback(new Error('lol')); }); diff --git a/packages/jest-haste-map/src/__tests__/index-test.js b/packages/jest-haste-map/src/__tests__/index-test.js index d1c39d0587cd..d19f2c865f07 100644 --- a/packages/jest-haste-map/src/__tests__/index-test.js +++ b/packages/jest-haste-map/src/__tests__/index-test.js @@ -178,7 +178,7 @@ describe('HasteMap', () => { ); }); - pit('matches files against a pattern', () => { + it('matches files against a pattern', () => { const hasteMap = new HasteMap(defaultConfig); return hasteMap.matchFiles(/fruits/).then(files => { @@ -197,7 +197,7 @@ describe('HasteMap', () => { }); }); - pit('builds a haste map on a fresh cache', () => { + it('builds a haste map on a fresh cache', () => { // Include these files in the map mockFs['/fruits/node_modules/react/react.js'] = [ '/**', @@ -275,7 +275,7 @@ describe('HasteMap', () => { }); }); - pit('warns on duplicate module ids', () => { + it('warns on duplicate module ids', () => { // Raspberry thinks it is a Strawberry mockFs['/fruits/raspberry.js'] = [ '/**', @@ -299,7 +299,7 @@ describe('HasteMap', () => { }); }); - pit('splits up modules by platform', () => { + it('splits up modules by platform', () => { mockFs = Object.create(null); mockFs['/fruits/strawberry.js'] = [ '/**', @@ -339,7 +339,7 @@ describe('HasteMap', () => { }); }); - pit('does not access the file system on a warm cache with no changes', () => { + it('does not access the file system on a warm cache with no changes', () => { return new HasteMap(defaultConfig).build().then(initialData => { // The first run should access the file system once for the (empty) cache // file and five times for the files in the system. @@ -367,7 +367,7 @@ describe('HasteMap', () => { }); }); - pit('only does minimal file system access when files change', () => { + it('only does minimal file system access when files change', () => { return new HasteMap(defaultConfig).build().then(initialData => { fs.readFileSync.mockClear(); @@ -409,7 +409,7 @@ describe('HasteMap', () => { }); }); - pit('correctly handles file deletions', () => { + it('correctly handles file deletions', () => { return new HasteMap(defaultConfig).build().then(initialData => { fs.readFileSync.mockClear(); @@ -437,7 +437,7 @@ describe('HasteMap', () => { }); }); - pit('ignores files that do not exist', () => { + it('ignores files that do not exist', () => { const watchman = require('../crawlers/watchman'); // getMockImplementation should be a public method. const mockImpl = watchman._getMockImplementation(); @@ -456,7 +456,7 @@ describe('HasteMap', () => { }); }); - pit('distributes work across workers', () => { + it('distributes work across workers', () => { const workerFarm = require('worker-farm'); return new HasteMap(Object.assign({}, defaultConfig, { maxWorkers: 4, @@ -477,7 +477,7 @@ describe('HasteMap', () => { }); }); - pit('tries to crawl using node as a fallback', () => { + it('tries to crawl using node as a fallback', () => { const watchman = require('../crawlers/watchman'); const node = require('../crawlers/node'); @@ -506,7 +506,7 @@ describe('HasteMap', () => { }); }); - pit('tries to crawl using node as a fallback when promise fails once', () => { + it('tries to crawl using node as a fallback when promise fails once', () => { const watchman = require('../crawlers/watchman'); const node = require('../crawlers/node'); @@ -530,7 +530,7 @@ describe('HasteMap', () => { }); }); - pit('stops crawling when both crawlers fail', () => { + it('stops crawling when both crawlers fail', () => { const watchman = require('../crawlers/watchman'); const node = require('../crawlers/node'); diff --git a/packages/jest-haste-map/src/crawlers/__tests__/node-test.js b/packages/jest-haste-map/src/crawlers/__tests__/node-test.js index 71e72c59f71c..0fa2d454dc83 100644 --- a/packages/jest-haste-map/src/crawlers/__tests__/node-test.js +++ b/packages/jest-haste-map/src/crawlers/__tests__/node-test.js @@ -79,7 +79,7 @@ describe('node crawler', () => { ].join('\n'); }); - pit('crawls for files based on patterns', () => { + it('crawls for files based on patterns', () => { process.platform = 'linux'; childProcess = require('child_process'); @@ -128,7 +128,7 @@ describe('node crawler', () => { return promise; }); - pit('updates only changed files', () => { + it('updates only changed files', () => { process.platform = 'linux'; nodeCrawl = require('../node'); @@ -156,7 +156,7 @@ describe('node crawler', () => { return promise; }); - pit('uses node fs APIs on windows', () => { + it('uses node fs APIs on windows', () => { process.platform = 'win32'; nodeCrawl = require('../node'); diff --git a/packages/jest-haste-map/src/crawlers/__tests__/watchman-test.js b/packages/jest-haste-map/src/crawlers/__tests__/watchman-test.js index dc73c9c2234d..a4a14019431e 100644 --- a/packages/jest-haste-map/src/crawlers/__tests__/watchman-test.js +++ b/packages/jest-haste-map/src/crawlers/__tests__/watchman-test.js @@ -82,7 +82,7 @@ describe('watchman watch', () => { }); }); - pit('returns a list of all files when there are no clocks', () => { + it('returns a list of all files when there are no clocks', () => { const watchman = require('fb-watchman'); const path = require('../../fastpath'); @@ -146,7 +146,7 @@ describe('watchman watch', () => { }); }); - pit('updates the file object when the clock is given', () => { + it('updates the file object when the clock is given', () => { mockResponse = { '/fruits': { version: '4.5.0', @@ -202,7 +202,7 @@ describe('watchman watch', () => { }); }); - pit('resets the file object when watchman is restarted', () => { + it('resets the file object when watchman is restarted', () => { mockResponse = { '/fruits': { version: '4.5.0', @@ -275,7 +275,7 @@ describe('watchman watch', () => { }); }); - pit('properly resets the file map when only one watcher is reset', () => { + it('properly resets the file map when only one watcher is reset', () => { mockResponse = { '/fruits': { version: '4.5.0', diff --git a/packages/jest-jasmine1/src/__tests__/reporter-test.js b/packages/jest-jasmine1/src/__tests__/reporter-test.js index e7874effcd6a..8ece3a485b8b 100644 --- a/packages/jest-jasmine1/src/__tests__/reporter-test.js +++ b/packages/jest-jasmine1/src/__tests__/reporter-test.js @@ -74,7 +74,7 @@ describe('JasmineReporter', () => { return chalk.bgRed(str); } - pit('colorizes single-line failures using a per-char diff', () => { + it('colorizes single-line failures using a per-char diff', () => { const runner = getExpectedRunner('foo', 'foobar', false); reporter.reportRunnerResults(runner); @@ -87,7 +87,7 @@ describe('JasmineReporter', () => { }); }); - pit('colorizes multi-line failures using a per-line diff', () => { + it('colorizes multi-line failures using a per-line diff', () => { const runner = getExpectedRunner('foo\nbar\nbaz', 'foo\nxxx\nbaz', false); reporter.reportRunnerResults(runner); @@ -99,6 +99,5 @@ describe('JasmineReporter', () => { ); }); }); - }); }); diff --git a/packages/jest-jasmine1/src/index.js b/packages/jest-jasmine1/src/index.js index 2edea05c9495..e28be97532cd 100644 --- a/packages/jest-jasmine1/src/index.js +++ b/packages/jest-jasmine1/src/index.js @@ -8,7 +8,7 @@ 'use strict'; const fs = require('graceful-fs'); -const jasminePit = require('jest-util').jasminePit; +const jasminePit = require('./jasmine-pit'); const JasmineReporter = require('./reporter'); const JASMINE_PATH = require.resolve('../vendor/jasmine-1.3.0'); diff --git a/packages/jest-jasmine1/src/jasmine-pit.js b/packages/jest-jasmine1/src/jasmine-pit.js new file mode 100644 index 000000000000..f7f9b7e7da12 --- /dev/null +++ b/packages/jest-jasmine1/src/jasmine-pit.js @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2014, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +function assertPromise(promise) { + if (!promise || typeof promise.then !== 'function') { + throw new Error(`pit(...): tests must return a promise`); + } +} + +function wrapForJasmine1(env, promiseFn) { + return () => { + let error = null; + let isFinished = false; + const currentSpec = env.currentSpec; + currentSpec.runs(() => { + try { + const promise = promiseFn.call(currentSpec); + assertPromise(promise); + promise + .then(() => isFinished = true) + .catch(err => { + error = err; + isFinished = true; + }); + } catch (e) { + error = e; + isFinished = true; + } + }); + currentSpec.waitsFor(() => isFinished); + currentSpec.runs(() => { + if (error) { + throw error; + } + }); + }; +} + +function install(global) { + const jasmine = global.jasmine; + const env = jasmine.getEnv(); + const wrapFn = wrapForJasmine1.bind(null, env); + + global.pit = function pit(specName, promiseFn) { + return env.it(specName, wrapFn(promiseFn)); + }; + + global.xpit = function xpit(specName, promiseFn) { + return env.xit(specName, wrapFn(promiseFn)); + }; + + global.pit.only = function pitOnly(specName, promiseFn) { + return env.it.only(specName, wrapFn(promiseFn)); + }; +} + +module.exports = { + install, +}; diff --git a/packages/jest-jasmine2/src/__tests__/reporter-test.js b/packages/jest-jasmine2/src/__tests__/reporter-test.js index 71573f39a825..edfc53b177ad 100644 --- a/packages/jest-jasmine2/src/__tests__/reporter-test.js +++ b/packages/jest-jasmine2/src/__tests__/reporter-test.js @@ -25,7 +25,7 @@ describe('Jasmine2Reporter', () => { describe('suites', () => { - pit('reports nested suites', () => { + it('reports nested suites', () => { const makeSpec = name => ({ fullName: name, description: 'description', @@ -76,7 +76,7 @@ describe('Jasmine2Reporter', () => { return chalk.bgRed(str); } - pit('colorizes single-line failures using a per-char diff', () => { + it('colorizes single-line failures using a per-char diff', () => { const result = getFailedResult('foo', 'foobar'); reporter.specDone(result); reporter.jasmineDone(); @@ -90,7 +90,7 @@ describe('Jasmine2Reporter', () => { }); }); - pit('colorizes multi-line failures using a per-line diff', () => { + it('colorizes multi-line failures using a per-line diff', () => { const result = getFailedResult('foo\nbar\nbaz', 'foo\nxxx\nbaz'); reporter.specDone(result); reporter.jasmineDone(); @@ -103,6 +103,5 @@ describe('Jasmine2Reporter', () => { ); }); }); - }); }); diff --git a/packages/jest-jasmine2/src/index.js b/packages/jest-jasmine2/src/index.js index 2a45708182b0..957914da843c 100644 --- a/packages/jest-jasmine2/src/index.js +++ b/packages/jest-jasmine2/src/index.js @@ -8,7 +8,7 @@ 'use strict'; const fs = require('graceful-fs'); -const jasminePit = require('jest-util').jasminePit; +const jasminePit = require('./jasmine-pit'); const JasmineReporter = require('./reporter'); const CALL_PRINT_LIMIT = 3; diff --git a/packages/jest-jasmine2/src/jasmine-pit.js b/packages/jest-jasmine2/src/jasmine-pit.js new file mode 100644 index 000000000000..0008b59c0d67 --- /dev/null +++ b/packages/jest-jasmine2/src/jasmine-pit.js @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2014, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + + /** + * This module adds ability to test async promise code with jasmine by + * returning a promise from `it\fit` block. + */ + +'use strict'; + +function isPromise(obj) { + return obj && typeof obj.then === 'function'; +} + +// return a wrapping function: `env.fit = promisifyIt(env.it, env)` +function promisifyIt(originalFn, env) { + return function(specName, fn) { + const isAsync = !!fn.length; // `done` was passed + + if (isAsync) { + return originalFn.call(env, specName, fn); // jasmine will handle it + } else { + // we make *all* tests async and run `done` right away if they + // didn't return a promise. + return originalFn.call(env, specName, function(done) { + const returnValue = fn.apply(this, arguments); + + if (isPromise(returnValue)) { + returnValue.then(done).catch(done.fail); + } else if (returnValue === undefined) { + done(); + } else { + done.fail(new Error( + 'Jest: `it` must return either a Promise or undefined.' + )); + } + }); + } + }; +} + +function install(global) { + const jasmine = global.jasmine; + + const env = jasmine.getEnv(); + // env.xit will not run anyways so we don't have to worry about it + global.pit = env.it = promisifyIt(env.it, env); + env.fit = promisifyIt(env.fit, env); +} + +module.exports = { + install, +}; diff --git a/packages/jest-util/src/index.js b/packages/jest-util/src/index.js index a48fe1069c4b..c63a1cc65b9b 100644 --- a/packages/jest-util/src/index.js +++ b/packages/jest-util/src/index.js @@ -14,7 +14,6 @@ const JasmineFormatter = require('./JasmineFormatter'); const installCommonGlobals = require('./installCommonGlobals'); const formatFailureMessage = require('./formatFailureMessage'); const path = require('path'); -const pit = require('./jasmine-pit'); function escapeStrForRegex(str) { return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); @@ -30,7 +29,6 @@ function replacePathSepForRegex(str) { exports.escapeStrForRegex = escapeStrForRegex; exports.FakeTimers = FakeTimers; exports.formatFailureMessage = formatFailureMessage; -exports.jasminePit = pit; exports.JasmineFormatter = JasmineFormatter; exports.installCommonGlobals = installCommonGlobals; exports.replacePathSepForRegex = replacePathSepForRegex; diff --git a/packages/jest-util/src/jasmine-pit.js b/packages/jest-util/src/jasmine-pit.js index d7a1b7a9e3cd..0a38a6f72be1 100644 --- a/packages/jest-util/src/jasmine-pit.js +++ b/packages/jest-util/src/jasmine-pit.js @@ -5,82 +5,56 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ -'use strict'; -function assertPromise(promise) { - if (!promise || typeof promise.then !== 'function') { - throw new Error(`pit(...): tests must return a promise`); - } -} + /** + * This module adds ability to test async promise code with jasmine by + * returning a promise from `it\fit` block. + */ -function wrapForJasmine1(env, promiseFn) { - return () => { - let error = null; - let isFinished = false; - const currentSpec = env.currentSpec; - currentSpec.runs(() => { - try { - const promise = promiseFn.call(currentSpec); - assertPromise(promise); - promise - .then(() => isFinished = true) - .catch(err => { - error = err; - isFinished = true; - }); - } catch (e) { - error = e; - isFinished = true; - } - }); - currentSpec.waitsFor(() => isFinished); - currentSpec.runs(() => { - if (error) { - throw error; - } - }); - }; -} +'use strict'; -function wrapForJasmine2(promiseFn) { - return done => { - const promise = promiseFn(); - const fail = done.fail ? done.fail : err => { throw err; }; +function isPromise(obj) { + return obj && typeof obj.then === 'function'; +} - try { - assertPromise(promise); - } catch (err) { - fail(err); +// return a wrapping function: `env.fit = promisifyIt(env.it, env)` +function promisifyIt(originalFn, env) { + return function(specName, fn) { + const isAsync = !!fn.length; // `done` was passed + + if (isAsync) { + return originalFn.call(env, specName, fn); // jasmine will handle it + } else { + // we make *all* tests async and run `done` right away if they + // didn't return a promise. + return originalFn.call(env, specName, function(done) { + const returnValue = fn.apply(this, arguments); + + if (isPromise(returnValue)) { + returnValue.then(done).catch(done.fail); + } else if (returnValue === undefined) { + done(); + } else { + done.fail(new Error( + 'Jest: `it` must return either a Promise or undefined.' + )); + } + }); } - - promise - .then(res => done(res)) - .catch(err => fail(err)); }; } function install(global) { const jasmine = global.jasmine; - const env = jasmine.getEnv(); - const isJasmine1 = jasmine.version_ && jasmine.version_.major === 1; - const wrapFn = isJasmine1 ? wrapForJasmine1.bind(null, env) : wrapForJasmine2; - global.pit = function pit(specName, promiseFn) { - return env.it(specName, wrapFn(promiseFn)); - }; - global.xpit = function xpit(specName, promiseFn) { - return env.xit(specName, wrapFn(promiseFn)); - }; - - if (isJasmine1) { - global.pit.only = function pitOnly(specName, promiseFn) { - return env.it.only(specName, wrapFn(promiseFn)); - }; - } else { - global.fpit = function fpit(desc, promiseFn) { - return env.fit(desc, wrapFn(promiseFn)); - }; + if (jasmine.version_ && jasmine.version_.major === 1) { + return require('./jasmine1-pit.js').install(global); } + + const env = jasmine.getEnv(); + // env.xit will not run anyways so we don't have to worry about it + env.pit = env.it = promisifyIt(env.it, env); + env.fit = promisifyIt(env.fit, env); } module.exports = { diff --git a/runTests.js b/runTests.js index 3dc10e3a58fc..552e83108d05 100644 --- a/runTests.js +++ b/runTests.js @@ -91,6 +91,7 @@ function runExampleTests(exampleDirectory) { packages.forEach(runPackageTests); + if (packagesOnly) { return; }