Skip to content

Commit 2b60556

Browse files
dflupunovemberborn
authored andcommittedJan 27, 2019
Print pending tests on interrupt, and on timeout in mini reporter
Fixes #583.
1 parent 0a5fe42 commit 2b60556

File tree

8 files changed

+124
-52
lines changed

8 files changed

+124
-52
lines changed
 

‎api.js

+26-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ class Api extends Emittery {
4343
this._allExtensions = this.options.extensions.all;
4444
this._regexpFullExtensions = new RegExp(`\\.(${this.options.extensions.full.map(ext => escapeStringRegexp(ext)).join('|')})$`);
4545
this._precompiler = null;
46+
this._interruptHandler = () => {};
47+
48+
if (options.ranFromCli) {
49+
process.on('SIGINT', () => this._interruptHandler());
50+
}
4651
}
4752

4853
run(files, runtimeOptions = {}) {
@@ -69,17 +74,36 @@ class Api extends Emittery {
6974
bailed = true;
7075
}
7176

77+
runStatus.emitStateChange({type: 'timeout', period: timeout});
78+
7279
for (const worker of pendingWorkers) {
7380
timedOutWorkerFiles.add(worker.file);
7481
worker.exit();
7582
}
76-
77-
runStatus.emitStateChange({type: 'timeout', period: timeout, timedOutWorkerFiles});
7883
}, timeout);
7984
} else {
8085
restartTimer = Object.assign(() => {}, {cancel() {}});
8186
}
8287

88+
this._interruptHandler = () => {
89+
if (bailed) {
90+
// Exiting already
91+
return;
92+
}
93+
94+
// Prevent new test files from running
95+
bailed = true;
96+
97+
// Make sure we don't run the timeout handler
98+
restartTimer.cancel();
99+
100+
runStatus.emitStateChange({type: 'interrupt'});
101+
102+
for (const worker of pendingWorkers) {
103+
worker.exit();
104+
}
105+
};
106+
83107
// Find all test files.
84108
return new AvaFiles({cwd: apiOptions.resolveTestsFrom, files, extensions: this._allExtensions}).findTestFiles()
85109
.then(files => {

‎lib/cli.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ exports.run = () => { // eslint-disable-line complexity
207207
snapshotDir: conf.snapshotDir ? path.resolve(projectDir, conf.snapshotDir) : null,
208208
color: conf.color,
209209
workerArgv: cli.flags['--'],
210-
parallelRuns
210+
parallelRuns,
211+
ranFromCli: true
211212
});
212213

213214
let reporter;
@@ -230,7 +231,16 @@ exports.run = () => { // eslint-disable-line complexity
230231
});
231232
}
232233

233-
api.on('run', plan => reporter.startRun(plan));
234+
api.on('run', plan => {
235+
reporter.startRun(plan);
236+
237+
plan.status.on('stateChange', evt => {
238+
if (evt.type === 'interrupt') {
239+
reporter.endRun();
240+
process.exit(1); // eslint-disable-line unicorn/no-process-exit
241+
}
242+
});
243+
});
234244

235245
const files = cli.input.length > 0 ? cli.input : arrify(conf.files);
236246

‎lib/reporters/mini.js

