Skip to content

Commit 8f312c0

Browse files
bunysaenovemberborn
andauthoredApr 4, 2020
Add t.passed to execution contexts
In `afterEach` hooks, consult `t.passed` to determine whether the test passed. Fixes #840. Co-authored-by: Mark Wubben <mark@novemberborn.net>
1 parent ede4f32 commit 8f312c0

File tree

5 files changed

+110
-9
lines changed

5 files changed

+110
-9
lines changed
 

‎docs/02-execution-context.md

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ The test title.
2222

2323
Contains shared state from hooks.
2424

25+
## `t.passed`
26+
27+
Whether a test has passed. This value is only accurate in the `test.afterEach()` and `test.afterEach.always()` hooks.
28+
2529
## `t.plan(count)`
2630

2731
Plan how many assertion there are in the test. The test will fail if the actual assertion count doesn't match the number of planned assertions. See [assertion planning](./03-assertions.md#assertion-planning).

‎index.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ export interface ExecutionContext<Context = unknown> extends Assertions {
308308
/** Title of the test or hook. */
309309
readonly title: string;
310310

311+
/** Whether the test has passed. Only accurate in afterEach hooks. */
312+
readonly passed: boolean;
313+
311314
log: LogFn;
312315
plan: PlanFn;
313316
timeout: TimeoutFn;

‎lib/runner.js

+14-9
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class Runner extends Emittery {
266266
return result;
267267
}
268268

269-
async runHooks(tasks, contextRef, titleSuffix) {
269+
async runHooks(tasks, contextRef, titleSuffix, testPassed) {
270270
const hooks = tasks.map(task => new Runnable({
271271
contextRef,
272272
experiments: this.experiments,
@@ -278,7 +278,8 @@ class Runner extends Emittery {
278278
updateSnapshots: this.updateSnapshots,
279279
metadata: task.metadata,
280280
powerAssert: this.powerAssert,
281-
title: `${task.title}${titleSuffix || ''}`
281+
title: `${task.title}${titleSuffix || ''}`,
282+
testPassed
282283
}));
283284
const outcome = await this.runMultiple(hooks, this.serial);
284285
for (const result of outcome.storedResults) {
@@ -304,10 +305,11 @@ class Runner extends Emittery {
304305
}
305306

306307
async runTest(task, contextRef) {
307-
let hooksAndTestOk = false;
308-
309308
const hookSuffix = ` for ${task.title}`;
310-
if (await this.runHooks(this.tasks.beforeEach, contextRef, hookSuffix)) {
309+
let hooksOk = await this.runHooks(this.tasks.beforeEach, contextRef, hookSuffix);
310+
311+
let testOk = false;
312+
if (hooksOk) {
311313
// Only run the test if all `beforeEach` hooks passed.
312314
const test = new Runnable({
313315
contextRef,
@@ -325,15 +327,18 @@ class Runner extends Emittery {
325327
});
326328

327329
const result = await this.runSingle(test);
328-
if (result.passed) {
330+
testOk = result.passed;
331+
332+
if (testOk) {
329333
this.emit('stateChange', {
330334
type: 'test-passed',
331335
title: result.title,
332336
duration: result.duration,
333337
knownFailing: result.metadata.failing,
334338
logs: result.logs
335339
});
336-
hooksAndTestOk = await this.runHooks(this.tasks.afterEach, contextRef, hookSuffix);
340+
341+
hooksOk = await this.runHooks(this.tasks.afterEach, contextRef, hookSuffix, testOk);
337342
} else {
338343
this.emit('stateChange', {
339344
type: 'test-failed',
@@ -347,8 +352,8 @@ class Runner extends Emittery {
347352
}
348353
}
349354

350-
const alwaysOk = await this.runHooks(this.tasks.afterEachAlways, contextRef, hookSuffix);
351-
return hooksAndTestOk && alwaysOk;
355+
const alwaysOk = await this.runHooks(this.tasks.afterEachAlways, contextRef, hookSuffix, testOk);
356+
return alwaysOk && hooksOk && testOk;
352357
}
353358

354359
async start() {

‎lib/test.js

+5
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ class ExecutionContext extends assert.Assertions {
174174
testMap.get(this).contextRef.set(context);
175175
}
176176

177+
get passed() {
178+
return testMap.get(this).testPassed;
179+
}
180+
177181
_throwsArgStart(assertion, file, line) {
178182
testMap.get(this).trackThrows({assertion, file, line});
179183
}
@@ -192,6 +196,7 @@ class Test {
192196
this.metadata = options.metadata;
193197
this.powerAssert = options.powerAssert;
194198
this.title = options.title;
199+
this.testPassed = options.testPassed;
195200
this.registerUniqueTitle = options.registerUniqueTitle;
196201
this.logs = [];
197202

‎test-tap/hooks.js

+84
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,90 @@ test('afterEach.always run even if beforeEach failed', t => {
371371
});
372372
});
373373

374+
test('afterEach: property `passed` of execution-context is false when test failed and true when test passed', t => {
375+
t.plan(1);
376+
377+
const passed = [];
378+
let i;
379+
return promiseEnd(new Runner(), runner => {
380+
runner.chain.afterEach(a => {
381+
passed[i] = a.passed;
382+
});
383+
384+
runner.chain('failure', () => {
385+
i = 0;
386+
throw new Error('something went wrong');
387+
});
388+
runner.chain('pass', a => {
389+
i = 1;
390+
a.pass();
391+
});
392+
}).then(() => {
393+
t.strictDeepEqual(passed, [undefined, true]);
394+
});
395+
});
396+
397+
test('afterEach.always: property `passed` of execution-context is false when test failed and true when test passed', t => {
398+
t.plan(1);
399+
400+
const passed = [];
401+
let i;
402+
return promiseEnd(new Runner(), runner => {
403+
runner.chain.afterEach.always(a => {
404+
passed[i] = a.passed;
405+
});
406+
407+
runner.chain('failure', () => {
408+
i = 0;
409+
throw new Error('something went wrong');
410+
});
411+
runner.chain('pass', a => {
412+
i = 1;
413+
a.pass();
414+
});
415+
}).then(() => {
416+
t.strictDeepEqual(passed, [false, true]);
417+
});
418+
});
419+
420+
test('afterEach.always: property `passed` of execution-context is false when before hook failed', t => {
421+
t.plan(1);
422+
423+
let passed;
424+
return promiseEnd(new Runner(), runner => {
425+
runner.chain.before(() => {
426+
throw new Error('something went wrong');
427+
});
428+
runner.chain.afterEach.always(a => {
429+
passed = a.passed;
430+
});
431+
runner.chain('pass', a => {
432+
a.pass();
433+
});
434+
}).then(() => {
435+
t.false(passed);
436+
});
437+
});
438+
439+
test('afterEach.always: property `passed` of execution-context is true when test passed and afterEach hook failed', t => {
440+
t.plan(1);
441+
442+
let passed;
443+
return promiseEnd(new Runner(), runner => {
444+
runner.chain.afterEach(() => {
445+
throw new Error('something went wrong');
446+
});
447+
runner.chain.afterEach.always(a => {
448+
passed = a.passed;
449+
});
450+
runner.chain('pass', a => {
451+
a.pass();
452+
});
453+
}).then(() => {
454+
t.true(passed);
455+
});
456+
});
457+
374458
test('ensure hooks run only around tests', t => {
375459
t.plan(1);
376460

0 commit comments

Comments
 (0)
Please sign in to comment.