fix(node/test): basic support for t.skip and t.todo (#29222)

Adds basic support for `t.skip` and `t.todo`
This commit is contained in:
David Sherret 2025-05-08 18:40:16 -05:00 committed by GitHub
parent 9188c79ab4
commit cd877fd16f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 171 additions and 163 deletions

View file

@ -7,8 +7,9 @@ const {
ArrayPrototypeForEach,
SafePromiseAll,
SafePromisePrototypeFinally,
Symbol,
} = primordials;
import { notImplemented, warnNotImplemented } from "ext:deno_node/_utils.ts";
import { notImplemented } from "ext:deno_node/_utils.ts";
import assert from "node:assert";
const methodsToCopy = [
@ -49,11 +50,20 @@ export function run() {
function noop() {}
const skippedSymbol = Symbol("skipped");
class NodeTestContext {
#denoContext: Deno.TestContext;
#parent: NodeTestContext | undefined;
#skipped = false;
constructor(t: Deno.TestContext) {
constructor(t: Deno.TestContext, parent: NodeTestContext | undefined) {
this.#denoContext = t;
this.#parent = parent;
}
get [skippedSymbol]() {
return this.#skipped || (this.#parent?.[skippedSymbol] ?? false);
}
get assert() {
@ -86,23 +96,34 @@ class NodeTestContext {
}
skip() {
warnNotImplemented("test.TestContext.skip");
this.#skipped = true;
return null;
}
todo() {
warnNotImplemented("test.TestContext.todo");
this.#skipped = true;
return null;
}
test(name, options, fn) {
const prepared = prepareOptions(name, options, fn, {});
// deno-lint-ignore no-this-alias
const parentContext = this;
return PromisePrototypeThen(
this.#denoContext.step({
name: prepared.name,
fn: async (denoTestContext) => {
const newNodeTextContext = new NodeTestContext(denoTestContext);
const newNodeTextContext = new NodeTestContext(
denoTestContext,
parentContext,
);
try {
await prepared.fn(newNodeTextContext);
} catch (err) {
if (!newNodeTextContext[skippedSymbol]) {
throw err;
}
}
},
ignore: prepared.options.todo || prepared.options.skip,
sanitizeExit: false,
@ -144,9 +165,20 @@ class TestSuite {
const prepared = prepareOptions(name, options, fn, overrides);
const step = this.#denoTestContext.step({
name: prepared.name,
fn: (denoTestContext) => {
const newNodeTextContext = new NodeTestContext(denoTestContext);
return prepared.fn(newNodeTextContext);
fn: async (denoTestContext) => {
const newNodeTextContext = new NodeTestContext(
denoTestContext,
undefined,
);
try {
return await prepared.fn(newNodeTextContext);
} catch (err) {
if (newNodeTextContext[skippedSymbol]) {
return undefined;
} else {
throw err;
}
}
},
ignore: prepared.options.todo || prepared.options.skip,
sanitizeExit: false,
@ -204,9 +236,13 @@ function prepareOptions(name, options, fn, overrides) {
function wrapTestFn(fn, resolve) {
return async function (t) {
const nodeTestContext = new NodeTestContext(t);
const nodeTestContext = new NodeTestContext(t, undefined);
try {
await fn(nodeTestContext);
} catch (err) {
if (!nodeTestContext[skippedSymbol]) {
throw err;
}
} finally {
resolve();
}

View file

@ -22,6 +22,13 @@ test("sync fail todo", (t) => {
throw new Error("thrown from sync fail todo");
});
test("todo thrown sub test", async (t) => {
t.todo("this is a todo test and is not treated as a failure");
await t.test("test", () => {
throw new Error("this does not fail the test");
});
});
test("sync fail todo with message", (t) => {
t.todo("this is a failing todo");
throw new Error("thrown from sync fail todo with message");
@ -35,6 +42,13 @@ test("sync skip pass with message", (t) => {
t.skip("this is skipped");
});
test("skip thrown sub test", async (t) => {
t.skip("this is a skip test and is not treated as a failure");
await t.test("test", () => {
throw new Error("this does not fail the test");
});
});
test("sync pass", (t) => {
t.diagnostic("this test should pass");
});

View file

@ -1,61 +1,35 @@
[WILDCARD]
running 67 tests from ./test.js
sync pass todo ...
------- output -------
Warning: Not implemented: test.TestContext.todo
----- output end -----
sync pass todo ... ok [WILDCARD]
sync pass todo with message ...
------- output -------
Warning: Not implemented: test.TestContext.todo
----- output end -----
sync pass todo with message ... ok [WILDCARD]
sync fail todo ...
------- output -------
Warning: Not implemented: test.TestContext.todo
----- output end -----
sync fail todo ... FAILED [WILDCARD]
sync fail todo with message ...
------- output -------
Warning: Not implemented: test.TestContext.todo
----- output end -----
sync fail todo with message ... FAILED [WILDCARD]
sync skip pass ...
------- output -------
Warning: Not implemented: test.TestContext.skip
----- output end -----
sync skip pass ... ok [WILDCARD]
sync skip pass with message ...
------- output -------
Warning: Not implemented: test.TestContext.skip
----- output end -----
sync skip pass with message ... ok [WILDCARD]
running 69 tests from ./test.js
sync pass todo ... ok ([WILDLINE])
sync pass todo with message ... ok ([WILDLINE])
sync fail todo ... ok ([WILDLINE])
todo thrown sub test ...
test ... ok ([WILDLINE])
todo thrown sub test ... ok ([WILDLINE])
sync fail todo with message ... ok ([WILDLINE])
sync skip pass ... ok ([WILDLINE])
sync skip pass with message ... ok ([WILDLINE])
skip thrown sub test ...
test ... ok ([WILDLINE])
skip thrown sub test ... ok ([WILDLINE])
sync pass ...
------- output -------
DIAGNOSTIC: this test should pass
----- output end -----
sync pass ... ok [WILDCARD]
sync throw fail ... FAILED [WILDCARD]
async skip pass ...
------- output -------
Warning: Not implemented: test.TestContext.skip
----- output end -----
async skip pass ... ok [WILDCARD]
async pass ... ok [WILDCARD]
async throw fail ... FAILED [WILDCARD]
sync pass ... ok ([WILDLINE])
sync throw fail ... FAILED ([WILDLINE])
async skip pass ... ok ([WILDLINE])
async pass ... ok ([WILDLINE])
async throw fail ... FAILED ([WILDLINE])
nested test ...
nested 1 ...
nested 2 ... ok [WILDCARD]
nested 1 ... ok [WILDCARD]
nested test ... ok [WILDCARD]
async skip fail ...
------- output -------
Warning: Not implemented: test.TestContext.skip
----- output end -----
async skip fail ... FAILED [WILDCARD]
async assertion fail ... FAILED [WILDCARD]
resolve pass ... ok [WILDCARD]
reject fail ... FAILED [WILDCARD]
nested 2 ... ok ([WILDLINE])
nested 1 ... ok ([WILDLINE])
nested test ... ok ([WILDLINE])
async skip fail ... ok ([WILDLINE])
async assertion fail ... FAILED ([WILDLINE])
resolve pass ... ok ([WILDLINE])
reject fail ... FAILED ([WILDLINE])
suite ...
test 1 ... ok ([WILDLINE])
test 2 ... ok ([WILDLINE])
@ -95,82 +69,69 @@ suite ... FAILED (due to 3 failed steps) ([WILDLINE])
assertions available via text context ... ok ([WILDLINE])
unhandled rejection - passes but warns ...
Uncaught error from ./test.js FAILED
unhandled rejection - passes but warns ... cancelled ([WILDCARD])
async unhandled rejection - passes but warns ... cancelled ([WILDCARD])
immediate throw - passes but warns ... cancelled ([WILDCARD])
immediate reject - passes but warns ... cancelled ([WILDCARD])
immediate resolve pass ... cancelled ([WILDCARD])
subtest sync throw fail ... cancelled ([WILDCARD])
sync throw non-error fail ... cancelled ([WILDCARD])
level 0a ... cancelled ([WILDCARD])
top level ... cancelled ([WILDCARD])
invalid subtest - pass but subtest fails ... cancelled ([WILDCARD])
sync skip option ... ignored ([WILDCARD])
sync skip option with message ... cancelled ([WILDCARD])
sync skip option is false fail ... cancelled ([WILDCARD])
noop ... cancelled ([WILDCARD])
functionOnly ... cancelled ([WILDCARD])
<anonymous> ... cancelled ([WILDCARD])
test with only a name provided ... cancelled ([WILDCARD])
noop ... cancelled ([WILDCARD])
noop ... ignored ([WILDCARD])
test with a name and options provided ... ignored ([WILDCARD])
functionAndOptions ... ignored ([WILDCARD])
escaped skip message ... cancelled ([WILDCARD])
escaped todo message ... cancelled ([WILDCARD])
escaped diagnostic ... cancelled ([WILDCARD])
callback pass ... cancelled ([WILDCARD])
callback fail ... cancelled ([WILDCARD])
sync t is this in test ... cancelled ([WILDCARD])
async t is this in test ... cancelled ([WILDCARD])
callback t is this in test ... cancelled ([WILDCARD])
callback also returns a Promise ... cancelled ([WILDCARD])
callback throw ... cancelled ([WILDCARD])
callback called twice ... cancelled ([WILDCARD])
callback called twice in different ticks ... cancelled ([WILDCARD])
callback called twice in future tick ... cancelled ([WILDCARD])
callback async throw ... cancelled ([WILDCARD])
callback async throw after done ... cancelled ([WILDCARD])
custom inspect symbol fail ... cancelled ([WILDCARD])
custom inspect symbol that throws fail ... cancelled ([WILDCARD])
subtest sync throw fails ... cancelled ([WILDCARD])
timed out async test ... cancelled ([WILDCARD])
timed out callback test ... cancelled ([WILDCARD])
large timeout async test is ok ... cancelled ([WILDCARD])
large timeout callback test is ok ... cancelled ([WILDCARD])
successful thenable ... cancelled ([WILDCARD])
rejected thenable ... cancelled ([WILDCARD])
unfinished test with uncaughtException ... cancelled ([WILDCARD])
unfinished test with unhandledRejection ... cancelled ([WILDCARD])
unhandled rejection - passes but warns ... cancelled ([WILDLINE])
async unhandled rejection - passes but warns ... cancelled ([WILDLINE])
immediate throw - passes but warns ... cancelled ([WILDLINE])
immediate reject - passes but warns ... cancelled ([WILDLINE])
immediate resolve pass ... cancelled ([WILDLINE])
subtest sync throw fail ... cancelled ([WILDLINE])
sync throw non-error fail ... cancelled ([WILDLINE])
level 0a ... cancelled ([WILDLINE])
top level ... cancelled ([WILDLINE])
invalid subtest - pass but subtest fails ... cancelled ([WILDLINE])
sync skip option ... ignored ([WILDLINE])
sync skip option with message ... cancelled ([WILDLINE])
sync skip option is false fail ... cancelled ([WILDLINE])
noop ... cancelled ([WILDLINE])
functionOnly ... cancelled ([WILDLINE])
<anonymous> ... cancelled ([WILDLINE])
test with only a name provided ... cancelled ([WILDLINE])
noop ... cancelled ([WILDLINE])
noop ... ignored ([WILDLINE])
test with a name and options provided ... ignored ([WILDLINE])
functionAndOptions ... ignored ([WILDLINE])
escaped skip message ... cancelled ([WILDLINE])
escaped todo message ... cancelled ([WILDLINE])
escaped diagnostic ... cancelled ([WILDLINE])
callback pass ... cancelled ([WILDLINE])
callback fail ... cancelled ([WILDLINE])
sync t is this in test ... cancelled ([WILDLINE])
async t is this in test ... cancelled ([WILDLINE])
callback t is this in test ... cancelled ([WILDLINE])
callback also returns a Promise ... cancelled ([WILDLINE])
callback throw ... cancelled ([WILDLINE])
callback called twice ... cancelled ([WILDLINE])
callback called twice in different ticks ... cancelled ([WILDLINE])
callback called twice in future tick ... cancelled ([WILDLINE])
callback async throw ... cancelled ([WILDLINE])
callback async throw after done ... cancelled ([WILDLINE])
custom inspect symbol fail ... cancelled ([WILDLINE])
custom inspect symbol that throws fail ... cancelled ([WILDLINE])
subtest sync throw fails ... cancelled ([WILDLINE])
timed out async test ... cancelled ([WILDLINE])
timed out callback test ... cancelled ([WILDLINE])
large timeout async test is ok ... cancelled ([WILDLINE])
large timeout callback test is ok ... cancelled ([WILDLINE])
successful thenable ... cancelled ([WILDLINE])
rejected thenable ... cancelled ([WILDLINE])
unfinished test with uncaughtException ... cancelled ([WILDLINE])
unfinished test with unhandledRejection ... cancelled ([WILDLINE])
ERRORS
sync fail todo => ./test.js:20:1
error: Error: thrown from sync fail todo
throw new Error("thrown from sync fail todo");
[WILDCARD]
sync fail todo with message => ./test.js:25:1
error: Error: thrown from sync fail todo with message
throw new Error("thrown from sync fail todo with message");
[WILDCARD]
sync throw fail => ./test.js:42:1
sync throw fail => [WILDLINE]
error: Error: thrown from sync throw fail
throw new Error("thrown from sync throw fail");
[WILDCARD]
^
at [WILDCARD]
async throw fail => ./test.js:53:1
async throw fail => [WILDLINE]
error: Error: thrown from async throw fail
throw new Error("thrown from async throw fail");
[WILDCARD]
^
at [WILDCARD]
async skip fail => ./test.js:64:1
error: Error: thrown from async throw fail
throw new Error("thrown from async throw fail");
[WILDCARD]
async assertion fail => ./test.js:69:1
async assertion fail => [WILDLINE]
error: AssertionError: Values are not strictly equal:
@ -182,25 +143,25 @@ error: AssertionError: Values are not strictly equal:
at [WILDCARD]
reject fail => ./test.js:78:1
reject fail => [WILDLINE]
error: Error: rejected from reject fail
return Promise.reject(new Error("rejected from reject fail"));
^
at [WILDCARD]
suite ... test 2 => ./test.js:[WILDLINE]
suite ... test 2 => [WILDLINE]
error: Error: thrown from test 2
throw new Error("thrown from test 2");
^
at [WILDCARD]
suite ... sub suite 1 ... nested test 2 => ./test.js:[WILDLINE]
suite ... sub suite 1 ... nested test 2 => [WILDLINE]
error: Error: thrown from nested test 2
throw new Error("thrown from nested test 2");
^
at [WILDCARD]
suite ... sub suite 2 ... nested test 1 => ./test.js:[WILDLINE]
suite ... sub suite 2 ... nested test 1 => [WILDLINE]
error: Error: thrown from nested test 1
throw new Error("thrown from nested test 1");
^
@ -216,18 +177,15 @@ It most likely originated from a dangling promise, event/timeout handler or top-
FAILURES
sync fail todo => ./test.js:20:1
sync fail todo with message => ./test.js:25:1
sync throw fail => ./test.js:42:1
async throw fail => ./test.js:53:1
async skip fail => ./test.js:64:1
async assertion fail => ./test.js:69:1
reject fail => ./test.js:78:1
suite ... test 2 => ./test.js:[WILDLINE]
suite ... sub suite 1 ... nested test 2 => ./test.js:[WILDLINE]
suite ... sub suite 2 ... nested test 1 => ./test.js:[WILDLINE]
sync throw fail => [WILDLINE]
async throw fail => [WILDLINE]
async assertion fail => [WILDLINE]
reject fail => [WILDLINE]
suite ... test 2 => [WILDLINE]
suite ... sub suite 1 ... nested test 2 => [WILDLINE]
suite ... sub suite 2 ... nested test 1 => [WILDLINE]
./test.js (uncaught error)
FAILED | 12 passed (21 steps) | 52 failed (5 steps) | 4 ignored [WILDCARD]
FAILED | 17 passed (23 steps) | 49 failed (5 steps) | 4 ignored ([WILDLINE])
error: Test failed

View file

@ -1,40 +1,40 @@
running 6 tests from [WILDCARD]/main.ts
foo ... FAILED ([WILDCARD])
bar ... FAILED ([WILDCARD])
baz ... FAILED ([WILDCARD])
qux ... FAILED ([WILDCARD])
quux ... FAILED ([WILDCARD])
quuz ... FAILED ([WILDCARD])
running 6 tests from [WILDLINE]/main.ts
foo ... FAILED ([WILDLINE])
bar ... FAILED ([WILDLINE])
baz ... FAILED ([WILDLINE])
qux ... FAILED ([WILDLINE])
quux ... FAILED ([WILDLINE])
quuz ... FAILED ([WILDLINE])
ERRORS
foo => [WILDCARD]/main.ts:1:6
foo => [WILDLINE]/main.ts:1:6
error: undefined
bar => [WILDCARD]/main.ts:5:6
bar => [WILDLINE]/main.ts:5:6
error: null
baz => [WILDCARD]/main.ts:9:6
baz => [WILDLINE]/main.ts:9:6
error: 123
qux => [WILDCARD]/main.ts:13:6
qux => [WILDLINE]/main.ts:13:6
error: "Hello, world!"
quux => [WILDCARD]/main.ts:17:6
quux => [WILDLINE]/main.ts:17:6
error: [ 1, 2, 3 ]
quuz => [WILDCARD]/main.ts:21:6
quuz => [WILDLINE]/main.ts:21:6
error: { a: "Hello, world!", b: [ 1, 2, 3 ] }
FAILURES
foo => [WILDCARD]/main.ts:1:6
bar => [WILDCARD]/main.ts:5:6
baz => [WILDCARD]/main.ts:9:6
qux => [WILDCARD]/main.ts:13:6
quux => [WILDCARD]/main.ts:17:6
quuz => [WILDCARD]/main.ts:21:6
foo => [WILDLINE]/main.ts:1:6
bar => [WILDLINE]/main.ts:5:6
baz => [WILDLINE]/main.ts:9:6
qux => [WILDLINE]/main.ts:13:6
quux => [WILDLINE]/main.ts:17:6
quuz => [WILDLINE]/main.ts:21:6
FAILED | 0 passed | 6 failed ([WILDCARD])
FAILED | 0 passed | 6 failed ([WILDLINE])
error: Test failed