From e6113236671b4ba2a4ce7da0af506fdf412f2e89 Mon Sep 17 00:00:00 2001 From: Jacob Smith <3012099+JakobJingleheimer@users.noreply.github.com> Date: Sun, 15 Mar 2026 14:20:24 +0100 Subject: [PATCH 1/2] test_runner: add context subtests `expectFailure` `only` `skip` `todo` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Heath Dutton🕴️ Co-Authored-By: Felipe <60716370+felipeness@users.noreply.github.com> --- lib/internal/test_runner/test.js | 15 ++++-- test/parallel/test-runner-subtest-methods.js | 53 ++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-runner-subtest-methods.js diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index 2203bbd3497659..f3b9ba26bb610c 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -14,6 +14,7 @@ const { MathMax, Number, NumberPrototypeToFixed, + ObjectAssign, ObjectKeys, ObjectSeal, Promise, @@ -358,11 +359,11 @@ class TestContext { this.#test.todo(message); } - test(name, options, fn) { - const overrides = { + #runTest(name, options, fn, extraOverrides) { + const overrides = ObjectAssign({ __proto__: null, loc: getCallerLocation(), - }; + }, extraOverrides); const { plan } = this.#test; if (plan !== null) { @@ -377,6 +378,14 @@ class TestContext { return subtest.start(); } + test = ObjectAssign((...args) => this.#runTest(...args), { + __proto__: null, + expectFailure: (name, opts, fn) => this.#runTest(name, opts, fn, { __proto__: null, expectFailure: true }), + only: (name, opts, fn) => this.#runTest(name, opts, fn, { __proto__: null, only: true }), + skip: (name, opts, fn) => this.#runTest(name, opts, fn, { __proto__: null, skip: true }), + todo: (name, opts, fn) => this.#runTest(name, opts, fn, { __proto__: null, todo: true }), + }); + before(fn, options) { this.#test.createHook('before', fn, { __proto__: null, diff --git a/test/parallel/test-runner-subtest-methods.js b/test/parallel/test-runner-subtest-methods.js new file mode 100644 index 00000000000000..08ddef85558798 --- /dev/null +++ b/test/parallel/test-runner-subtest-methods.js @@ -0,0 +1,53 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const { test } = require('node:test'); +const { isPromise } = require('node:util/types'); + +test('subtest context should have test variants', async (t) => { + assert.strictEqual(typeof t.test, 'function'); + assert.strictEqual(typeof t.test.expectFailure, 'function'); + assert.strictEqual(typeof t.test.only, 'function'); + assert.strictEqual(typeof t.test.skip, 'function'); + assert.strictEqual(typeof t.test.todo, 'function'); +}); + +test('subtest should return a promise', async (t) => { + const normal = t.test('normal subtest'); + assert.ok(isPromise(normal)); + await normal; +}); + +test('t.test[variant]() should return a promise', async (t) => { + assert.ok(isPromise( + t.test.expectFailure('expectFailure subtest', () => { throw new Error('This should pass'); }) + )); + assert.ok(isPromise( + t.test.only('only subtest') + )); + assert.ok(isPromise( + t.test.skip('skip subtest') + )); + assert.ok(isPromise( + t.test.todo('todo subtest') + )); +}); + +test('nested subtests should have test variants', async (t) => { + await t.test('level 1', async (t) => { + assert.strictEqual(typeof t.test, 'function'); + assert.strictEqual(typeof t.test.expectFailure, 'function'); + assert.strictEqual(typeof t.test.only, 'function'); + assert.strictEqual(typeof t.test.skip, 'function'); + assert.strictEqual(typeof t.test.todo, 'function'); + + await t.test('level 2', async (t) => { + assert.strictEqual(typeof t.test, 'function'); + assert.strictEqual(typeof t.test.expectFailure, 'function'); + assert.strictEqual(typeof t.test.skip, 'function'); + assert.strictEqual(typeof t.test.todo, 'function'); + assert.strictEqual(typeof t.test.only, 'function'); + }); + }); +}); From d5e777d6c7ff623db15d18a831ec52ee240f5fb2 Mon Sep 17 00:00:00 2001 From: Jacob Smith <3012099+JakobJingleheimer@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:06:47 +0100 Subject: [PATCH 2/2] fixup!: try `await`ing subtests --- test/parallel/test-runner-subtest-methods.js | 26 +++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/parallel/test-runner-subtest-methods.js b/test/parallel/test-runner-subtest-methods.js index 08ddef85558798..e43e6d9595268a 100644 --- a/test/parallel/test-runner-subtest-methods.js +++ b/test/parallel/test-runner-subtest-methods.js @@ -20,18 +20,20 @@ test('subtest should return a promise', async (t) => { }); test('t.test[variant]() should return a promise', async (t) => { - assert.ok(isPromise( - t.test.expectFailure('expectFailure subtest', () => { throw new Error('This should pass'); }) - )); - assert.ok(isPromise( - t.test.only('only subtest') - )); - assert.ok(isPromise( - t.test.skip('skip subtest') - )); - assert.ok(isPromise( - t.test.todo('todo subtest') - )); + const xfail = t.test.expectFailure('expectFailure subtest', () => { throw new Error('This should pass'); }); + const only = t.test.only('only subtest'); + const skip = t.test.skip('skip subtest'); + const todo = t.test.todo('todo subtest'); + + assert.ok(isPromise(xfail)); + assert.ok(isPromise(only)); + assert.ok(isPromise(skip)); + assert.ok(isPromise(todo)); + + await xfail; + await only; + await skip; + await todo; }); test('nested subtests should have test variants', async (t) => {