Skip to content

Commit 42cd78c

Browse files
committed
added notifyMode flag to specify when a notification should display
forgot about other notifyMode configs add notifyMode to normalize Created TestSchedulerContext to save previous status of test to make the change option work updated docs minor linting fix Added additional options such as success-change and failure-change Put conditions back in if else clauses Fixed documentation on notifyMode Added notify reporter test (for review) Finished NotifyReporter tests. Testing against simulated sequences of events.
1 parent a24204a commit 42cd78c

File tree

17 files changed

+350
-9
lines changed

17 files changed

+350
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@
286286

287287
### Features
288288

289+
* `[jest-cli]` Added --notifyMode to specify when to be notified.
290+
([#5125](https://github.com/facebook/jest/pull/5125))
289291
* `[jest-message-util]` Add codeframe to test assertion failures
290292
([#5087](https://github.com/facebook/jest/pull/5087))
291293
* `[jest-config]` Add Global Setup/Teardown options

docs/Configuration.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,21 @@ Default: `false`
425425

426426
Activates notifications for test results.
427427

428+
### `notifyMode` [string]
429+
430+
Default: `always`
431+
432+
Specifies notification mode. Requires `notify: true`.
433+
434+
#### Modes
435+
436+
* `always`: always send a notification.
437+
* `failure`: send a notification when tests fail.
438+
* `success`: send a notification when tests pass.
439+
* `change`: send a notification when the status changed.
440+
* `success-change`: send a notification when tests pass or once when it fails.
441+
* `failure-success`: send a notification when tests fails or once when it passes.
442+
428443
### `preset` [string]
429444

430445
Default: `undefined`

integration-tests/__tests__/__snapshots__/show_config.test.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
8080
\\"noStackTrace\\": false,
8181
\\"nonFlagArgs\\": [],
8282
\\"notify\\": false,
83+
\\"notifyMode\\": \\"always\\",
8384
\\"passWithNoTests\\": false,
8485
\\"rootDir\\": \\"<<REPLACED_ROOT_DIR>>\\",
8586
\\"runTestsByPath\\": false,
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`test always 1`] = `
4+
Array [
5+
Object {
6+
"icon": true,
7+
"message": "✅ 3 tests passed",
8+
"title": "100% Passed",
9+
},
10+
Object {
11+
"icon": true,
12+
"message": "⛔️ 3 of 3 tests failed",
13+
"title": "100% Failed",
14+
},
15+
Object {
16+
"icon": true,
17+
"message": "✅ 3 tests passed",
18+
"title": "100% Passed",
19+
},
20+
Object {
21+
"icon": true,
22+
"message": "✅ 3 tests passed",
23+
"title": "100% Passed",
24+
},
25+
Object {
26+
"icon": true,
27+
"message": "⛔️ 3 of 3 tests failed",
28+
"title": "100% Failed",
29+
},
30+
Object {
31+
"icon": true,
32+
"message": "⛔️ 3 of 3 tests failed",
33+
"title": "100% Failed",
34+
},
35+
]
36+
`;
37+
38+
exports[`test change 1`] = `
39+
Array [
40+
Object {
41+
"icon": true,
42+
"message": "✅ 3 tests passed",
43+
"title": "100% Passed",
44+
},
45+
Object {
46+
"icon": true,
47+
"message": "⛔️ 3 of 3 tests failed",
48+
"title": "100% Failed",
49+
},
50+
Object {
51+
"icon": true,
52+
"message": "✅ 3 tests passed",
53+
"title": "100% Passed",
54+
},
55+
Object {
56+
"icon": true,
57+
"message": "⛔️ 3 of 3 tests failed",
58+
"title": "100% Failed",
59+
},
60+
]
61+
`;
62+
63+
exports[`test failure-change 1`] = `
64+
Array [
65+
Object {
66+
"icon": true,
67+
"message": "✅ 3 tests passed",
68+
"title": "100% Passed",
69+
},
70+
Object {
71+
"icon": true,
72+
"message": "⛔️ 3 of 3 tests failed",
73+
"title": "100% Failed",
74+
},
75+
Object {
76+
"icon": true,
77+
"message": "✅ 3 tests passed",
78+
"title": "100% Passed",
79+
},
80+
Object {
81+
"icon": true,
82+
"message": "⛔️ 3 of 3 tests failed",
83+
"title": "100% Failed",
84+
},
85+
Object {
86+
"icon": true,
87+
"message": "⛔️ 3 of 3 tests failed",
88+
"title": "100% Failed",
89+
},
90+
]
91+
`;
92+
93+
exports[`test success 1`] = `
94+
Array [
95+
Object {
96+
"icon": true,
97+
"message": "✅ 3 tests passed",
98+
"title": "100% Passed",
99+
},
100+
Object {
101+
"icon": true,
102+
"message": "✅ 3 tests passed",
103+
"title": "100% Passed",
104+
},
105+
Object {
106+
"icon": true,
107+
"message": "✅ 3 tests passed",
108+
"title": "100% Passed",
109+
},
110+
]
111+
`;
112+
113+
exports[`test success-change 1`] = `
114+
Array [
115+
Object {
116+
"icon": true,
117+
"message": "✅ 3 tests passed",
118+
"title": "100% Passed",
119+
},
120+
Object {
121+
"icon": true,
122+
"message": "⛔️ 3 of 3 tests failed",
123+
"title": "100% Failed",
124+
},
125+
Object {
126+
"icon": true,
127+
"message": "✅ 3 tests passed",
128+
"title": "100% Passed",
129+
},
130+
Object {
131+
"icon": true,
132+
"message": "✅ 3 tests passed",
133+
"title": "100% Passed",
134+
},
135+
Object {
136+
"icon": true,
137+
"message": "⛔️ 3 of 3 tests failed",
138+
"title": "100% Failed",
139+
},
140+
]
141+
`;
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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+
*/
8+
9+
'use strict';
10+
11+
import TestScheduler from '../test_scheduler';
12+
import NotifyReporter from '../reporters/notify_reporter';
13+
import type {TestSchedulerContext} from '../test_scheduler';
14+
import type {AggregatedResult} from '../../../../types/TestResult';
15+
import type {Jest as jest} from '../../../../types/Jest';
16+
17+
const ICON_PATH = '/assets/jest_logo.png';
18+
19+
jest.mock('../reporters/default_reporter');
20+
jest.mock('node-notifier', () => ({
21+
notify: jest.fn(),
22+
}));
23+
24+
const initialContext: TestSchedulerContext = {
25+
firstRun: true,
26+
previousSuccess: false,
27+
};
28+
29+
const aggregatedResultsSuccess: AggregatedResult = {
30+
numFailedTestSuites: 0,
31+
numFailedTests: 0,
32+
numPassedTestSuites: 1,
33+
numPassedTests: 3,
34+
numRuntimeErrorTestSuites: 0,
35+
numTotalTestSuites: 1,
36+
numTotalTests: 3,
37+
success: true,
38+
};
39+
40+
const aggregatedResultsFailure: AggregatedResult = {
41+
numFailedTestSuites: 1,
42+
numFailedTests: 3,
43+
numPassedTestSuites: 0,
44+
numPassedTests: 9,
45+
numRuntimeErrorTestSuites: 0,
46+
numTotalTestSuites: 1,
47+
numTotalTests: 3,
48+
success: false,
49+
};
50+
51+
// Simulated sequence of events for NotifyReporter
52+
const notifyEvents = [
53+
aggregatedResultsSuccess,
54+
aggregatedResultsFailure,
55+
aggregatedResultsSuccess,
56+
aggregatedResultsSuccess,
57+
aggregatedResultsFailure,
58+
aggregatedResultsFailure,
59+
];
60+
61+
const iconShown = path => path.endsWith(ICON_PATH);
62+
63+
test('.addReporter() .removeReporter()', () => {
64+
const scheduler = new TestScheduler(
65+
{},
66+
{},
67+
Object.assign({}, initialContext),
68+
);
69+
const reporter = new NotifyReporter();
70+
scheduler.addReporter(reporter);
71+
expect(scheduler._dispatcher._reporters).toContain(reporter);
72+
scheduler.removeReporter(NotifyReporter);
73+
expect(scheduler._dispatcher._reporters).not.toContain(reporter);
74+
});
75+
76+
const testModes = (notifyMode: string, arl: Array<AggregatedResult>) => {
77+
const notify = require('node-notifier');
78+
notify.notify.mock.calls = [];
79+
80+
let previousContext = initialContext;
81+
arl.forEach((ar, i) => {
82+
const newContext = Object.assign(previousContext, {
83+
firstRun: i === 0,
84+
previousSuccess: previousContext.previousSuccess,
85+
});
86+
const reporter = new NotifyReporter(
87+
{notify: true, notifyMode},
88+
{},
89+
newContext,
90+
);
91+
previousContext = newContext;
92+
reporter.onRunComplete(new Set(), ar);
93+
});
94+
95+
expect(
96+
notify.notify.mock.calls.map(([{icon, message, title}]) => ({
97+
icon: iconShown(icon),
98+
message,
99+
title,
100+
})),
101+
).toMatchSnapshot();
102+
};
103+
104+
test('test always', () => {
105+
testModes('always', notifyEvents);
106+
});
107+
108+
test('test success', () => {
109+
testModes('success', notifyEvents);
110+
});
111+
112+
test('test change', () => {
113+
testModes('change', notifyEvents);
114+
});
115+
116+
test('test success-change', () => {
117+
testModes('success-change', notifyEvents);
118+
});
119+
120+
test('test failure-change', () => {
121+
testModes('failure-change', notifyEvents);
122+
});

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,11 @@ export const options = {
365365
description: 'Activates notifications for test results.',
366366
type: 'boolean',
367367
},
368+
notifyMode: {
369+
default: 'always',
370+
description: 'Specifies when notifications will appear for test results.',
371+
type: 'string',
372+
},
368373
onlyChanged: {
369374
alias: 'o',
370375
default: undefined,

packages/jest-cli/src/reporters/notify_reporter.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import path from 'path';
1616
import util from 'util';
1717
import notifier from 'node-notifier';
1818
import BaseReporter from './base_reporter';
19+
import type {TestSchedulerContext} from '../test_scheduler';
1920

2021
const isDarwin = process.platform === 'darwin';
2122

@@ -24,29 +25,48 @@ const icon = path.resolve(__dirname, '../assets/jest_logo.png');
2425
export default class NotifyReporter extends BaseReporter {
2526
_startRun: (globalConfig: GlobalConfig) => *;
2627
_globalConfig: GlobalConfig;
27-
28+
_context: TestSchedulerContext;
2829
constructor(
2930
globalConfig: GlobalConfig,
3031
startRun: (globalConfig: GlobalConfig) => *,
32+
context: TestSchedulerContext,
3133
) {
3234
super();
3335
this._globalConfig = globalConfig;
3436
this._startRun = startRun;
37+
this._context = context;
3538
}
3639

3740
onRunComplete(contexts: Set<Context>, result: AggregatedResult): void {
3841
const success =
3942
result.numFailedTests === 0 && result.numRuntimeErrorTestSuites === 0;
4043

41-
if (success) {
44+
const notifyMode = this._globalConfig.notifyMode;
45+
const statusChanged =
46+
this._context.previousSuccess !== success || this._context.firstRun;
47+
if (
48+
success &&
49+
(notifyMode === 'always' ||
50+
notifyMode === 'success' ||
51+
notifyMode === 'success-change' ||
52+
(notifyMode === 'change' && statusChanged) ||
53+
(notifyMode === 'failure-change' && statusChanged))
54+
) {
4255
const title = util.format('%d%% Passed', 100);
4356
const message = util.format(
4457
(isDarwin ? '\u2705 ' : '') + '%d tests passed',
4558
result.numPassedTests,
4659
);
4760

4861
notifier.notify({icon, message, title});
49-
} else {
62+
} else if (
63+
!success &&
64+
(notifyMode === 'always' ||
65+
notifyMode === 'failure' ||
66+
notifyMode === 'failure-change' ||
67+
(notifyMode === 'change' && statusChanged) ||
68+
(notifyMode === 'success-change' && statusChanged))
69+
) {
5070
const failed = result.numFailedTests / result.numTotalTests;
5171

5272
const title = util.format(
@@ -83,5 +103,7 @@ export default class NotifyReporter extends BaseReporter {
83103
},
84104
);
85105
}
106+
this._context.previousSuccess = success;
107+
this._context.firstRun = false;
86108
}
87109
}

0 commit comments

Comments
 (0)