Skip to content

Commit 6f7c85a

Browse files
lstkzcpojer
authored andcommitted
add only-failures mode (#4886)
* add only-failures mode Signed-off-by: Łukasz Sentkiewicz <sirmims@gmail.com> lint fixes Signed-off-by: Łukasz Sentkiewicz <sirmims@gmail.com> fix tests Signed-off-by: Łukasz Sentkiewicz <sirmims@gmail.com> fix tests Signed-off-by: Łukasz Sentkiewicz <sirmims@gmail.com> * replace 'only-failures' message Signed-off-by: Łukasz Sentkiewicz <sirmims@gmail.com>
1 parent 20d9fbf commit 6f7c85a

15 files changed

Lines changed: 180 additions & 3 deletions

File tree

packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Array [
1212
"
1313
Watch Usage
1414
› Press a to run all tests.
15+
› Press f to run only failed tests.
1516
› Press p to filter by a filename regex pattern.
1617
› Press t to filter by a test name regex pattern.
1718
› Press q to quit watch mode.
@@ -26,6 +27,7 @@ Array [
2627
"
2728
Watch Usage
2829
› Press a to run all tests.
30+
› Press f to run only failed tests.
2931
› Press p to filter by a filename regex pattern.
3032
› Press t to filter by a test name regex pattern.
3133
› Press s to do nothing.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import FailedTestsCache from '../failed_tests_cache';
2+
3+
describe('FailedTestsCache', () => {
4+
test('should filter tests', () => {
5+
const failedTestsCache = new FailedTestsCache();
6+
failedTestsCache.setTestResults([
7+
{
8+
numFailingTests: 0,
9+
testFilePath: '/path/to/passing.js',
10+
testResults: [
11+
{fullName: 'test 1', status: 'passed'},
12+
{fullName: 'test 2', status: 'passed'},
13+
],
14+
},
15+
{
16+
numFailingTests: 2,
17+
testFilePath: '/path/to/failed_1.js',
18+
testResults: [
19+
{fullName: 'test 3', status: 'failed'},
20+
{fullName: 'test 4', status: 'failed'},
21+
],
22+
},
23+
{
24+
numFailingTests: 1,
25+
testFilePath: '/path/to/failed_2.js',
26+
testResults: [
27+
{fullName: 'test 5', status: 'failed'},
28+
{fullName: 'test 6', status: 'passed'},
29+
],
30+
},
31+
]);
32+
33+
const result = failedTestsCache.filterTests([
34+
{
35+
path: '/path/to/passing.js',
36+
},
37+
{
38+
path: '/path/to/failed_1.js',
39+
},
40+
{
41+
path: '/path/to/failed_2.js',
42+
},
43+
{
44+
path: '/path/to/unknown.js',
45+
},
46+
]);
47+
expect(result).toMatchObject([
48+
{
49+
path: '/path/to/failed_1.js',
50+
},
51+
{
52+
path: '/path/to/failed_2.js',
53+
},
54+
]);
55+
expect(failedTestsCache.updateConfig({})).toMatchObject({
56+
enabledTestsMap: {
57+
'/path/to/failed_1.js': {'test 3': true, 'test 4': true},
58+
'/path/to/failed_2.js': {'test 5': true},
59+
},
60+
});
61+
});
62+
});

packages/jest-cli/src/cli/args.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ export const options = {
357357
'running tests in a git repository at the moment.',
358358
type: 'boolean',
359359
},
360+
onlyFailures: {
361+
alias: 'f',
362+
default: undefined,
363+
description: 'Run tests that failed in the previous execution.',
364+
type: 'boolean',
365+
},
360366
outputFile: {
361367
description:
362368
'Write test results to a file when the --json option is ' +

packages/jest-cli/src/cli/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ const runWithoutWatch = async (
368368
return await runJest({
369369
changedFilesPromise,
370370
contexts,
371+
failedTestsCache: null,
371372
globalConfig,
372373
onComplete,
373374
outputStream,

packages/jest-cli/src/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export const KEYS = {
2323
CONTROL_D: '04',
2424
ENTER: '0d',
2525
ESCAPE: '1b',
26+
F: '66',
2627
O: '6f',
2728
P: '70',
2829
Q: '71',
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import type {Test} from 'types/TestRunner';
11+
import type {TestResult} from 'types/TestResult';
12+
import type {GlobalConfig} from 'types/Config';
13+
14+
export default class FailedTestsCache {
15+
_enabledTestsMap: ?{[key: string]: {[key: string]: boolean}};
16+
17+
filterTests(tests: Array<Test>): Array<Test> {
18+
if (!this._enabledTestsMap) {
19+
return tests;
20+
}
21+
// $FlowFixMe
22+
return tests.filter(testResult => this._enabledTestsMap[testResult.path]);
23+
}
24+
25+
setTestResults(testResults: Array<TestResult>) {
26+
this._enabledTestsMap = (testResults || [])
27+
.filter(testResult => testResult.numFailingTests)
28+
.reduce((suiteMap, testResult) => {
29+
suiteMap[testResult.testFilePath] = testResult.testResults
30+
.filter(test => test.status === 'failed')
31+
.reduce((testMap, test) => {
32+
testMap[test.fullName] = true;
33+
return testMap;
34+
}, {});
35+
return suiteMap;
36+
}, {});
37+
this._enabledTestsMap = Object.freeze(this._enabledTestsMap);
38+
}
39+
40+
updateConfig(globalConfig: GlobalConfig): GlobalConfig {
41+
if (!this._enabledTestsMap) {
42+
return globalConfig;
43+
}
44+
// $FlowFixMe Object.assign
45+
const newConfig: GlobalConfig = Object.assign({}, globalConfig);
46+
newConfig.enabledTestsMap = this._enabledTestsMap;
47+
return Object.freeze(newConfig);
48+
}
49+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import chalk from 'chalk';
2+
3+
export default function getNoTestFoundFailed() {
4+
return (
5+
chalk.bold('No failed test found.\n') +
6+
chalk.dim('Press `f` to run all tests.')
7+
);
8+
}

packages/jest-cli/src/get_no_test_found_message.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import getNoTestFound from './get_no_test_found';
22
import getNoTestFoundRelatedToChangedFiles from './get_no_test_found_related_to_changed_files';
33
import getNoTestFoundVerbose from './get_no_test_found_verbose';
4+
import getNoTestFoundFailed from './get_no_test_found_failed';
45

56
export default function getNoTestsFoundMessage(
67
testRunData,
78
globalConfig,
89
): string {
10+
if (globalConfig.onlyFailures) {
11+
return getNoTestFoundFailed();
12+
}
913
if (globalConfig.onlyChanged) {
1014
return getNoTestFoundRelatedToChangedFiles(globalConfig);
1115
}

packages/jest-cli/src/lib/update_global_config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Options = {
1616
updateSnapshot?: SnapshotUpdateState,
1717
mode?: 'watch' | 'watchAll',
1818
passWithNoTests?: boolean,
19+
onlyFailures?: boolean,
1920
};
2021

2122
export default (globalConfig: GlobalConfig, options: Options): GlobalConfig => {
@@ -60,5 +61,9 @@ export default (globalConfig: GlobalConfig, options: Options): GlobalConfig => {
6061
newConfig.passWithNoTests = true;
6162
}
6263

64+
if ('onlyFailures' in options) {
65+
newConfig.onlyFailures = options.onlyFailures || false;
66+
}
67+
6368
return Object.freeze(newConfig);
6469
};

packages/jest-cli/src/run_jest.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import SearchSource from './search_source';
2222
import TestScheduler from './test_scheduler';
2323
import TestSequencer from './test_sequencer';
2424
import {makeEmptyAggregatedTestResult} from './test_result_helpers';
25+
import FailedTestsCache from './failed_tests_cache';
2526

2627
const setConfig = (contexts, newConfig) =>
2728
contexts.forEach(
@@ -82,6 +83,7 @@ export default (async function runJest({
8283
startRun,
8384
changedFilesPromise,
8485
onComplete,
86+
failedTestsCache,
8587
}: {
8688
globalConfig: GlobalConfig,
8789
contexts: Array<Context>,
@@ -90,6 +92,7 @@ export default (async function runJest({
9092
startRun: (globalConfig: GlobalConfig) => *,
9193
changedFilesPromise: ?ChangedFilesPromise,
9294
onComplete: (testResults: AggregatedResult) => any,
95+
failedTestsCache: ?FailedTestsCache,
9396
}) {
9497
if (globalConfig.globalSetup) {
9598
// $FlowFixMe
@@ -138,6 +141,12 @@ export default (async function runJest({
138141
onComplete && onComplete(makeEmptyAggregatedTestResult());
139142
return null;
140143
}
144+
145+
if (globalConfig.onlyFailures && failedTestsCache) {
146+
allTests = failedTestsCache.filterTests(allTests);
147+
globalConfig = failedTestsCache.updateConfig(globalConfig);
148+
}
149+
141150
if (!allTests.length) {
142151
const noTestsFoundMessage = getNoTestsFoundMessage(
143152
testRunData,

0 commit comments

Comments
 (0)