Skip to content

Commit 1db1b6e

Browse files
Fix --onlyfailures flag to work in non-watch mode (#10678)
1 parent 0eab327 commit 1db1b6e

File tree

13 files changed

+130
-14
lines changed

13 files changed

+130
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Fixes
88

99
- `[expect]` Fix `objectContaining` to work recursively into sub-objects ([#10508](https://github.com/facebook/jest/pull/10508))
10+
- `[jest-cli, jest-core, jest-config, jest-types]` Fix `--onlyFailures` flag to work in non-watch mode ([#10678](https://github.com/facebook/jest/pull/10678/files))
1011
- `[jest-config]` Fix for the `jest.config.ts` compiler to not interfere with `tsconfig.json` files ([#10675](https://github.com/facebook/jest/pull/10675))
1112
- `[jest-message-util]` Update to work properly with Node 15 ([#10660](https://github.com/facebook/jest/pull/10660))
1213
- `[jest-mock]` Allow to mock methods in getters (TypeScript 3.9 export) ([#10156](https://github.com/facebook/jest/pull/10156))
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. 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+
8+
import {tmpdir} from 'os';
9+
import * as path from 'path';
10+
import * as fs from 'graceful-fs';
11+
import {cleanup, writeFiles} from '../Utils';
12+
import runJest from '../runJest';
13+
14+
const DIR = path.resolve(tmpdir(), 'non-watch-mode-onlyFailures');
15+
16+
beforeEach(() => cleanup(DIR));
17+
afterEach(() => cleanup(DIR));
18+
19+
test('onlyFailures flag works in non-watch mode', () => {
20+
writeFiles(DIR, {
21+
'__tests__/a.js': `
22+
test('bar', () => { expect('bar').toBe('foo'); });
23+
`,
24+
'__tests__/b.js': `
25+
test('foo', () => { expect('foo').toBe('foo'); });
26+
`,
27+
'package.json': JSON.stringify({
28+
jest: {
29+
testEnvironment: 'node',
30+
},
31+
}),
32+
});
33+
34+
let stdout, stderr;
35+
36+
({stdout, stderr} = runJest(DIR));
37+
expect(stdout).toBe('');
38+
expect(stderr).toMatch('FAIL __tests__/a.js');
39+
expect(stderr).toMatch('PASS __tests__/b.js');
40+
41+
// only the failed test should run and it should fail
42+
({stdout, stderr} = runJest(DIR, ['--onlyFailures']));
43+
expect(stdout).toBe('');
44+
expect(stderr).toMatch('FAIL __tests__/a.js');
45+
expect(stderr).not.toMatch('__tests__/b.js');
46+
47+
// fix the failing test
48+
const data = "test('bar 1', () => { expect('bar').toBe('bar'); })";
49+
fs.writeFileSync(path.join(DIR, '__tests__/a.js'), data);
50+
51+
// only the failed test should run and it should pass
52+
({stdout, stderr} = runJest(DIR, ['--onlyFailures']));
53+
expect(stdout).toBe('');
54+
expect(stderr).toMatch('PASS __tests__/a.js');
55+
expect(stderr).not.toMatch('__tests__/b.js');
56+
57+
// No test should run
58+
({stdout, stderr} = runJest(DIR, ['--onlyFailures']));
59+
expect(stdout).toBe('No failed test found.');
60+
expect(stderr).toBe('');
61+
});

packages/jest-cli/src/__tests__/cli/args.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ describe('check', () => {
3131
);
3232
});
3333

34+
it('raises an exception if onlyFailures and watchAll are both specified', () => {
35+
const argv = {onlyFailures: true, watchAll: true} as Config.Argv;
36+
expect(() => check(argv)).toThrow(
37+
'Both --onlyFailures and --watchAll were specified',
38+
);
39+
});
40+
3441
it('raises an exception when lastCommit and watchAll are both specified', () => {
3542
const argv = {lastCommit: true, watchAll: true} as Config.Argv;
3643
expect(() => check(argv)).toThrow(

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ export function check(argv: Config.Argv): true {
3232
}
3333
}
3434

35+
if (argv.onlyFailures && argv.watchAll) {
36+
throw new Error(
37+
`Both --onlyFailures and --watchAll were specified, but these two ` +
38+
'options do not make sense together.',
39+
);
40+
}
41+
3542
if (argv.findRelatedTests && argv._.length === 0) {
3643
throw new Error(
3744
'The --findRelatedTests option requires file paths to be specified.\n' +

packages/jest-config/src/ValidConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const initialOptions: Config.InitialOptions = {
7777
notify: false,
7878
notifyMode: 'failure-change',
7979
onlyChanged: false,
80+
onlyFailures: false,
8081
preset: 'react-native',
8182
prettierPath: '<rootDir>/node_modules/prettier',
8283
projects: ['project-a', 'project-b/'],

packages/jest-config/src/normalize.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,7 @@ export default function normalize(
896896
case 'notify':
897897
case 'notifyMode':
898898
case 'onlyChanged':
899+
case 'onlyFailures':
899900
case 'outputFile':
900901
case 'passWithNoTests':
901902
case 'replname':
@@ -985,10 +986,12 @@ export default function normalize(
985986

986987
if (argv.all) {
987988
newOptions.onlyChanged = false;
989+
newOptions.onlyFailures = false;
988990
} else if (newOptions.testPathPattern) {
989991
// When passing a test path pattern we don't want to only monitor changed
990-
// files unless `--watch` is also passed.
992+
// or failed files unless `--watch` is also passed.
991993
newOptions.onlyChanged = newOptions.watch;
994+
newOptions.onlyFailures = newOptions.watch;
992995
}
993996

994997
if (!newOptions.onlyChanged) {

packages/jest-core/src/__tests__/__snapshots__/getNoTestsFoundMessage.test.js.snap

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
exports[`getNoTestsFoundMessage returns correct message when monitoring only changed 1`] = `"<bold>No tests found related to files changed since last commit.</>"`;
44
5-
exports[`getNoTestsFoundMessage returns correct message when monitoring only failures 1`] = `
6-
"<bold>No failed test found.</>
7-
<bold></><dim>Press \`f\` to quit \\"only failed tests\\" mode.</>"
8-
`;
5+
exports[`getNoTestsFoundMessage returns correct message when monitoring only failures 1`] = `"<bold>No failed test found.</>"`;
96
107
exports[`getNoTestsFoundMessage returns correct message with passWithNoTests 1`] = `"<bold>No tests found, exiting with code 0</>"`;
118

packages/jest-core/src/getNoTestFoundFailed.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@
66
*/
77

88
import chalk = require('chalk');
9+
import type {Config} from '@jest/types';
10+
import {isInteractive} from 'jest-util';
911

10-
export default function getNoTestFoundFailed(): string {
11-
return (
12-
chalk.bold('No failed test found.\n') +
13-
chalk.dim('Press `f` to quit "only failed tests" mode.')
14-
);
12+
export default function getNoTestFoundFailed(
13+
globalConfig: Config.GlobalConfig,
14+
): string {
15+
let msg = chalk.bold('No failed test found.');
16+
if (isInteractive) {
17+
msg += chalk.dim(
18+
'\n' +
19+
(globalConfig.watch
20+
? 'Press `f` to quit "only failed tests" mode.'
21+
: 'Run Jest without `--onlyFailures` or with `--all` to run all tests.'),
22+
);
23+
}
24+
return msg;
1525
}

packages/jest-core/src/getNoTestsFoundMessage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function getNoTestsFoundMessage(
1818
globalConfig: Config.GlobalConfig,
1919
): string {
2020
if (globalConfig.onlyFailures) {
21-
return getNoTestFoundFailed();
21+
return getNoTestFoundFailed(globalConfig);
2222
}
2323
if (globalConfig.onlyChanged) {
2424
return getNoTestFoundRelatedToChangedFiles(globalConfig);

packages/jest-core/src/runJest.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,13 @@ export default async function runJest({
198198
return;
199199
}
200200

201-
if (globalConfig.onlyFailures && failedTestsCache) {
202-
allTests = failedTestsCache.filterTests(allTests);
203-
globalConfig = failedTestsCache.updateConfig(globalConfig);
201+
if (globalConfig.onlyFailures) {
202+
if (failedTestsCache) {
203+
allTests = failedTestsCache.filterTests(allTests);
204+
globalConfig = failedTestsCache.updateConfig(globalConfig);
205+
} else {
206+
allTests = sequencer.allFailedTests(allTests);
207+
}
204208
}
205209

206210
const hasTests = allTests.length > 0;

packages/jest-test-sequencer/src/__tests__/test_sequencer.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,21 @@ test('writes the cache based on results without existing cache', () => {
162162
});
163163
});
164164

165+
test('returns failed tests in sorted order', () => {
166+
fs.readFileSync.mockImplementationOnce(() =>
167+
JSON.stringify({
168+
'/test-a.js': [SUCCESS, 5],
169+
'/test-ab.js': [FAIL, 1],
170+
'/test-c.js': [FAIL],
171+
}),
172+
);
173+
const testPaths = ['/test-a.js', '/test-ab.js', '/test-c.js'];
174+
expect(sequencer.allFailedTests(toTests(testPaths))).toEqual([
175+
{context, duration: undefined, path: '/test-c.js'},
176+
{context, duration: 1, path: '/test-ab.js'},
177+
]);
178+
});
179+
165180
test('writes the cache based on the results', () => {
166181
fs.readFileSync.mockImplementationOnce(() =>
167182
JSON.stringify({

packages/jest-test-sequencer/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ export default class TestSequencer {
109109
});
110110
}
111111

112+
allFailedTests(tests: Array<Test>): Array<Test> {
113+
const hasFailed = (cache: Cache, test: Test) =>
114+
cache[test.path]?.[0] === FAIL;
115+
return this.sort(
116+
tests.filter(test => hasFailed(this._getCache(test), test)),
117+
);
118+
}
119+
112120
cacheResults(tests: Array<Test>, results: AggregatedResult): void {
113121
const map = Object.create(null);
114122
tests.forEach(test => (map[test.path] = test));

packages/jest-types/src/Config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ export type InitialOptions = Partial<{
168168
notify: boolean;
169169
notifyMode: string;
170170
onlyChanged: boolean;
171+
onlyFailures: boolean;
171172
outputFile: Path;
172173
passWithNoTests: boolean;
173174
preprocessorIgnorePatterns: Array<Glob>;
@@ -418,6 +419,7 @@ export type Argv = Arguments<
418419
notify: boolean;
419420
notifyMode: string;
420421
onlyChanged: boolean;
422+
onlyFailures: boolean;
421423
outputFile: string;
422424
preset: string | null | undefined;
423425
projects: Array<string>;

0 commit comments

Comments
 (0)