+23-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,14 @@ class MiniReporter {
178178
this.writeTestSummary(evt);
179179
break;
180180
case 'timeout':
181-
this.writeWithCounts(colors.error(`${figures.cross} Exited because no new tests completed within the last ${evt.period}ms of inactivity`));
181+
this.lineWriter.writeLine(colors.error(`\n${figures.cross} Timed out while running tests`));
182+
this.lineWriter.writeLine('');
183+
this.writePendingTests(evt);
184+
break;
185+
case 'interrupt':
186+
this.lineWriter.writeLine(colors.error(`\n${figures.cross} Exiting due to SIGINT`));
187+
this.lineWriter.writeLine('');
188+
this.writePendingTests(evt);
182189
break;
183190
case 'uncaught-exception':
184191
this.uncaughtExceptions.push(evt);
@@ -347,6 +354,21 @@ class MiniReporter {
347354
this.writeErr(evt);
348355
}
349356

357+
writePendingTests(evt) {
358+
for (const [file, testsInFile] of evt.pendingTests) {
359+
if (testsInFile.size === 0) {
360+
continue;
361+
}
362+
363+
this.lineWriter.writeLine(`${testsInFile.size} tests were pending in ${file}\n`);
364+
for (const title of testsInFile) {
365+
this.lineWriter.writeLine(`${figures.circleDotted} ${this.prefixTitle(file, title)}`);
366+
}
367+
368+
this.lineWriter.writeLine('');
369+
}
370+
}
371+
350372
endRun() { // eslint-disable-line complexity
351373
this.spinner.stop();
352374
cliCursor.show(this.reportStream);

‎lib/reporters/verbose.js

+18-40
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,6 @@ class VerboseReporter {
103103
const fileStats = this.stats && evt.testFile ? this.stats.byFile.get(evt.testFile) : null;
104104

105105
switch (evt.type) {
106-
case 'declared-test':
107-
this.addTestRunning(evt.testFile, evt.title);
108-
break;
109106
case 'hook-failed':
110107
this.failures.push(evt);
111108
this.writeTestSummary(evt);
@@ -145,20 +142,25 @@ class VerboseReporter {
145142
this.stats = evt.stats;
146143
break;
147144
case 'test-failed':
148-
this.removeTestRunning(evt.testFile, evt.title);
149145
this.failures.push(evt);
150146
this.writeTestSummary(evt);
151147
break;
152148
case 'test-passed':
153-
this.removeTestRunning(evt.testFile, evt.title);
154149
if (evt.knownFailing) {
155150
this.knownFailures.push(evt);
156151
}
157152

158153
this.writeTestSummary(evt);
159154
break;
160155
case 'timeout':
161-
this.writeTimeoutSummary(evt);
156+
this.lineWriter.writeLine(colors.error(`\n${figures.cross} Timed out while running tests`));
157+
this.lineWriter.writeLine('');
158+
this.writePendingTests(evt);
159+
break;
160+
case 'interrupt':
161+
this.lineWriter.writeLine(colors.error(`\n${figures.cross} Exiting due to SIGINT`));
162+
this.lineWriter.writeLine('');
163+
this.writePendingTests(evt);
162164
break;
163165
case 'uncaught-exception':
164166
this.lineWriter.ensureEmptyLine();
@@ -261,42 +263,18 @@ class VerboseReporter {
261263
}
262264
}
263265

264-
addTestRunning(file, title) {
265-
if (!this.runningTestFiles.has(file)) {
266-
this.runningTestFiles.set(file, new Set());
267-
}
268-
269-
this.runningTestFiles.get(file).add(title);
270-
}
271-
272-
removeTestRunning(file, title) {
273-
const byFile = this.runningTestFiles.get(file);
274-
if (byFile) {
275-
byFile.delete(title);
276-
}
277-
}
278-
279-
writeTimeoutSummary(evt) {
280-
this.lineWriter.writeLine(colors.error(`\n${figures.cross} Exited because no new tests completed within the last ${evt.period}ms of inactivity`));
281-
let wroteTrailingSeparator = false;
282-
283-
for (const timedOutFile of evt.timedOutWorkerFiles) {
284-
const byFile = this.runningTestFiles.get(timedOutFile);
285-
if (byFile) {
286-
this.runningTestFiles.delete(timedOutFile);
287-
288-
if (!wroteTrailingSeparator) {
289-
this.lineWriter.writeLine('');
290-
}
291-
292-
this.lineWriter.writeLine(`${byFile.size} tests still running in ${timedOutFile}:\n`);
293-
for (const title of byFile) {
294-
this.lineWriter.writeLine(`${figures.circleDotted} ${this.prefixTitle(timedOutFile, title)}`);
295-
}
266+
writePendingTests(evt) {
267+
for (const [file, testsInFile] of evt.pendingTests) {
268+
if (testsInFile.size === 0) {
269+
continue;
270+
}
296271

297-
this.lineWriter.writeLine('');
298-
wroteTrailingSeparator = true;
272+
this.lineWriter.writeLine(`${testsInFile.size} tests were pending in ${file}\n`);
273+
for (const title of testsInFile) {
274+
this.lineWriter.writeLine(`${figures.circleDotted} ${this.prefixTitle(file, title)}`);
299275
}
276+
277+
this.lineWriter.writeLine('');
300278
}
301279
}
302280

‎lib/run-status.js

+26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ class RunStatus extends Emittery {
66
constructor(files, parallelRuns) {
77
super();
88

9+
this.pendingTests = new Map();
10+
911
this.stats = {
1012
byFile: new Map(),
1113
declaredTests: 0,
@@ -43,6 +45,8 @@ class RunStatus extends Emittery {
4345
uncaughtExceptions: 0,
4446
unhandledRejections: 0
4547
});
48+
49+
this.pendingTests.set(testFile, new Set());
4650
worker.onStateChange(data => this.emitStateChange(data));
4751
}
4852

@@ -55,6 +59,7 @@ class RunStatus extends Emittery {
5559
case 'declared-test':
5660
stats.declaredTests++;
5761
fileStats.declaredTests++;
62+
this.addPendingTest(event);
5863
break;
5964
case 'hook-failed':
6065
stats.failedHooks++;
@@ -87,6 +92,7 @@ class RunStatus extends Emittery {
8792
fileStats.failedTests++;
8893
stats.remainingTests--;
8994
fileStats.remainingTests--;
95+
this.removePendingTest(event);
9096
break;
9197
case 'test-passed':
9298
if (event.knownFailing) {
@@ -99,10 +105,17 @@ class RunStatus extends Emittery {
99105

100106
stats.remainingTests--;
101107
fileStats.remainingTests--;
108+
this.removePendingTest(event);
102109
break;
103110
case 'timeout':
111+
event.pendingTests = this.pendingTests;
112+
this.pendingTests = new Map();
104113
stats.timeouts++;
105114
break;
115+
case 'interrupt':
116+
event.pendingTests = this.pendingTests;
117+
this.pendingTests = new Map();
118+
break;
106119
case 'uncaught-exception':
107120
stats.uncaughtExceptions++;
108121
fileStats.uncaughtExceptions++;
@@ -149,5 +162,18 @@ class RunStatus extends Emittery {
149162

150163
return 0;
151164
}
165+
166+
addPendingTest(event) {
167+
if (this.pendingTests.has(event.testFile)) {
168+
this.pendingTests.get(event.testFile).add(event.title);
169+
}
170+
}
171+
172+
removePendingTest(event) {
173+
if (this.pendingTests.has(event.testFile)) {
174+
this.pendingTests.get(event.testFile).delete(event.title);
175+
}
176+
}
152177
}
178+
153179
module.exports = RunStatus;

‎test/integration/assorted.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,23 @@ const {execCli} = require('../helper/cli');
1010
test('timeout', t => {
1111
execCli(['long-running.js', '-T', '1s'], (err, stdout) => {
1212
t.ok(err);
13-
t.match(stdout, /Exited because no new tests completed within the last 1000ms of inactivity/);
13+
t.match(stdout, /Timed out/);
1414
t.end();
1515
});
1616
});
1717

18+
// FIXME: This test fails in CI, but not locally. Re-enable at some point…
19+
// test('interrupt', t => {
20+
// const proc = execCli(['long-running.js'], (_, stdout) => {
21+
// t.match(stdout, /SIGINT/);
22+
// t.end();
23+
// });
24+
//
25+
// setTimeout(() => {
26+
// proc.kill('SIGINT');
27+
// }, 2000);
28+
// });
29+
1830
test('include anonymous functions in error reports', t => {
1931
execCli('error-in-anonymous-function.js', (err, stdout) => {
2032
t.ok(err);

‎test/reporters/verbose.timeoutinmultiplefiles.log

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
✔ a › a passes two
66
---tty-stream-chunk-separator
77

8-
✖ Exited because no new tests completed within the last 10000ms of inactivity
8+
✖ Timed out while running tests
99

10-
2 tests still running in ~/test/fixture/report/timeoutinmultiplefiles/a.js:
10+
2 tests were pending in ~/test/fixture/report/timeoutinmultiplefiles/a.js
1111

1212
◌ a › a slow
1313
◌ a › a slow two
@@ -18,9 +18,9 @@
1818
✔ b › b passes two
1919
---tty-stream-chunk-separator
2020

21-
✖ Exited because no new tests completed within the last 10000ms of inactivity
21+
✖ Timed out while running tests
2222

23-
3 tests still running in ~/test/fixture/report/timeoutinmultiplefiles/b.js:
23+
3 tests were pending in ~/test/fixture/report/timeoutinmultiplefiles/b.js
2424

2525
◌ b › b slow
2626
◌ b › b slow two

‎test/reporters/verbose.timeoutinsinglefile.log

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
✔ passes two
66
---tty-stream-chunk-separator
77

8-
✖ Exited because no new tests completed within the last 10000ms of inactivity
8+
✖ Timed out while running tests
99

10-
2 tests still running in ~/test/fixture/report/timeoutinsinglefile/a.js:
10+
2 tests were pending in ~/test/fixture/report/timeoutinsinglefile/a.js
1111

1212
◌ slow
1313
◌ slow two

0 commit comments

Comments
 (0)
Please sign in to comment.