From fdd340b4cce6f2a7249bb5546dfbd947a686b57d Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Tue, 18 Sep 2018 19:44:14 +0100 Subject: [PATCH 01/11] Add test.todo to jest-jasmine --- packages/jest-cli/src/constants.js | 1 + packages/jest-cli/src/reporters/utils.js | 2 ++ .../src/reporters/verbose_reporter.js | 2 ++ packages/jest-cli/src/testResultHelpers.js | 6 +++- .../src/__tests__/todo_error.test.js | 29 +++++++++++++++++++ packages/jest-jasmine2/src/index.js | 1 + packages/jest-jasmine2/src/jasmine/Env.js | 19 ++++++++++++ packages/jest-jasmine2/src/jasmine/Spec.js | 15 +++++++++- .../src/jasmine/jasmine_light.js | 4 +++ packages/jest-jasmine2/src/reporter.js | 4 +++ packages/jest-runner/src/run_test.js | 5 +++- types/TestResult.js | 4 ++- 12 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 packages/jest-jasmine2/src/__tests__/todo_error.test.js diff --git a/packages/jest-cli/src/constants.js b/packages/jest-cli/src/constants.js index 67418d211d72..fec5f008415e 100644 --- a/packages/jest-cli/src/constants.js +++ b/packages/jest-cli/src/constants.js @@ -15,6 +15,7 @@ export const ICONS = { failed: isWindows ? '\u00D7' : '\u2715', pending: '\u25CB', success: isWindows ? '\u221A' : '\u2713', + todo: '\u270E', }; export const PACKAGE_JSON = 'package.json'; export const JEST_CONFIG = 'jest.config.js'; diff --git a/packages/jest-cli/src/reporters/utils.js b/packages/jest-cli/src/reporters/utils.js index bdea97306ef9..7f519991892d 100644 --- a/packages/jest-cli/src/reporters/utils.js +++ b/packages/jest-cli/src/reporters/utils.js @@ -122,6 +122,7 @@ export const getSummary = ( const testsFailed = aggregatedResults.numFailedTests; const testsPassed = aggregatedResults.numPassedTests; const testsPending = aggregatedResults.numPendingTests; + const testsTodo = aggregatedResults.numTodoTests; const testsTotal = aggregatedResults.numTotalTests; const width = (options && options.width) || 0; @@ -141,6 +142,7 @@ export const getSummary = ( chalk.bold('Tests: ') + (testsFailed ? chalk.bold.red(`${testsFailed} failed`) + ', ' : '') + (testsPending ? chalk.bold.yellow(`${testsPending} skipped`) + ', ' : '') + + (testsTodo ? chalk.bold.magenta(`${testsTodo} todo`) + ', ' : '') + (testsPassed ? chalk.bold.green(`${testsPassed} passed`) + ', ' : '') + `${testsTotal} total`; diff --git a/packages/jest-cli/src/reporters/verbose_reporter.js b/packages/jest-cli/src/reporters/verbose_reporter.js index 1e86c88b1516..6dc4c8df7927 100644 --- a/packages/jest-cli/src/reporters/verbose_reporter.js +++ b/packages/jest-cli/src/reporters/verbose_reporter.js @@ -97,6 +97,8 @@ export default class VerboseReporter extends DefaultReporter { return chalk.red(ICONS.failed); } else if (status === 'pending') { return chalk.yellow(ICONS.pending); + } else if (status === 'todo') { + return chalk.magenta(ICONS.todo); } else { return chalk.green(ICONS.success); } diff --git a/packages/jest-cli/src/testResultHelpers.js b/packages/jest-cli/src/testResultHelpers.js index ad2c8c0fed47..8c3a97ce07c0 100644 --- a/packages/jest-cli/src/testResultHelpers.js +++ b/packages/jest-cli/src/testResultHelpers.js @@ -21,6 +21,7 @@ export const makeEmptyAggregatedTestResult = (): AggregatedResult => ({ numPendingTestSuites: 0, numPendingTests: 0, numRuntimeErrorTestSuites: 0, + numTodoTests: 0, numTotalTestSuites: 0, numTotalTests: 0, openHandles: [], @@ -57,6 +58,7 @@ export const buildFailureTestResult = ( numFailingTests: 0, numPassingTests: 0, numPendingTests: 0, + numTodoTests: 0, openHandles: [], perfStats: { end: 0, @@ -87,10 +89,12 @@ export const addResult = ( aggregatedResults.numTotalTests += testResult.numPassingTests + testResult.numFailingTests + - testResult.numPendingTests; + testResult.numPendingTests + + testResult.numTodoTests; aggregatedResults.numFailedTests += testResult.numFailingTests; aggregatedResults.numPassedTests += testResult.numPassingTests; aggregatedResults.numPendingTests += testResult.numPendingTests; + aggregatedResults.numTodoTests += testResult.numTodoTests; if (testResult.testExecError) { aggregatedResults.numRuntimeErrorTestSuites++; diff --git a/packages/jest-jasmine2/src/__tests__/todo_error.test.js b/packages/jest-jasmine2/src/__tests__/todo_error.test.js new file mode 100644 index 000000000000..efa924c78a9c --- /dev/null +++ b/packages/jest-jasmine2/src/__tests__/todo_error.test.js @@ -0,0 +1,29 @@ +/** + * 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.todo error throwing', () => { + it('it throws error when given no arguments', () => { + expect(() => { + it.todo(); + }).toThrowError('Todo must be called with only a description.'); + }); + it('it throws error when given more than one argument', () => { + expect(() => { + it.todo('test1', () => {}); + }).toThrowError('Todo must be called with only a description.'); + }); + it('it throws error when given none string description', () => { + expect(() => { + it.todo(() => {}); + }).toThrowError( + 'Invalid first argument: () => {}. Todo must be called with a string.', + ); + }); +}); diff --git a/packages/jest-jasmine2/src/index.js b/packages/jest-jasmine2/src/index.js index ca4e8f824b6b..b93eab79d10c 100644 --- a/packages/jest-jasmine2/src/index.js +++ b/packages/jest-jasmine2/src/index.js @@ -62,6 +62,7 @@ async function jasmine2( environment.global.test = environment.global.it; environment.global.it.only = environment.global.fit; + environment.global.it.todo = env.todo; environment.global.it.skip = environment.global.xit; environment.global.xtest = environment.global.xit; environment.global.describe.skip = environment.global.xdescribe; diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index d547c0a8f271..5bd9ffd84cce 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -328,6 +328,9 @@ export default function(j$) { if (currentDeclarationSuite.markedPending) { suite.pend(); } + if (currentDeclarationSuite.markedTodo) { + suite.todo(); + } addSpecsToSuite(suite, specDefinitions); return suite; }; @@ -489,6 +492,22 @@ export default function(j$) { return spec; }; + this.todo = function() { + if (arguments.length !== 1) { + throw new Error('Todo must be called with only a description.'); + } + const description = arguments[0]; + if (typeof description !== 'string') { + throw new Error( + `Invalid first argument: ${description}. Todo must be called with a string.`, + ); + } + const spec = specFactory(description, () => {}, currentDeclarationSuite); + spec.todo(); + currentDeclarationSuite.addChild(spec); + return spec; + }; + this.fit = function(description, fn, timeout) { const spec = specFactory( description, diff --git a/packages/jest-jasmine2/src/jasmine/Spec.js b/packages/jest-jasmine2/src/jasmine/Spec.js index 807c3c168f75..c7ab00dfe70e 100644 --- a/packages/jest-jasmine2/src/jasmine/Spec.js +++ b/packages/jest-jasmine2/src/jasmine/Spec.js @@ -103,7 +103,12 @@ Spec.prototype.execute = function(onComplete, enabled) { this.onStart(this); - if (!this.isExecutable() || this.markedPending || enabled === false) { + if ( + !this.isExecutable() || + this.markedPending || + this.markedTodo || + enabled === false + ) { complete(enabled); return; } @@ -175,6 +180,10 @@ Spec.prototype.pend = function(message) { } }; +Spec.prototype.todo = function() { + this.markedTodo = true; +}; + Spec.prototype.getResult = function() { this.result.status = this.status(); return this.result; @@ -185,6 +194,10 @@ Spec.prototype.status = function(enabled) { return 'disabled'; } + if (this.markedTodo) { + return 'todo'; + } + if (this.markedPending) { return 'pending'; } diff --git a/packages/jest-jasmine2/src/jasmine/jasmine_light.js b/packages/jest-jasmine2/src/jasmine/jasmine_light.js index f79918edcdeb..520eaf9a2581 100644 --- a/packages/jest-jasmine2/src/jasmine/jasmine_light.js +++ b/packages/jest-jasmine2/src/jasmine/jasmine_light.js @@ -88,6 +88,10 @@ exports.interface = function(jasmine: Jasmine, env: any) { return env.xit.apply(env, arguments); }, + tit(description: string) { + return env.tit(description); + }, + fit() { return env.fit.apply(env, arguments); }, diff --git a/packages/jest-jasmine2/src/reporter.js b/packages/jest-jasmine2/src/reporter.js index ac1e37c67976..2a867736f29a 100644 --- a/packages/jest-jasmine2/src/reporter.js +++ b/packages/jest-jasmine2/src/reporter.js @@ -85,12 +85,15 @@ export default class Jasmine2Reporter { let numFailingTests = 0; let numPassingTests = 0; let numPendingTests = 0; + let numTodoTests = 0; const testResults = this._testResults; testResults.forEach(testResult => { if (testResult.status === 'failed') { numFailingTests++; } else if (testResult.status === 'pending') { numPendingTests++; + } else if (testResult.status === 'todo') { + numTodoTests++; } else { numPassingTests++; } @@ -107,6 +110,7 @@ export default class Jasmine2Reporter { numFailingTests, numPassingTests, numPendingTests, + numTodoTests, perfStats: { end: 0, start: 0, diff --git a/packages/jest-runner/src/run_test.js b/packages/jest-runner/src/run_test.js index 64ed00bb9261..d511c31c52c6 100644 --- a/packages/jest-runner/src/run_test.js +++ b/packages/jest-runner/src/run_test.js @@ -195,7 +195,10 @@ async function runTestInternal( } const testCount = - result.numPassingTests + result.numFailingTests + result.numPendingTests; + result.numPassingTests + + result.numFailingTests + + result.numPendingTests + + result.numTodoTests; result.perfStats = {end: Date.now(), start}; result.testFilePath = path; diff --git a/types/TestResult.js b/types/TestResult.js index b32bd15e9590..928dc0d280e8 100644 --- a/types/TestResult.js +++ b/types/TestResult.js @@ -83,7 +83,7 @@ export type AssertionLocation = {| path: string, |}; -export type Status = 'passed' | 'failed' | 'skipped' | 'pending'; +export type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo'; export type Bytes = number; export type Milliseconds = number; @@ -118,6 +118,7 @@ export type AggregatedResultWithoutCoverage = { numPassedTests: number, numPassedTestSuites: number, numPendingTests: number, + numTodoTests: number, numPendingTestSuites: number, numRuntimeErrorTestSuites: number, numTotalTests: number, @@ -150,6 +151,7 @@ export type TestResult = {| numFailingTests: number, numPassingTests: number, numPendingTests: number, + numTodoTests: number, openHandles: Array, perfStats: {| end: Milliseconds, From 3cbe1c0764c9ee571cf509a73d9398bafa79b617 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Tue, 18 Sep 2018 20:04:45 +0100 Subject: [PATCH 02/11] Add test.todo to jest-circus --- .../__tests__/circus_todo_test_error.test.js | 44 +++++++++++++++++++ packages/jest-circus/src/event_handler.js | 4 ++ packages/jest-circus/src/index.js | 25 +++++++++++ .../jest_adapter_init.js | 5 +++ packages/jest-circus/src/run.js | 5 +++ types/Circus.js | 8 +++- 6 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 packages/jest-circus/src/__tests__/circus_todo_test_error.test.js diff --git a/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js b/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js new file mode 100644 index 000000000000..20e701f07baf --- /dev/null +++ b/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js @@ -0,0 +1,44 @@ +/** + * 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 strict-local + */ + +'use strict'; + +let circusIt; + +// using jest-jasmine2's 'it' to test jest-circus's 'it'. Had to differentiate +// the two with this alias. + +const aliasCircusIt = () => { + const {it} = require('../index.js'); + circusIt = it; +}; + +aliasCircusIt(); + +describe('test/it.todo error throwing', () => { + it('todo throws error when given no arguments', () => { + expect(() => { + // $FlowFixMe: Testing runitme errors here + circusIt.todo(); + }).toThrowError('Todo must be called with only a description.'); + }); + it('todo throws error when given more than one argument', () => { + expect(() => { + circusIt.todo('test1', () => {}); + }).toThrowError('Todo must be called with only a description.'); + }); + it('todo throws error when given none string description', () => { + expect(() => { + // $FlowFixMe: Testing runitme errors here + circusIt.todo(() => {}); + }).toThrowError( + 'Invalid first argument: () => {}. Todo must be called with a string.', + ); + }); +}); diff --git a/packages/jest-circus/src/event_handler.js b/packages/jest-circus/src/event_handler.js index e50e1f56548d..fb5af141b802 100644 --- a/packages/jest-circus/src/event_handler.js +++ b/packages/jest-circus/src/event_handler.js @@ -105,6 +105,10 @@ const handler: EventHandler = (event, state): void => { event.test.status = 'skip'; break; } + case 'test_todo': { + event.test.status = 'todo'; + break; + } case 'test_done': { event.test.duration = getTestDuration(event.test); event.test.status = 'done'; diff --git a/packages/jest-circus/src/index.js b/packages/jest-circus/src/index.js index 790987a43349..71a9dc38b3c5 100644 --- a/packages/jest-circus/src/index.js +++ b/packages/jest-circus/src/index.js @@ -121,6 +121,31 @@ test.only = (testName: TestName, fn: TestFn, timeout?: number) => { }); }; +test.todo = (testName: TestName, ...rest: Array) => { + if (rest.length > 0 || testName === undefined) { + throw new Error('Todo must be called with only a description.'); + } + + if (typeof testName !== 'string') { + throw new Error( + `Invalid first argument: ${testName}. Todo must be called with a string.`, + ); + } + const asyncError = new Error(); + if (Error.captureStackTrace) { + Error.captureStackTrace(asyncError, test); + } + + return dispatch({ + asyncError, + fn: () => {}, + mode: 'todo', + name: 'add_test', + testName, + timeout: undefined, + }); +}; + test.each = bindEach(test); test.only.each = bindEach(test.only); test.skip.each = bindEach(test.skip); diff --git a/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter_init.js b/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter_init.js index 47df43f83778..7f076478e948 100644 --- a/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter_init.js +++ b/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter_init.js @@ -122,12 +122,16 @@ export const runAndTransformResultsToJestFormat = async ({ let numFailingTests = 0; let numPassingTests = 0; let numPendingTests = 0; + let numTodoTests = 0; const assertionResults = runResult.testResults.map(testResult => { let status: Status; if (testResult.status === 'skip') { status = 'pending'; numPendingTests += 1; + } else if (testResult.status === 'todo') { + status = 'todo'; + numTodoTests += 1; } else if (testResult.errors.length) { status = 'failed'; numFailingTests += 1; @@ -184,6 +188,7 @@ export const runAndTransformResultsToJestFormat = async ({ numFailingTests, numPassingTests, numPendingTests, + numTodoTests, openHandles: [], perfStats: { // populated outside diff --git a/packages/jest-circus/src/run.js b/packages/jest-circus/src/run.js index bf6bae4f2683..37fcfc8d1616 100644 --- a/packages/jest-circus/src/run.js +++ b/packages/jest-circus/src/run.js @@ -97,6 +97,11 @@ const _runTest = async (test: TestEntry): Promise => { return; } + if (test.mode === 'todo') { + dispatch({name: 'test_todo', test}); + return; + } + const {afterEach, beforeEach} = getEachHooksForTest(test); for (const hook of beforeEach) { diff --git a/types/Circus.js b/types/Circus.js index 484afbde9656..7a95671f9460 100644 --- a/types/Circus.js +++ b/types/Circus.js @@ -10,7 +10,7 @@ export type DoneFn = (reason?: string | Error) => void; export type BlockFn = () => void; export type BlockName = string | Function; -export type BlockMode = void | 'skip' | 'only'; +export type BlockMode = void | 'skip' | 'only' | 'todo'; export type TestMode = BlockMode; export type TestName = string; export type TestFn = (done?: DoneFn) => ?Promise; @@ -105,6 +105,10 @@ export type Event = name: 'test_skip', test: TestEntry, |} + | {| + name: 'test_todo', + test: TestEntry, + |} | {| // test failure is defined by presence of errors in `test.errors`, // `test_done` indicates that the test and all its hooks were run, @@ -145,7 +149,7 @@ export type Event = name: 'teardown', |}; -export type TestStatus = 'skip' | 'done'; +export type TestStatus = 'skip' | 'done' | 'todo'; export type TestResult = {| duration: ?number, errors: Array, From 392c1d055ece909abc80575cff87c88664b84da5 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Tue, 18 Sep 2018 20:52:58 +0100 Subject: [PATCH 03/11] Add end to end tests --- .../__snapshots__/test-todo.test.js.snap | 82 +++++++++++++++++++ e2e/__tests__/test-todo.test.js | 55 +++++++++++++ e2e/test-todo/__tests__/statuses.test.js | 20 +++++ .../__tests__/todo_multiple_args.test.js | 8 ++ e2e/test-todo/__tests__/todo_no_args.test.js | 8 ++ .../__tests__/todo_non_string.test.js | 8 ++ e2e/test-todo/package.json | 5 ++ 7 files changed, 186 insertions(+) create mode 100644 e2e/__tests__/__snapshots__/test-todo.test.js.snap create mode 100644 e2e/__tests__/test-todo.test.js create mode 100644 e2e/test-todo/__tests__/statuses.test.js create mode 100644 e2e/test-todo/__tests__/todo_multiple_args.test.js create mode 100644 e2e/test-todo/__tests__/todo_no_args.test.js create mode 100644 e2e/test-todo/__tests__/todo_non_string.test.js create mode 100644 e2e/test-todo/package.json diff --git a/e2e/__tests__/__snapshots__/test-todo.test.js.snap b/e2e/__tests__/__snapshots__/test-todo.test.js.snap new file mode 100644 index 000000000000..01fbde0bb801 --- /dev/null +++ b/e2e/__tests__/__snapshots__/test-todo.test.js.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`shows error messages when called with invalid argument 1`] = ` +"FAIL __tests__/todo_non_string.test.js + ● Test suite failed to run + + Invalid first argument: () => {}. Todo must be called with a string. + + 6 | */ + 7 | + > 8 | it.todo(() => {}); + | ^ + 9 | + + at packages/jest-jasmine2/build/jasmine/Env.js:480:15 + at __tests__/todo_non_string.test.js:8:4 + +" +`; + +exports[`shows error messages when called with multiple arguments 1`] = ` +"FAIL __tests__/todo_multiple_args.test.js + ● Test suite failed to run + + Todo must be called with only a description. + + 6 | */ + 7 | + > 8 | it.todo('todo later', () => {}); + | ^ + 9 | + + at packages/jest-jasmine2/build/jasmine/Env.js:476:15 + at __tests__/todo_multiple_args.test.js:8:4 + +" +`; + +exports[`shows error messages when called with no arguments 1`] = ` +"FAIL __tests__/todo_no_args.test.js + ● Test suite failed to run + + Todo must be called with only a description. + + 6 | */ + 7 | + > 8 | it.todo(); + | ^ + 9 | + + at packages/jest-jasmine2/build/jasmine/Env.js:476:15 + at __tests__/todo_no_args.test.js:8:4 + +" +`; + +exports[`works with all statuses 1`] = ` +"FAIL __tests__/statuses.test.js + ✓ passes + ✕ fails + ✎ todo + ○ skipped 1 test + + ● fails + + expect(received).toBe(expected) // Object.is equality + + Expected: 101 + Received: 10 + + 11 | + 12 | it('fails', () => { + > 13 | expect(10).toBe(101); + | ^ + 14 | }); + 15 | + 16 | it.skip('skips', () => { + + at __tests__/statuses.test.js:13:14 + +" +`; diff --git a/e2e/__tests__/test-todo.test.js b/e2e/__tests__/test-todo.test.js new file mode 100644 index 000000000000..3975d7da3576 --- /dev/null +++ b/e2e/__tests__/test-todo.test.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2018-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'; + +const path = require('path'); +const runJest = require('../runJest'); +const {extractSummary} = require('../Utils'); +const dir = path.resolve(__dirname, '../test-todo'); + +test('works with all statuses', () => { + const result = runJest(dir, ['statuses.test.js']); + expect(result.status).toBe(1); + const output = extractSummary(result.stderr) + .rest.split('\n') + .map(line => line.trimRight()) + .join('\n'); + expect(output).toMatchSnapshot(); +}); + +test('shows error messages when called with no arguments', () => { + const result = runJest(dir, ['todo_no_args.test.js']); + expect(result.status).toBe(1); + const output = extractSummary(result.stderr) + .rest.split('\n') + .map(line => line.trimRight()) + .join('\n'); + expect(output).toMatchSnapshot(); +}); + +test('shows error messages when called with multiple arguments', () => { + const result = runJest(dir, ['todo_multiple_args.test.js']); + expect(result.status).toBe(1); + const output = extractSummary(result.stderr) + .rest.split('\n') + .map(line => line.trimRight()) + .join('\n'); + expect(output).toMatchSnapshot(); +}); + +test('shows error messages when called with invalid argument', () => { + const result = runJest(dir, ['todo_non_string.test.js']); + expect(result.status).toBe(1); + const output = extractSummary(result.stderr) + .rest.split('\n') + .map(line => line.trimRight()) + .join('\n'); + expect(output).toMatchSnapshot(); +}); diff --git a/e2e/test-todo/__tests__/statuses.test.js b/e2e/test-todo/__tests__/statuses.test.js new file mode 100644 index 000000000000..4d9034ee7080 --- /dev/null +++ b/e2e/test-todo/__tests__/statuses.test.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2018-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. + */ + +it('passes', () => { + expect(10).toBe(10); +}); + +it('fails', () => { + expect(10).toBe(101); +}); + +it.skip('skips', () => { + expect(10).toBe(101); +}); + +it.todo('todo'); diff --git a/e2e/test-todo/__tests__/todo_multiple_args.test.js b/e2e/test-todo/__tests__/todo_multiple_args.test.js new file mode 100644 index 000000000000..e0b852c31d88 --- /dev/null +++ b/e2e/test-todo/__tests__/todo_multiple_args.test.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2018-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. + */ + +it.todo('todo later', () => {}); diff --git a/e2e/test-todo/__tests__/todo_no_args.test.js b/e2e/test-todo/__tests__/todo_no_args.test.js new file mode 100644 index 000000000000..d82b8e902c53 --- /dev/null +++ b/e2e/test-todo/__tests__/todo_no_args.test.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2018-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. + */ + +it.todo(); diff --git a/e2e/test-todo/__tests__/todo_non_string.test.js b/e2e/test-todo/__tests__/todo_non_string.test.js new file mode 100644 index 000000000000..5c3c9d766a9d --- /dev/null +++ b/e2e/test-todo/__tests__/todo_non_string.test.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2018-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. + */ + +it.todo(() => {}); diff --git a/e2e/test-todo/package.json b/e2e/test-todo/package.json new file mode 100644 index 000000000000..148788b25446 --- /dev/null +++ b/e2e/test-todo/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "node" + } +} From ec08b6f9c20c8fd1e71dd4a64b89a6e30f6911e3 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Tue, 18 Sep 2018 21:54:15 +0100 Subject: [PATCH 04/11] Remove unused interface --- packages/jest-jasmine2/src/jasmine/jasmine_light.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/jest-jasmine2/src/jasmine/jasmine_light.js b/packages/jest-jasmine2/src/jasmine/jasmine_light.js index 520eaf9a2581..f79918edcdeb 100644 --- a/packages/jest-jasmine2/src/jasmine/jasmine_light.js +++ b/packages/jest-jasmine2/src/jasmine/jasmine_light.js @@ -88,10 +88,6 @@ exports.interface = function(jasmine: Jasmine, env: any) { return env.xit.apply(env, arguments); }, - tit(description: string) { - return env.tit(description); - }, - fit() { return env.fit.apply(env, arguments); }, From 663ed551df373f0f7d8dd01896c5d39efaf0e264 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Tue, 18 Sep 2018 22:56:47 +0100 Subject: [PATCH 05/11] Group todo tests into one log per suite --- .../src/reporters/verbose_reporter.js | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/jest-cli/src/reporters/verbose_reporter.js b/packages/jest-cli/src/reporters/verbose_reporter.js index 6dc4c8df7927..9883c1844049 100644 --- a/packages/jest-cli/src/reporters/verbose_reporter.js +++ b/packages/jest-cli/src/reporters/verbose_reporter.js @@ -114,26 +114,48 @@ export default class VerboseReporter extends DefaultReporter { if (this._globalConfig.expand) { tests.forEach(test => this._logTest(test, indentLevel)); } else { - const skippedCount = tests.reduce((result, test) => { - if (test.status === 'pending') { - result += 1; - } else { - this._logTest(test, indentLevel); - } + const summedTests = tests.reduce( + (result, test) => { + if (test.status === 'pending') { + result.pending += 1; + } else if (test.status === 'todo') { + result.todo += 1; + } else { + this._logTest(test, indentLevel); + } + + return result; + }, + {pending: 0, todo: 0}, + ); - return result; - }, 0); + if (summedTests.pending > 0) { + this._logSummedTests( + 'skipped', + this._getIcon('pending'), + summedTests.pending, + indentLevel, + ); + } - if (skippedCount > 0) { - this._logSkippedTests(skippedCount, indentLevel); + if (summedTests.todo > 0) { + this._logSummedTests( + 'todo', + this._getIcon('todo'), + summedTests.todo, + indentLevel, + ); } } } - _logSkippedTests(count: number, indentLevel: number) { - const icon = this._getIcon('pending'); - const text = chalk.dim(`skipped ${count} test${count === 1 ? '' : 's'}`); - + _logSummedTests( + prefix: string, + icon: string, + count: number, + indentLevel: number, + ) { + const text = chalk.dim(`${prefix} ${count} test${count === 1 ? '' : 's'}`); this._logLine(`${icon} ${text}`, indentLevel); } From a720cde9ef9731a9c5e855bea4070da3d76c79a0 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Tue, 18 Sep 2018 23:08:03 +0100 Subject: [PATCH 06/11] Remove stringifying of invalid arg --- e2e/__tests__/__snapshots__/test-todo.test.js.snap | 10 +++++----- .../src/__tests__/circus_todo_test_error.test.js | 4 +--- packages/jest-circus/src/index.js | 7 +------ .../jest-jasmine2/src/__tests__/todo_error.test.js | 4 +--- packages/jest-jasmine2/src/jasmine/Env.js | 9 ++------- 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/e2e/__tests__/__snapshots__/test-todo.test.js.snap b/e2e/__tests__/__snapshots__/test-todo.test.js.snap index 01fbde0bb801..0691269625aa 100644 --- a/e2e/__tests__/__snapshots__/test-todo.test.js.snap +++ b/e2e/__tests__/__snapshots__/test-todo.test.js.snap @@ -4,7 +4,7 @@ exports[`shows error messages when called with invalid argument 1`] = ` "FAIL __tests__/todo_non_string.test.js ● Test suite failed to run - Invalid first argument: () => {}. Todo must be called with a string. + Todo must be called with only a description. 6 | */ 7 | @@ -12,7 +12,7 @@ exports[`shows error messages when called with invalid argument 1`] = ` | ^ 9 | - at packages/jest-jasmine2/build/jasmine/Env.js:480:15 + at packages/jest-jasmine2/build/jasmine/Env.js:477:15 at __tests__/todo_non_string.test.js:8:4 " @@ -30,7 +30,7 @@ exports[`shows error messages when called with multiple arguments 1`] = ` | ^ 9 | - at packages/jest-jasmine2/build/jasmine/Env.js:476:15 + at packages/jest-jasmine2/build/jasmine/Env.js:477:15 at __tests__/todo_multiple_args.test.js:8:4 " @@ -48,7 +48,7 @@ exports[`shows error messages when called with no arguments 1`] = ` | ^ 9 | - at packages/jest-jasmine2/build/jasmine/Env.js:476:15 + at packages/jest-jasmine2/build/jasmine/Env.js:477:15 at __tests__/todo_no_args.test.js:8:4 " @@ -58,8 +58,8 @@ exports[`works with all statuses 1`] = ` "FAIL __tests__/statuses.test.js ✓ passes ✕ fails - ✎ todo ○ skipped 1 test + ✎ todo 1 test ● fails diff --git a/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js b/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js index 20e701f07baf..1564ce029559 100644 --- a/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js +++ b/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js @@ -37,8 +37,6 @@ describe('test/it.todo error throwing', () => { expect(() => { // $FlowFixMe: Testing runitme errors here circusIt.todo(() => {}); - }).toThrowError( - 'Invalid first argument: () => {}. Todo must be called with a string.', - ); + }).toThrowError('Todo must be called with only a description.'); }); }); diff --git a/packages/jest-circus/src/index.js b/packages/jest-circus/src/index.js index 71a9dc38b3c5..4cf7946e032b 100644 --- a/packages/jest-circus/src/index.js +++ b/packages/jest-circus/src/index.js @@ -122,15 +122,10 @@ test.only = (testName: TestName, fn: TestFn, timeout?: number) => { }; test.todo = (testName: TestName, ...rest: Array) => { - if (rest.length > 0 || testName === undefined) { + if (rest.length > 0 || typeof testName !== 'string') { throw new Error('Todo must be called with only a description.'); } - if (typeof testName !== 'string') { - throw new Error( - `Invalid first argument: ${testName}. Todo must be called with a string.`, - ); - } const asyncError = new Error(); if (Error.captureStackTrace) { Error.captureStackTrace(asyncError, test); diff --git a/packages/jest-jasmine2/src/__tests__/todo_error.test.js b/packages/jest-jasmine2/src/__tests__/todo_error.test.js index efa924c78a9c..7d195e75f4a7 100644 --- a/packages/jest-jasmine2/src/__tests__/todo_error.test.js +++ b/packages/jest-jasmine2/src/__tests__/todo_error.test.js @@ -22,8 +22,6 @@ describe('test/it.todo error throwing', () => { it('it throws error when given none string description', () => { expect(() => { it.todo(() => {}); - }).toThrowError( - 'Invalid first argument: () => {}. Todo must be called with a string.', - ); + }).toThrowError('Todo must be called with only a description.'); }); }); diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index 5bd9ffd84cce..a4d8177552dc 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -493,14 +493,9 @@ export default function(j$) { }; this.todo = function() { - if (arguments.length !== 1) { - throw new Error('Todo must be called with only a description.'); - } const description = arguments[0]; - if (typeof description !== 'string') { - throw new Error( - `Invalid first argument: ${description}. Todo must be called with a string.`, - ); + if (arguments.length !== 1 || typeof description !== 'string') { + throw new Error('Todo must be called with only a description.'); } const spec = specFactory(description, () => {}, currentDeclarationSuite); spec.todo(); From 1a1725259b240f6c5ee482db8e88a5e95e84f6f9 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Wed, 19 Sep 2018 17:20:48 +0100 Subject: [PATCH 07/11] Add capture stack trace to invalid args error --- e2e/__tests__/__snapshots__/test-todo.test.js.snap | 3 --- packages/jest-circus/src/index.js | 8 +++++++- packages/jest-jasmine2/src/jasmine/Env.js | 9 ++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/e2e/__tests__/__snapshots__/test-todo.test.js.snap b/e2e/__tests__/__snapshots__/test-todo.test.js.snap index 0691269625aa..010062cd1f54 100644 --- a/e2e/__tests__/__snapshots__/test-todo.test.js.snap +++ b/e2e/__tests__/__snapshots__/test-todo.test.js.snap @@ -12,7 +12,6 @@ exports[`shows error messages when called with invalid argument 1`] = ` | ^ 9 | - at packages/jest-jasmine2/build/jasmine/Env.js:477:15 at __tests__/todo_non_string.test.js:8:4 " @@ -30,7 +29,6 @@ exports[`shows error messages when called with multiple arguments 1`] = ` | ^ 9 | - at packages/jest-jasmine2/build/jasmine/Env.js:477:15 at __tests__/todo_multiple_args.test.js:8:4 " @@ -48,7 +46,6 @@ exports[`shows error messages when called with no arguments 1`] = ` | ^ 9 | - at packages/jest-jasmine2/build/jasmine/Env.js:477:15 at __tests__/todo_no_args.test.js:8:4 " diff --git a/packages/jest-circus/src/index.js b/packages/jest-circus/src/index.js index 4cf7946e032b..02e173743878 100644 --- a/packages/jest-circus/src/index.js +++ b/packages/jest-circus/src/index.js @@ -123,7 +123,13 @@ test.only = (testName: TestName, fn: TestFn, timeout?: number) => { test.todo = (testName: TestName, ...rest: Array) => { if (rest.length > 0 || typeof testName !== 'string') { - throw new Error('Todo must be called with only a description.'); + const e = new Error('Todo must be called with only a description.'); + + if (Error.captureStackTrace) { + Error.captureStackTrace(e, test.todo); + } + + throw e; } const asyncError = new Error(); diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index a4d8177552dc..86c67fbe1220 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -495,8 +495,15 @@ export default function(j$) { this.todo = function() { const description = arguments[0]; if (arguments.length !== 1 || typeof description !== 'string') { - throw new Error('Todo must be called with only a description.'); + const e = new Error('Todo must be called with only a description.'); + + if (Error.captureStackTrace) { + Error.captureStackTrace(e, test.todo); + } + + throw e; } + const spec = specFactory(description, () => {}, currentDeclarationSuite); spec.todo(); currentDeclarationSuite.addChild(spec); From 54fac6da794f029898e23625d5881edfbe7d58e6 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Wed, 19 Sep 2018 17:26:37 +0100 Subject: [PATCH 08/11] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d7fbd43a29..6332c3821f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Features +- `[jest-jasmine2/jest-circus/jest-cli]` Add test.todo ([#6996](https://github.com/facebook/jest/pull/6996)) - `[pretty-format]` Option to not escape strings in diff messages ([#5661](https://github.com/facebook/jest/pull/5661)) ### Fixes From 8891c3652d7097a4077b6bd0dd6778c50d3ac173 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Wed, 19 Sep 2018 17:47:47 +0100 Subject: [PATCH 09/11] Remove unnessecary snapshot normalisation --- .../__snapshots__/test-todo.test.js.snap | 16 +++++------ e2e/__tests__/test-todo.test.js | 28 ++++++------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/e2e/__tests__/__snapshots__/test-todo.test.js.snap b/e2e/__tests__/__snapshots__/test-todo.test.js.snap index 010062cd1f54..bd51088a7639 100644 --- a/e2e/__tests__/__snapshots__/test-todo.test.js.snap +++ b/e2e/__tests__/__snapshots__/test-todo.test.js.snap @@ -7,10 +7,10 @@ exports[`shows error messages when called with invalid argument 1`] = ` Todo must be called with only a description. 6 | */ - 7 | + 7 | > 8 | it.todo(() => {}); | ^ - 9 | + 9 | at __tests__/todo_non_string.test.js:8:4 @@ -24,10 +24,10 @@ exports[`shows error messages when called with multiple arguments 1`] = ` Todo must be called with only a description. 6 | */ - 7 | + 7 | > 8 | it.todo('todo later', () => {}); | ^ - 9 | + 9 | at __tests__/todo_multiple_args.test.js:8:4 @@ -41,10 +41,10 @@ exports[`shows error messages when called with no arguments 1`] = ` Todo must be called with only a description. 6 | */ - 7 | + 7 | > 8 | it.todo(); | ^ - 9 | + 9 | at __tests__/todo_no_args.test.js:8:4 @@ -65,12 +65,12 @@ exports[`works with all statuses 1`] = ` Expected: 101 Received: 10 - 11 | + 11 | 12 | it('fails', () => { > 13 | expect(10).toBe(101); | ^ 14 | }); - 15 | + 15 | 16 | it.skip('skips', () => { at __tests__/statuses.test.js:13:14 diff --git a/e2e/__tests__/test-todo.test.js b/e2e/__tests__/test-todo.test.js index 3975d7da3576..c430731a8f62 100644 --- a/e2e/__tests__/test-todo.test.js +++ b/e2e/__tests__/test-todo.test.js @@ -17,39 +17,27 @@ const dir = path.resolve(__dirname, '../test-todo'); test('works with all statuses', () => { const result = runJest(dir, ['statuses.test.js']); expect(result.status).toBe(1); - const output = extractSummary(result.stderr) - .rest.split('\n') - .map(line => line.trimRight()) - .join('\n'); - expect(output).toMatchSnapshot(); + const {rest} = extractSummary(result.stderr); + expect(rest).toMatchSnapshot(); }); test('shows error messages when called with no arguments', () => { const result = runJest(dir, ['todo_no_args.test.js']); expect(result.status).toBe(1); - const output = extractSummary(result.stderr) - .rest.split('\n') - .map(line => line.trimRight()) - .join('\n'); - expect(output).toMatchSnapshot(); + const {rest} = extractSummary(result.stderr); + expect(rest).toMatchSnapshot(); }); test('shows error messages when called with multiple arguments', () => { const result = runJest(dir, ['todo_multiple_args.test.js']); expect(result.status).toBe(1); - const output = extractSummary(result.stderr) - .rest.split('\n') - .map(line => line.trimRight()) - .join('\n'); - expect(output).toMatchSnapshot(); + const {rest} = extractSummary(result.stderr); + expect(rest).toMatchSnapshot(); }); test('shows error messages when called with invalid argument', () => { const result = runJest(dir, ['todo_non_string.test.js']); expect(result.status).toBe(1); - const output = extractSummary(result.stderr) - .rest.split('\n') - .map(line => line.trimRight()) - .join('\n'); - expect(output).toMatchSnapshot(); + const {rest} = extractSummary(result.stderr); + expect(rest).toMatchSnapshot(); }); From 0e3fb84cbeb73b2edc7e32ab9a2a65598b691903 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Wed, 19 Sep 2018 17:57:00 +0100 Subject: [PATCH 10/11] Add docs --- docs/GlobalAPI.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/GlobalAPI.md b/docs/GlobalAPI.md index 1cfdfc642b56..0eb4f2be26d2 100644 --- a/docs/GlobalAPI.md +++ b/docs/GlobalAPI.md @@ -655,3 +655,21 @@ test('will be ran', () => { expect(1 / 0).toBe(Infinity); }); ``` + +### `test.todo(name)` + +Use `test.todo` when you are planning on writing tests. These tests will be highlighted in the summary output at the end so you know how many tests you still need todo. + +_Note_: If you supply a test callback function then the `test.todo` will throw an error. If you have already implemented the test and it is broken and you do not want it to run, then use `test.skip` instead. + +#### API + +- `name`: `String` the title of the test plan. + +Example: + +```js +const add = (a, b) => a + b; + +test.todo('add should be associative'); +``` From 7325d16e9e981c69f4bf8688a78de2a85e818b64 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Sat, 29 Sep 2018 12:41:15 +0100 Subject: [PATCH 11/11] Add use test.todo suggestion to tests without callback function --- e2e/__tests__/__snapshots__/globals.test.js.snap | 4 ++-- .../src/__tests__/circus_it_test_error.test.js | 8 ++++++-- packages/jest-circus/src/index.js | 4 +++- .../jest-jasmine2/src/__tests__/it_test_error.test.js | 8 ++++++-- packages/jest-jasmine2/src/jasmine/Env.js | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/e2e/__tests__/__snapshots__/globals.test.js.snap b/e2e/__tests__/__snapshots__/globals.test.js.snap index 4e61b01522ec..bb31e4b13844 100644 --- a/e2e/__tests__/__snapshots__/globals.test.js.snap +++ b/e2e/__tests__/__snapshots__/globals.test.js.snap @@ -23,7 +23,7 @@ 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. + Missing second argument. It must be a callback function. Perhaps you want to use \`test.todo\` for a test placeholder. 1 | 2 | it('it', () => {}); @@ -50,7 +50,7 @@ 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. + Missing second argument. It must be a callback function. Perhaps you want to use \`test.todo\` for a test placeholder. 1 | 2 | it('it', () => {}); 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 index 4fd4e2d99f27..4af5597c40a1 100644 --- a/packages/jest-circus/src/__tests__/circus_it_test_error.test.js +++ b/packages/jest-circus/src/__tests__/circus_it_test_error.test.js @@ -37,7 +37,9 @@ describe('test/it error throwing', () => { expect(() => { // $FlowFixMe: Easy, we're testing runitme errors here circusIt('test2'); - }).toThrowError('Missing second argument. It must be a callback function.'); + }).toThrowError( + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', + ); }); it(`it throws an error when first argument isn't a string`, () => { expect(() => { @@ -62,7 +64,9 @@ describe('test/it error throwing', () => { expect(() => { // $FlowFixMe: Easy, we're testing runitme errors here circusTest('test6'); - }).toThrowError('Missing second argument. It must be a callback function.'); + }).toThrowError( + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', + ); }); it(`test throws an error when first argument isn't a string`, () => { expect(() => { diff --git a/packages/jest-circus/src/index.js b/packages/jest-circus/src/index.js index 02e173743878..c6463ce0cc1e 100644 --- a/packages/jest-circus/src/index.js +++ b/packages/jest-circus/src/index.js @@ -68,7 +68,9 @@ const test = (testName: TestName, fn: TestFn, timeout?: number) => { ); } if (fn === undefined) { - throw new Error('Missing second argument. It must be a callback function.'); + throw new Error( + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', + ); } if (typeof fn !== 'function') { throw new Error( diff --git a/packages/jest-jasmine2/src/__tests__/it_test_error.test.js b/packages/jest-jasmine2/src/__tests__/it_test_error.test.js index 9f1727645ffd..54189045b767 100644 --- a/packages/jest-jasmine2/src/__tests__/it_test_error.test.js +++ b/packages/jest-jasmine2/src/__tests__/it_test_error.test.js @@ -12,7 +12,9 @@ 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.'); + }).toThrowError( + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', + ); }); it(`it throws an error when first argument isn't a string`, () => { expect(() => { @@ -29,7 +31,9 @@ describe('test/it error throwing', () => { test(`test throws error with missing callback function`, () => { expect(() => { test('test4'); - }).toThrowError('Missing second argument. It must be a callback function.'); + }).toThrowError( + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', + ); }); test(`test throws an error when first argument isn't a string`, () => { expect(() => { diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index 86c67fbe1220..e464f0713669 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -453,7 +453,7 @@ export default function(j$) { } if (fn === undefined) { throw new Error( - 'Missing second argument. It must be a callback function.', + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', ); } if (typeof fn !== 'function') {