Add test.todo (#6996)

This commit is contained in:
Matt Phillips 2018-09-29 13:07:57 +01:00 committed by Simen Bekkhus
parent 15b43d6a27
commit 49b238046f
29 changed files with 416 additions and 28 deletions

View File

@ -2,6 +2,7 @@
### Features ### 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)) - `[pretty-format]` Option to not escape strings in diff messages ([#5661](https://github.com/facebook/jest/pull/5661))
- `[jest-haste-map]` Add `getFileIterator` to `HasteFS` for faster file iteration ([#7010](https://github.com/facebook/jest/pull/7010)). - `[jest-haste-map]` Add `getFileIterator` to `HasteFS` for faster file iteration ([#7010](https://github.com/facebook/jest/pull/7010)).
- `[jest-worker]` [**BREAKING**] Add functionality to call a `setup` method in the worker before the first call and a `teardown` method when ending the farm ([#7014](https://github.com/facebook/jest/pull/7014)). - `[jest-worker]` [**BREAKING**] Add functionality to call a `setup` method in the worker before the first call and a `teardown` method when ending the farm ([#7014](https://github.com/facebook/jest/pull/7014)).

View File

@ -655,3 +655,21 @@ test('will be ran', () => {
expect(1 / 0).toBe(Infinity); 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');
```

View File

@ -23,7 +23,7 @@ exports[`cannot test with no implementation 1`] = `
"FAIL __tests__/only-constructs.test.js "FAIL __tests__/only-constructs.test.js
● Test suite failed to run ● 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 | 1 |
2 | it('it', () => {}); 2 | it('it', () => {});
@ -50,7 +50,7 @@ exports[`cannot test with no implementation with expand arg 1`] = `
"FAIL __tests__/only-constructs.test.js "FAIL __tests__/only-constructs.test.js
● Test suite failed to run ● 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 | 1 |
2 | it('it', () => {}); 2 | it('it', () => {});

View File

@ -0,0 +1,79 @@
// 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
Todo must be called with only a description.
6 | */
7 |
> 8 | it.todo(() => {});
| ^
9 |
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 __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 __tests__/todo_no_args.test.js:8:4
"
`;
exports[`works with all statuses 1`] = `
"FAIL __tests__/statuses.test.js
✓ passes
✕ fails
○ skipped 1 test
✎ todo 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
"
`;

View File

@ -0,0 +1,43 @@
/**
* 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 {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 {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 {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 {rest} = extractSummary(result.stderr);
expect(rest).toMatchSnapshot();
});

View File

@ -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');

View File

@ -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', () => {});

View File

@ -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();

View File

@ -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(() => {});

View File

@ -0,0 +1,5 @@
{
"jest": {
"testEnvironment": "node"
}
}

View File

@ -37,7 +37,9 @@ describe('test/it error throwing', () => {
expect(() => { expect(() => {
// $FlowFixMe: Easy, we're testing runitme errors here // $FlowFixMe: Easy, we're testing runitme errors here
circusIt('test2'); 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`, () => { it(`it throws an error when first argument isn't a string`, () => {
expect(() => { expect(() => {
@ -62,7 +64,9 @@ describe('test/it error throwing', () => {
expect(() => { expect(() => {
// $FlowFixMe: Easy, we're testing runitme errors here // $FlowFixMe: Easy, we're testing runitme errors here
circusTest('test6'); 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`, () => { it(`test throws an error when first argument isn't a string`, () => {
expect(() => { expect(() => {

View File

@ -0,0 +1,42 @@
/**
* 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('Todo must be called with only a description.');
});
});

View File

@ -105,6 +105,10 @@ const handler: EventHandler = (event, state): void => {
event.test.status = 'skip'; event.test.status = 'skip';
break; break;
} }
case 'test_todo': {
event.test.status = 'todo';
break;
}
case 'test_done': { case 'test_done': {
event.test.duration = getTestDuration(event.test); event.test.duration = getTestDuration(event.test);
event.test.status = 'done'; event.test.status = 'done';

View File

@ -68,7 +68,9 @@ const test = (testName: TestName, fn: TestFn, timeout?: number) => {
); );
} }
if (fn === undefined) { 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') { if (typeof fn !== 'function') {
throw new Error( throw new Error(
@ -121,6 +123,32 @@ test.only = (testName: TestName, fn: TestFn, timeout?: number) => {
}); });
}; };
test.todo = (testName: TestName, ...rest: Array<mixed>) => {
if (rest.length > 0 || typeof testName !== 'string') {
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();
if (Error.captureStackTrace) {
Error.captureStackTrace(asyncError, test);
}
return dispatch({
asyncError,
fn: () => {},
mode: 'todo',
name: 'add_test',
testName,
timeout: undefined,
});
};
test.each = bindEach(test); test.each = bindEach(test);
test.only.each = bindEach(test.only); test.only.each = bindEach(test.only);
test.skip.each = bindEach(test.skip); test.skip.each = bindEach(test.skip);

View File

@ -128,12 +128,16 @@ export const runAndTransformResultsToJestFormat = async ({
let numFailingTests = 0; let numFailingTests = 0;
let numPassingTests = 0; let numPassingTests = 0;
let numPendingTests = 0; let numPendingTests = 0;
let numTodoTests = 0;
const assertionResults = runResult.testResults.map(testResult => { const assertionResults = runResult.testResults.map(testResult => {
let status: Status; let status: Status;
if (testResult.status === 'skip') { if (testResult.status === 'skip') {
status = 'pending'; status = 'pending';
numPendingTests += 1; numPendingTests += 1;
} else if (testResult.status === 'todo') {
status = 'todo';
numTodoTests += 1;
} else if (testResult.errors.length) { } else if (testResult.errors.length) {
status = 'failed'; status = 'failed';
numFailingTests += 1; numFailingTests += 1;
@ -190,6 +194,7 @@ export const runAndTransformResultsToJestFormat = async ({
numFailingTests, numFailingTests,
numPassingTests, numPassingTests,
numPendingTests, numPendingTests,
numTodoTests,
openHandles: [], openHandles: [],
perfStats: { perfStats: {
// populated outside // populated outside

View File

@ -97,6 +97,11 @@ const _runTest = async (test: TestEntry): Promise<void> => {
return; return;
} }
if (test.mode === 'todo') {
dispatch({name: 'test_todo', test});
return;
}
const {afterEach, beforeEach} = getEachHooksForTest(test); const {afterEach, beforeEach} = getEachHooksForTest(test);
for (const hook of beforeEach) { for (const hook of beforeEach) {

View File

@ -15,6 +15,7 @@ export const ICONS = {
failed: isWindows ? '\u00D7' : '\u2715', failed: isWindows ? '\u00D7' : '\u2715',
pending: '\u25CB', pending: '\u25CB',
success: isWindows ? '\u221A' : '\u2713', success: isWindows ? '\u221A' : '\u2713',
todo: '\u270E',
}; };
export const PACKAGE_JSON = 'package.json'; export const PACKAGE_JSON = 'package.json';
export const JEST_CONFIG = 'jest.config.js'; export const JEST_CONFIG = 'jest.config.js';

View File

@ -122,6 +122,7 @@ export const getSummary = (
const testsFailed = aggregatedResults.numFailedTests; const testsFailed = aggregatedResults.numFailedTests;
const testsPassed = aggregatedResults.numPassedTests; const testsPassed = aggregatedResults.numPassedTests;
const testsPending = aggregatedResults.numPendingTests; const testsPending = aggregatedResults.numPendingTests;
const testsTodo = aggregatedResults.numTodoTests;
const testsTotal = aggregatedResults.numTotalTests; const testsTotal = aggregatedResults.numTotalTests;
const width = (options && options.width) || 0; const width = (options && options.width) || 0;
@ -141,6 +142,7 @@ export const getSummary = (
chalk.bold('Tests: ') + chalk.bold('Tests: ') +
(testsFailed ? chalk.bold.red(`${testsFailed} failed`) + ', ' : '') + (testsFailed ? chalk.bold.red(`${testsFailed} failed`) + ', ' : '') +
(testsPending ? chalk.bold.yellow(`${testsPending} skipped`) + ', ' : '') + (testsPending ? chalk.bold.yellow(`${testsPending} skipped`) + ', ' : '') +
(testsTodo ? chalk.bold.magenta(`${testsTodo} todo`) + ', ' : '') +
(testsPassed ? chalk.bold.green(`${testsPassed} passed`) + ', ' : '') + (testsPassed ? chalk.bold.green(`${testsPassed} passed`) + ', ' : '') +
`${testsTotal} total`; `${testsTotal} total`;

View File

@ -97,6 +97,8 @@ export default class VerboseReporter extends DefaultReporter {
return chalk.red(ICONS.failed); return chalk.red(ICONS.failed);
} else if (status === 'pending') { } else if (status === 'pending') {
return chalk.yellow(ICONS.pending); return chalk.yellow(ICONS.pending);
} else if (status === 'todo') {
return chalk.magenta(ICONS.todo);
} else { } else {
return chalk.green(ICONS.success); return chalk.green(ICONS.success);
} }
@ -112,26 +114,48 @@ export default class VerboseReporter extends DefaultReporter {
if (this._globalConfig.expand) { if (this._globalConfig.expand) {
tests.forEach(test => this._logTest(test, indentLevel)); tests.forEach(test => this._logTest(test, indentLevel));
} else { } else {
const skippedCount = tests.reduce((result, test) => { const summedTests = tests.reduce(
if (test.status === 'pending') { (result, test) => {
result += 1; if (test.status === 'pending') {
} else { result.pending += 1;
this._logTest(test, indentLevel); } else if (test.status === 'todo') {
} result.todo += 1;
} else {
this._logTest(test, indentLevel);
}
return result; return result;
}, 0); },
{pending: 0, todo: 0},
);
if (skippedCount > 0) { if (summedTests.pending > 0) {
this._logSkippedTests(skippedCount, indentLevel); this._logSummedTests(
'skipped',
this._getIcon('pending'),
summedTests.pending,
indentLevel,
);
}
if (summedTests.todo > 0) {
this._logSummedTests(
'todo',
this._getIcon('todo'),
summedTests.todo,
indentLevel,
);
} }
} }
} }
_logSkippedTests(count: number, indentLevel: number) { _logSummedTests(
const icon = this._getIcon('pending'); prefix: string,
const text = chalk.dim(`skipped ${count} test${count === 1 ? '' : 's'}`); icon: string,
count: number,
indentLevel: number,
) {
const text = chalk.dim(`${prefix} ${count} test${count === 1 ? '' : 's'}`);
this._logLine(`${icon} ${text}`, indentLevel); this._logLine(`${icon} ${text}`, indentLevel);
} }

View File

@ -21,6 +21,7 @@ export const makeEmptyAggregatedTestResult = (): AggregatedResult => ({
numPendingTestSuites: 0, numPendingTestSuites: 0,
numPendingTests: 0, numPendingTests: 0,
numRuntimeErrorTestSuites: 0, numRuntimeErrorTestSuites: 0,
numTodoTests: 0,
numTotalTestSuites: 0, numTotalTestSuites: 0,
numTotalTests: 0, numTotalTests: 0,
openHandles: [], openHandles: [],
@ -57,6 +58,7 @@ export const buildFailureTestResult = (
numFailingTests: 0, numFailingTests: 0,
numPassingTests: 0, numPassingTests: 0,
numPendingTests: 0, numPendingTests: 0,
numTodoTests: 0,
openHandles: [], openHandles: [],
perfStats: { perfStats: {
end: 0, end: 0,
@ -87,10 +89,12 @@ export const addResult = (
aggregatedResults.numTotalTests += aggregatedResults.numTotalTests +=
testResult.numPassingTests + testResult.numPassingTests +
testResult.numFailingTests + testResult.numFailingTests +
testResult.numPendingTests; testResult.numPendingTests +
testResult.numTodoTests;
aggregatedResults.numFailedTests += testResult.numFailingTests; aggregatedResults.numFailedTests += testResult.numFailingTests;
aggregatedResults.numPassedTests += testResult.numPassingTests; aggregatedResults.numPassedTests += testResult.numPassingTests;
aggregatedResults.numPendingTests += testResult.numPendingTests; aggregatedResults.numPendingTests += testResult.numPendingTests;
aggregatedResults.numTodoTests += testResult.numTodoTests;
if (testResult.testExecError) { if (testResult.testExecError) {
aggregatedResults.numRuntimeErrorTestSuites++; aggregatedResults.numRuntimeErrorTestSuites++;

View File

@ -12,7 +12,9 @@ describe('test/it error throwing', () => {
it(`it throws error with missing callback function`, () => { it(`it throws error with missing callback function`, () => {
expect(() => { expect(() => {
it('test1'); 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`, () => { it(`it throws an error when first argument isn't a string`, () => {
expect(() => { expect(() => {
@ -29,7 +31,9 @@ describe('test/it error throwing', () => {
test(`test throws error with missing callback function`, () => { test(`test throws error with missing callback function`, () => {
expect(() => { expect(() => {
test('test4'); 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`, () => { test(`test throws an error when first argument isn't a string`, () => {
expect(() => { expect(() => {

View File

@ -0,0 +1,27 @@
/**
* 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('Todo must be called with only a description.');
});
});

View File

@ -62,6 +62,7 @@ async function jasmine2(
environment.global.test = environment.global.it; environment.global.test = environment.global.it;
environment.global.it.only = environment.global.fit; environment.global.it.only = environment.global.fit;
environment.global.it.todo = env.todo;
environment.global.it.skip = environment.global.xit; environment.global.it.skip = environment.global.xit;
environment.global.xtest = environment.global.xit; environment.global.xtest = environment.global.xit;
environment.global.describe.skip = environment.global.xdescribe; environment.global.describe.skip = environment.global.xdescribe;

View File

@ -330,6 +330,9 @@ export default function(j$) {
if (currentDeclarationSuite.markedPending) { if (currentDeclarationSuite.markedPending) {
suite.pend(); suite.pend();
} }
if (currentDeclarationSuite.markedTodo) {
suite.todo();
}
addSpecsToSuite(suite, specDefinitions); addSpecsToSuite(suite, specDefinitions);
return suite; return suite;
}; };
@ -452,7 +455,7 @@ export default function(j$) {
} }
if (fn === undefined) { if (fn === undefined) {
throw new Error( 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') { if (typeof fn !== 'function') {
@ -491,6 +494,24 @@ export default function(j$) {
return spec; return spec;
}; };
this.todo = function() {
const description = arguments[0];
if (arguments.length !== 1 || typeof description !== 'string') {
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);
return spec;
};
this.fit = function(description, fn, timeout) { this.fit = function(description, fn, timeout) {
const spec = specFactory( const spec = specFactory(
description, description,

View File

@ -103,7 +103,12 @@ Spec.prototype.execute = function(onComplete, enabled) {
this.onStart(this); this.onStart(this);
if (!this.isExecutable() || this.markedPending || enabled === false) { if (
!this.isExecutable() ||
this.markedPending ||
this.markedTodo ||
enabled === false
) {
complete(enabled); complete(enabled);
return; return;
} }
@ -175,6 +180,10 @@ Spec.prototype.pend = function(message) {
} }
}; };
Spec.prototype.todo = function() {
this.markedTodo = true;
};
Spec.prototype.getResult = function() { Spec.prototype.getResult = function() {
this.result.status = this.status(); this.result.status = this.status();
return this.result; return this.result;
@ -185,6 +194,10 @@ Spec.prototype.status = function(enabled) {
return 'disabled'; return 'disabled';
} }
if (this.markedTodo) {
return 'todo';
}
if (this.markedPending) { if (this.markedPending) {
return 'pending'; return 'pending';
} }

View File

@ -85,12 +85,15 @@ export default class Jasmine2Reporter {
let numFailingTests = 0; let numFailingTests = 0;
let numPassingTests = 0; let numPassingTests = 0;
let numPendingTests = 0; let numPendingTests = 0;
let numTodoTests = 0;
const testResults = this._testResults; const testResults = this._testResults;
testResults.forEach(testResult => { testResults.forEach(testResult => {
if (testResult.status === 'failed') { if (testResult.status === 'failed') {
numFailingTests++; numFailingTests++;
} else if (testResult.status === 'pending') { } else if (testResult.status === 'pending') {
numPendingTests++; numPendingTests++;
} else if (testResult.status === 'todo') {
numTodoTests++;
} else { } else {
numPassingTests++; numPassingTests++;
} }
@ -107,6 +110,7 @@ export default class Jasmine2Reporter {
numFailingTests, numFailingTests,
numPassingTests, numPassingTests,
numPendingTests, numPendingTests,
numTodoTests,
perfStats: { perfStats: {
end: 0, end: 0,
start: 0, start: 0,

View File

@ -195,7 +195,10 @@ async function runTestInternal(
} }
const testCount = const testCount =
result.numPassingTests + result.numFailingTests + result.numPendingTests; result.numPassingTests +
result.numFailingTests +
result.numPendingTests +
result.numTodoTests;
result.perfStats = {end: Date.now(), start}; result.perfStats = {end: Date.now(), start};
result.testFilePath = path; result.testFilePath = path;

View File

@ -10,7 +10,7 @@
export type DoneFn = (reason?: string | Error) => void; export type DoneFn = (reason?: string | Error) => void;
export type BlockFn = () => void; export type BlockFn = () => void;
export type BlockName = string | Function; export type BlockName = string | Function;
export type BlockMode = void | 'skip' | 'only'; export type BlockMode = void | 'skip' | 'only' | 'todo';
export type TestMode = BlockMode; export type TestMode = BlockMode;
export type TestName = string; export type TestName = string;
export type TestFn = (done?: DoneFn) => ?Promise<any>; export type TestFn = (done?: DoneFn) => ?Promise<any>;
@ -105,6 +105,10 @@ export type Event =
name: 'test_skip', name: 'test_skip',
test: TestEntry, test: TestEntry,
|} |}
| {|
name: 'test_todo',
test: TestEntry,
|}
| {| | {|
// test failure is defined by presence of errors in `test.errors`, // test failure is defined by presence of errors in `test.errors`,
// `test_done` indicates that the test and all its hooks were run, // `test_done` indicates that the test and all its hooks were run,
@ -145,7 +149,7 @@ export type Event =
name: 'teardown', name: 'teardown',
|}; |};
export type TestStatus = 'skip' | 'done'; export type TestStatus = 'skip' | 'done' | 'todo';
export type TestResult = {| export type TestResult = {|
duration: ?number, duration: ?number,
errors: Array<FormattedError>, errors: Array<FormattedError>,

View File

@ -83,7 +83,7 @@ export type AssertionLocation = {|
path: string, path: string,
|}; |};
export type Status = 'passed' | 'failed' | 'skipped' | 'pending'; export type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo';
export type Bytes = number; export type Bytes = number;
export type Milliseconds = number; export type Milliseconds = number;
@ -118,6 +118,7 @@ export type AggregatedResultWithoutCoverage = {
numPassedTests: number, numPassedTests: number,
numPassedTestSuites: number, numPassedTestSuites: number,
numPendingTests: number, numPendingTests: number,
numTodoTests: number,
numPendingTestSuites: number, numPendingTestSuites: number,
numRuntimeErrorTestSuites: number, numRuntimeErrorTestSuites: number,
numTotalTests: number, numTotalTests: number,
@ -150,6 +151,7 @@ export type TestResult = {|
numFailingTests: number, numFailingTests: number,
numPassingTests: number, numPassingTests: number,
numPendingTests: number, numPendingTests: number,
numTodoTests: number,
openHandles: Array<Error>, openHandles: Array<Error>,
perfStats: {| perfStats: {|
end: Milliseconds, end: Milliseconds,