diff --git a/CHANGELOG.md b/CHANGELOG.md index ef70b719a8d8..3ef8b5a0fea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ### Features +* `[jest-jasmine2]` Adds error throwing and descriptive errors to `it`/ `test` + for invalid arguements. `[jest-circus]` Adds error throwing and descriptive + errors to `it`/ `test` for invalid arguements. * `[jest-matcher-utils]` Add `isNot` option to `matcherHint` function ([#5512](https://github.com/facebook/jest/pull/5512)) diff --git a/integration-tests/Utils.js b/integration-tests/Utils.js index 3fb045589a1f..bad044ad6faf 100644 --- a/integration-tests/Utils.js +++ b/integration-tests/Utils.js @@ -159,7 +159,9 @@ const extractSummary = (stdout: string) => { // unifies their output to make it possible to snapshot them. // TODO: Remove when we drop support for node 4 const cleanupStackTrace = (output: string) => { - return output.replace(/^.*at.*[\s][\(]?(\S*\:\d*\:\d*).*$/gm, ' at $1'); + return output + .replace(/.*(?=packages)/g, ' at ') + .replace(/^.*at.*[\s][\(]?(\S*\:\d*\:\d*).*$/gm, ' at $1'); }; module.exports = { diff --git a/integration-tests/__tests__/__snapshots__/failures.test.js.snap b/integration-tests/__tests__/__snapshots__/failures.test.js.snap index 28b3ea19ed4b..b773dd6645e9 100644 --- a/integration-tests/__tests__/__snapshots__/failures.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/failures.test.js.snap @@ -116,7 +116,7 @@ exports[`works with async failures 1`] = ` + \\"foo\\": \\"bar\\", } - at ../../packages/expect/build/index.js:104:76 + at packages/expect/build/index.js:104:76 " `; diff --git a/integration-tests/__tests__/__snapshots__/globals.test.js.snap b/integration-tests/__tests__/__snapshots__/globals.test.js.snap index 809211cb5725..280ee77d5556 100644 --- a/integration-tests/__tests__/__snapshots__/globals.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/globals.test.js.snap @@ -20,6 +20,77 @@ Ran all test suites. " `; +exports[`cannot test with no implementation 1`] = ` +"FAIL __tests__/only-constructs.test.js + ● Test suite failed to run + + Missing second argument. It must be a callback function. + + 1 | + 2 | it('it', () => {}); + > 3 | it('it, no implementation'); + 4 | test('test, no implementation'); + 5 | + + at packages/jest-jasmine2/build/jasmine/Env.js:390:15 + at __tests__/only-constructs.test.js:3:5 + +" +`; + +exports[`cannot test with no implementation 2`] = ` +"Test Suites: 1 failed, 1 total +Tests: 0 total +Snapshots: 0 total +Time: <> +Ran all test suites. +" +`; + +exports[`cannot test with no implementation with expand arg 1`] = ` +"FAIL __tests__/only-constructs.test.js + ● Test suite failed to run + + Missing second argument. It must be a callback function. + + 1 | + 2 | it('it', () => {}); + > 3 | it('it, no implementation'); + 4 | test('test, no implementation'); + 5 | + + at packages/jest-jasmine2/build/jasmine/Env.js:390:15 + at __tests__/only-constructs.test.js:3:5 + +" +`; + +exports[`cannot test with no implementation with expand arg 2`] = ` +"Test Suites: 1 failed, 1 total +Tests: 0 total +Snapshots: 0 total +Time: <> +Ran all test suites. +" +`; + +exports[`function as descriptor 1`] = ` +"PASS __tests__/function-as-descriptor.test.js + Foo + ✓ it + +" +`; + +exports[`function as descriptor 2`] = ` +"Test Suites: 1 passed, 1 total +Tests: 1 passed, 1 total +Snapshots: 0 total +Time: <> +Ran all test suites. +" +`; + exports[`only 1`] = ` "PASS __tests__/only-constructs.test.js ✓ test.only @@ -121,55 +192,3 @@ Time: <> Ran all test suites. " `; - -exports[`tests with no implementation 1`] = ` -"PASS __tests__/only-constructs.test.js - ✓ it - ○ skipped 2 tests - -" -`; - -exports[`tests with no implementation 2`] = ` -"Test Suites: 1 passed, 1 total -Tests: 2 skipped, 1 passed, 3 total -Snapshots: 0 total -Time: <> -Ran all test suites. -" -`; - -exports[`tests with no implementation with expand arg 1`] = ` -"PASS __tests__/only-constructs.test.js - ✓ it - ○ it, no implementation - ○ test, no implementation - -" -`; - -exports[`tests with no implementation with expand arg 2`] = ` -"Test Suites: 1 passed, 1 total -Tests: 2 skipped, 1 passed, 3 total -Snapshots: 0 total -Time: <> -Ran all test suites. -" -`; - -exports[`function as descriptor 1`] = ` -"PASS __tests__/function-as-descriptor.test.js - Foo - ✓ it - -" -`; - -exports[`function as descriptor 2`] = ` -"Test Suites: 1 passed, 1 total -Tests: 1 passed, 1 total -Snapshots: 0 total -Time: <> -Ran all test suites. -" -`; diff --git a/integration-tests/__tests__/globals.test.js b/integration-tests/__tests__/globals.test.js index e141b0e9c715..79cc76beaac8 100644 --- a/integration-tests/__tests__/globals.test.js +++ b/integration-tests/__tests__/globals.test.js @@ -110,7 +110,7 @@ test('only', () => { expect(summary).toMatchSnapshot(); }); -test('tests with no implementation', () => { +test('cannot test with no implementation', () => { const filename = 'only-constructs.test.js'; const content = ` it('it', () => {}); @@ -120,9 +120,10 @@ test('tests with no implementation', () => { writeFiles(TEST_DIR, {[filename]: content}); const {stderr, status} = runJest(DIR); - expect(status).toBe(0); + expect(status).toBe(1); const {summary, rest} = extractSummary(stderr); + expect(rest).toMatchSnapshot(); expect(summary).toMatchSnapshot(); }); @@ -188,7 +189,7 @@ test('only with expand arg', () => { expect(summary).toMatchSnapshot(); }); -test('tests with no implementation with expand arg', () => { +test('cannot test with no implementation with expand arg', () => { const filename = 'only-constructs.test.js'; const content = ` it('it', () => {}); @@ -198,7 +199,7 @@ test('tests with no implementation with expand arg', () => { writeFiles(TEST_DIR, {[filename]: content}); const {stderr, status} = runJest(DIR, ['--expand']); - expect(status).toBe(0); + expect(status).toBe(1); const {summary, rest} = extractSummary(stderr); expect(rest).toMatchSnapshot(); diff --git a/packages/jest-circus/src/__tests__/circus_it_test_error.test.js b/packages/jest-circus/src/__tests__/circus_it_test_error.test.js new file mode 100644 index 000000000000..da2e8d234e00 --- /dev/null +++ b/packages/jest-circus/src/__tests__/circus_it_test_error.test.js @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +'use strict'; + +let circusIt; +let circusTest; + +//using jest-jasmine2's 'it' to test jest-circus's 'it'. Had to differentiate +//the two with this aliaser. + +const aliasCircusIt = () => { + const {it, test} = require('../index.js'); + circusIt = it; + circusTest = test; +}; + +aliasCircusIt(); + +//A few of these tests require incorrect types to throw errors and thus pass +//the test. The typechecks on jest-circus would prevent that, so +//this file has been listed in the .flowconfig ignore section. + +describe('test/it error throwing', () => { + it(`it doesn't throw an error with valid arguments`, () => { + expect(() => { + circusIt('test1', () => {}); + }).not.toThrowError(); + }); + it(`it throws error with missing callback function`, () => { + expect(() => { + // $FlowFixMe: Easy, we're testing runitme errors here + circusIt('test2'); + }).toThrowError('Missing second argument. It must be a callback function.'); + }); + it(`it throws an error when first argument isn't a string`, () => { + expect(() => { + // $FlowFixMe: Easy, we're testing runitme errors here + circusIt(() => {}); + }).toThrowError(`Invalid first argument, () => {}. It must be a string.`); + }); + it('it throws an error when callback function is not a function', () => { + expect(() => { + // $FlowFixMe: Easy, we're testing runitme errors here + circusIt('test4', 'test4b'); + }).toThrowError( + `Invalid second argument, test4b. It must be a callback function.`, + ); + }); + it(`test doesn't throw an error with valid arguments`, () => { + expect(() => { + circusTest('test5', () => {}); + }).not.toThrowError(); + }); + it(`test throws error with missing callback function`, () => { + expect(() => { + // $FlowFixMe: Easy, we're testing runitme errors here + circusTest('test6'); + }).toThrowError('Missing second argument. It must be a callback function.'); + }); + it(`test throws an error when first argument isn't a string`, () => { + expect(() => { + // $FlowFixMe: Easy, we're testing runitme errors here + circusTest(() => {}); + }).toThrowError(`Invalid first argument, () => {}. It must be a string.`); + }); + it('test throws an error when callback function is not a function', () => { + expect(() => { + // $FlowFixMe: Easy, we're testing runitme errors here + circusTest('test8', 'test8b'); + }).toThrowError( + `Invalid second argument, test8b. It must be a callback function.`, + ); + }); +}); diff --git a/packages/jest-circus/src/index.js b/packages/jest-circus/src/index.js index e77a72f79518..0fbdfd218c0b 100644 --- a/packages/jest-circus/src/index.js +++ b/packages/jest-circus/src/index.js @@ -38,8 +38,22 @@ const beforeAll = (fn: HookFn) => _addHook(fn, 'beforeAll'); const afterEach = (fn: HookFn) => _addHook(fn, 'afterEach'); const afterAll = (fn: HookFn) => _addHook(fn, 'afterAll'); -const test = (testName: TestName, fn?: TestFn) => - dispatch({fn, name: 'add_test', testName}); +const test = (testName: TestName, fn: TestFn) => { + if (typeof testName !== 'string') { + throw new Error( + `Invalid first argument, ${testName}. It must be a string.`, + ); + } + if (fn === undefined) { + throw new Error('Missing second argument. It must be a callback function.'); + } + if (typeof fn !== 'function') { + throw new Error( + `Invalid second argument, ${fn}. It must be a callback function.`, + ); + } + return dispatch({fn, name: 'add_test', testName}); +}; const it = test; test.skip = (testName: TestName, fn?: TestFn) => dispatch({fn, mode: 'skip', name: 'add_test', testName}); diff --git a/packages/jest-jasmine2/src/__tests__/it_test_error.test.js b/packages/jest-jasmine2/src/__tests__/it_test_error.test.js new file mode 100644 index 000000000000..9f1727645ffd --- /dev/null +++ b/packages/jest-jasmine2/src/__tests__/it_test_error.test.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +describe('test/it error throwing', () => { + it(`it throws error with missing callback function`, () => { + expect(() => { + it('test1'); + }).toThrowError('Missing second argument. It must be a callback function.'); + }); + it(`it throws an error when first argument isn't a string`, () => { + expect(() => { + it(() => {}); + }).toThrowError(`Invalid first argument, () => {}. It must be a string.`); + }); + it('it throws an error when callback function is not a function', () => { + expect(() => { + it('test3', 'test3b'); + }).toThrowError( + `Invalid second argument, test3b. It must be a callback function.`, + ); + }); + test(`test throws error with missing callback function`, () => { + expect(() => { + test('test4'); + }).toThrowError('Missing second argument. It must be a callback function.'); + }); + test(`test throws an error when first argument isn't a string`, () => { + expect(() => { + test(() => {}); + }).toThrowError(`Invalid first argument, () => {}. It must be a string.`); + }); + test('test throws an error when callback function is not a function', () => { + expect(() => { + test('test6', 'test6b'); + }).toThrowError( + `Invalid second argument, test6b. It must be a callback function.`, + ); + }); +}); diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index 179529922ada..1f382ddb3e96 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -424,6 +424,21 @@ export default function(j$) { }; this.it = function(description, fn, timeout) { + if (typeof description !== 'string') { + throw new Error( + `Invalid first argument, ${description}. It must be a string.`, + ); + } + if (fn === undefined) { + throw new Error( + 'Missing second argument. It must be a callback function.', + ); + } + if (typeof fn !== 'function') { + throw new Error( + `Invalid second argument, ${fn}. It must be a callback function.`, + ); + } const spec = specFactory( description, fn, diff --git a/packages/jest-message-util/src/index.js b/packages/jest-message-util/src/index.js index 91db678e0d56..a990cde4aa65 100644 --- a/packages/jest-message-util/src/index.js +++ b/packages/jest-message-util/src/index.js @@ -45,7 +45,7 @@ type StackTraceOptions = { }; const PATH_NODE_MODULES = `${path.sep}node_modules${path.sep}`; -const PATH_EXPECT_BUILD = `${path.sep}expect${path.sep}build${path.sep}`; +const PATH_JEST_PACKAGES = `${path.sep}jest${path.sep}packages${path.sep}`; // filter for noisy stack trace lines const JASMINE_IGNORE = /^\s+at(?:(?:.*?vendor\/|jasmine\-)|\s+jasmine\.buildExpectationResult)/; @@ -220,7 +220,7 @@ const formatPaths = ( const getTopFrame = (lines: string[]) => { for (const line of lines) { - if (line.includes(PATH_NODE_MODULES) || line.includes(PATH_EXPECT_BUILD)) { + if (line.includes(PATH_NODE_MODULES) || line.includes(PATH_JEST_PACKAGES)) { continue; } diff --git a/yarn.lock b/yarn.lock index 1e32c1df1b78..4fb55bb21993 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4534,10 +4534,6 @@ jest-docblock@^21, jest-docblock@^21.0.0, jest-docblock@^21.2.0: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" -jest-get-type@^21.2.0: - version "21.2.0" - resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" - jest-haste-map@^21: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-21.2.0.tgz#1363f0a8bb4338f24f001806571eff7a4b2ff3d8" @@ -4549,39 +4545,6 @@ jest-haste-map@^21: sane "^2.0.0" worker-farm "^1.3.1" -jest-message-util@^21.2.1: - version "21.2.1" - resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-message-util/-/jest-message-util-21.2.1.tgz#bfe5d4692c84c827d1dcf41823795558f0a1acbe" - dependencies: - chalk "^2.0.1" - micromatch "^2.3.11" - slash "^1.0.0" - -jest-mock@^21.2.0: - version "21.2.0" - resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-mock/-/jest-mock-21.2.0.tgz#7eb0770e7317968165f61ea2a7281131534b3c0f" - -jest-util@^21.2.1: - version "21.2.1" - resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-util/-/jest-util-21.2.1.tgz#a274b2f726b0897494d694a6c3d6a61ab819bb78" - dependencies: - callsites "^2.0.0" - chalk "^2.0.1" - graceful-fs "^4.1.11" - jest-message-util "^21.2.1" - jest-mock "^21.2.0" - jest-validate "^21.2.1" - mkdirp "^0.5.1" - -jest-validate@^21.2.1: - version "21.2.1" - resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" - dependencies: - chalk "^2.0.1" - jest-get-type "^21.2.0" - leven "^2.1.0" - pretty-format "^21.2.1" - jquery@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787" @@ -6237,13 +6200,6 @@ prettier@^1.10.1: version "1.10.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.1.tgz#01423fea6957ea23618d37d339ef0e7f7c967fc6" -pretty-format@^21.2.1: - version "21.2.1" - resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36" - dependencies: - ansi-regex "^3.0.0" - ansi-styles "^3.2.0" - pretty-format@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-4.3.1.tgz#530be5c42b3c05b36414a7a2a4337aa80acd0e8d"