Skip to content

Commit 2f49206

Browse files
committed
test_runner: before and after each hooks
1 parent dabda03 commit 2f49206

File tree

8 files changed

+222
-145
lines changed

8 files changed

+222
-145
lines changed

lib/internal/test_runner/harness.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,20 @@ function runInParentContext(Factory) {
170170
return cb;
171171
}
172172

173+
function afterEach(fn, options) {
174+
const parent = testResources.get(executionAsyncId()) || setup(root);
175+
parent.createBeforeEachHook(fn, options);
176+
177+
}
178+
function beforeEach(fn, options) {
179+
const parent = testResources.get(executionAsyncId()) || setup(root);
180+
parent.createBeforeEachHook(fn, options);
181+
}
182+
173183
module.exports = {
174184
test: FunctionPrototypeBind(test, root),
175185
describe: runInParentContext(Suite),
176186
it: runInParentContext(ItTest),
187+
afterEach,
188+
beforeEach,
177189
};

lib/internal/test_runner/test.js

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ class TestContext {
6666
this.#test = test;
6767
}
6868

69+
get name() {
70+
return this.#test.name;
71+
}
72+
6973
diagnostic(message) {
7074
this.#test.diagnostic(message);
7175
}
@@ -88,6 +92,14 @@ class TestContext {
8892

8993
return subtest.start();
9094
}
95+
96+
beforeEach(fn, options) {
97+
this.#test.createBeforeEachHook(fn, options);
98+
}
99+
100+
afterEach(fn, options) {
101+
this.#test.createAfterEachHook(fn, options);
102+
}
91103
}
92104

93105
class Test extends AsyncResource {
@@ -165,6 +177,8 @@ class Test extends AsyncResource {
165177
this.pendingSubtests = [];
166178
this.readySubtests = new SafeMap();
167179
this.subtests = [];
180+
this.beforeEachHooks = [];
181+
this.afterEachHooks = [];
168182
this.waitingOn = 0;
169183
this.finished = false;
170184
}
@@ -267,6 +281,18 @@ class Test extends AsyncResource {
267281
return test;
268282
}
269283

284+
createBeforeEachHook(fn, options) {
285+
const hook = new TestHook(fn, options);
286+
ArrayPrototypePush(this.beforeEachHooks, hook);
287+
return hook;
288+
}
289+
290+
createAfterEachHook(fn, options) {
291+
const hook = new TestHook(fn, options);
292+
ArrayPrototypePush(this.afterEachHooks, hook);
293+
return hook;
294+
}
295+
270296
cancel() {
271297
if (this.endTime !== null) {
272298
return;
@@ -278,6 +304,7 @@ class Test extends AsyncResource {
278304
kCancelledByParent
279305
)
280306
);
307+
this.startTime = this.startTime || this.endTime; // if a test was canceled before it was started, e.g inside a hook
281308
this.cancelled = true;
282309
}
283310

@@ -334,12 +361,24 @@ class Test extends AsyncResource {
334361
return { ctx, args: [ctx] };
335362
}
336363

337-
async run() {
338-
this.parent.activeSubtests++;
364+
async #runHooks(hooks) {
365+
await ArrayPrototypeReduce(hooks, async (prev, hook) => {
366+
await prev;
367+
await hook.run(this.getRunArgs());
368+
}, PromiseResolve());
369+
}
370+
371+
async run(...runArgs) {
372+
if (this.parent !== null) {
373+
this.parent.activeSubtests++;
374+
}
375+
if (this.parent?.beforeEachHooks.length > 0) {
376+
await this.#runHooks(this.parent.beforeEachHooks);
377+
}
339378
this.startTime = hrtime();
340379

341380
try {
342-
const { args, ctx } = this.getRunArgs();
381+
const { args, ctx } = ReflectApply(this.getRunArgs, this, runArgs);
343382
ArrayPrototypeUnshift(args, this.fn, ctx); // Note that if it's not OK to mutate args, we need to first clone it.
344383

345384
if (this.fn.length === args.length - 1) {
@@ -372,9 +411,13 @@ class Test extends AsyncResource {
372411
}
373412
}
374413

414+
if (this.parent?.afterEachHooks.length > 0) {
415+
await this.#runHooks(this.parent.afterEachHooks);
416+
}
417+
375418
// Clean up the test. Then, try to report the results and execute any
376419
// tests that were pending due to available concurrency.
377-
this.postRun();
420+
await this.postRun();
378421
}
379422

380423
postRun() {
@@ -413,7 +456,7 @@ class Test extends AsyncResource {
413456
this.parent.activeSubtests--;
414457
this.parent.addReadySubtest(this);
415458
this.parent.processReadySubtestRange(false);
416-
this.parent.processPendingSubtests();
459+
return this.parent.processPendingSubtests();
417460
}
418461
}
419462

@@ -473,10 +516,23 @@ class Test extends AsyncResource {
473516
}
474517
}
475518

519+
class TestHook extends Test {
520+
constructor(fn, options) {
521+
if (options === null || typeof options !== 'object') {
522+
options = kEmptyObject;
523+
}
524+
super({ fn, ...options });
525+
}
526+
getRunArgs(testContext) {
527+
return testContext;
528+
}
529+
}
530+
476531
class ItTest extends Test {
477532
constructor(opt) { super(opt); } // eslint-disable-line no-useless-constructor
478533
getRunArgs() {
479-
return { ctx: {}, args: [] };
534+
const ctx = new TestContext(this);
535+
return { ctx, args: [] };
480536
}
481537
}
482538
class Suite extends Test {

lib/test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict';
2-
const { test, describe, it } = require('internal/test_runner/harness');
2+
const { test, describe, it, afterEach, beforeEach } = require('internal/test_runner/harness');
33
const { emitExperimentalWarning } = require('internal/util');
44

55
emitExperimentalWarning('The test runner');
@@ -8,3 +8,5 @@ module.exports = test;
88
module.exports.test = test;
99
module.exports.describe = describe;
1010
module.exports.it = it;
11+
module.exports.afterEach = afterEach;
12+
module.exports.beforeEach = beforeEach;

test/message/test_runner_describe_it.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -224,19 +224,6 @@ it('callback fail', (done) => {
224224
});
225225
});
226226

227-
it('sync t is this in test', function() {
228-
assert.deepStrictEqual(this, {});
229-
});
230-
231-
it('async t is this in test', async function() {
232-
assert.deepStrictEqual(this, {});
233-
});
234-
235-
it('callback t is this in test', function(done) {
236-
assert.deepStrictEqual(this, {});
237-
done();
238-
});
239-
240227
it('callback also returns a Promise', async (done) => {
241228
throw new Error('thrown from callback also returns a Promise');
242229
});

0 commit comments

Comments
 (0)