diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index 0f2265bb74366a..11cb85fe20ad3a 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -1,5 +1,6 @@ 'use strict'; const { + ArrayPrototypeForEach, ArrayPrototypePush, ArrayPrototypePushApply, ArrayPrototypeShift, @@ -254,6 +255,47 @@ class TestContext { constructor(test) { this.#test = test; + + this.test = (name, options, fn) => { + const overrides = { + __proto__: null, + loc: getCallerLocation(), + }; + + const { plan } = this.#test; + if (plan !== null) { + plan.count(); + } + + const subtest = this.#test.createSubtest( + // eslint-disable-next-line no-use-before-define + Test, name, options, fn, overrides, + ); + + return subtest.start(); + }; + + ArrayPrototypeForEach(['skip', 'todo', 'only'], (keyword) => { + this.test[keyword] = (name, options, fn) => { + const overrides = { + __proto__: null, + [keyword]: true, + loc: getCallerLocation(), + }; + + const { plan } = this.#test; + if (plan !== null) { + plan.count(); + } + + const subtest = this.#test.createSubtest( + // eslint-disable-next-line no-use-before-define + Test, name, options, fn, overrides, + ); + + return subtest.start(); + }; + }); } get signal() { @@ -348,25 +390,6 @@ class TestContext { this.#test.todo(message); } - test(name, options, fn) { - const overrides = { - __proto__: null, - loc: getCallerLocation(), - }; - - const { plan } = this.#test; - if (plan !== null) { - plan.count(); - } - - const subtest = this.#test.createSubtest( - // eslint-disable-next-line no-use-before-define - Test, name, options, fn, overrides, - ); - - return subtest.start(); - } - before(fn, options) { this.#test.createHook('before', fn, { __proto__: null, diff --git a/test/parallel/test-runner-subtest-skip-todo-only.js b/test/parallel/test-runner-subtest-skip-todo-only.js new file mode 100644 index 00000000000000..c01cd974299095 --- /dev/null +++ b/test/parallel/test-runner-subtest-skip-todo-only.js @@ -0,0 +1,46 @@ +'use strict'; +require('../common'); +const assert = require('node:assert'); +const { test } = require('node:test'); +const { isPromise } = require('node:util/types'); + +// Regression test for https://github.com/nodejs/node/issues/50665 +// Ensures that t.test.skip, t.test.todo, and t.test.only are available +// on the subtest context. + +test('subtest context should have test variants', async (t) => { + assert.strictEqual(typeof t.test, 'function'); + assert.strictEqual(typeof t.test.skip, 'function'); + assert.strictEqual(typeof t.test.todo, 'function'); + assert.strictEqual(typeof t.test.only, 'function'); +}); + +test('subtest variants should return promises', async (t) => { + const normal = t.test('normal subtest'); + const skipped = t.test.skip('skipped subtest'); + const todo = t.test.todo('todo subtest'); + + assert(isPromise(normal)); + assert(isPromise(skipped)); + assert(isPromise(todo)); + + await normal; + await skipped; + await todo; +}); + +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.skip, 'function'); + assert.strictEqual(typeof t.test.todo, 'function'); + assert.strictEqual(typeof t.test.only, 'function'); + + await t.test('level 2', async (t) => { + assert.strictEqual(typeof t.test, 'function'); + assert.strictEqual(typeof t.test.skip, 'function'); + assert.strictEqual(typeof t.test.todo, 'function'); + assert.strictEqual(typeof t.test.only, 'function'); + }); + }); +});