mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
2063 lines
62 KiB
TypeScript
2063 lines
62 KiB
TypeScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
import {
|
|
assert,
|
|
assertEquals,
|
|
assertObjectMatch,
|
|
assertRejects,
|
|
assertStrictEquals,
|
|
assertThrows,
|
|
} from "@std/assert";
|
|
import {
|
|
after,
|
|
afterAll,
|
|
afterEach,
|
|
before,
|
|
beforeAll,
|
|
beforeEach,
|
|
describe,
|
|
it,
|
|
} from "./bdd.ts";
|
|
import { TestSuiteInternal } from "./_test_suite.ts";
|
|
import { assertSpyCall, assertSpyCalls, type Spy, spy, stub } from "./mock.ts";
|
|
|
|
class TestContext implements Deno.TestContext {
|
|
name: string;
|
|
origin: string;
|
|
steps: TestContext[];
|
|
spies: {
|
|
step: Spy;
|
|
};
|
|
|
|
constructor(name: string) {
|
|
this.name = name;
|
|
this.origin = "origin";
|
|
this.spies = {
|
|
step: spy(this, "step"),
|
|
};
|
|
this.steps = [];
|
|
}
|
|
|
|
async step(t: Deno.TestStepDefinition): Promise<boolean>;
|
|
async step(
|
|
name: string,
|
|
fn: (t: Deno.TestContext) => void | Promise<void>,
|
|
): Promise<boolean>;
|
|
async step(
|
|
fn: (t: Deno.TestContext) => void | Promise<void>,
|
|
): Promise<boolean>;
|
|
async step(
|
|
tOrNameOrFn:
|
|
| Deno.TestStepDefinition
|
|
| string
|
|
| ((t: Deno.TestContext) => void | Promise<void>),
|
|
fn?: (t: Deno.TestContext) => void | Promise<void>,
|
|
): Promise<boolean> {
|
|
let ignore = false;
|
|
if (typeof tOrNameOrFn === "function") {
|
|
ignore = false;
|
|
fn = tOrNameOrFn;
|
|
} else if (typeof tOrNameOrFn === "object") {
|
|
ignore = tOrNameOrFn.ignore ?? false;
|
|
fn = tOrNameOrFn.fn;
|
|
}
|
|
|
|
const name = typeof tOrNameOrFn === "string"
|
|
? tOrNameOrFn
|
|
: tOrNameOrFn.name;
|
|
const context = new TestContext(name);
|
|
this.steps.push(context);
|
|
if (!ignore) {
|
|
await fn!(context);
|
|
}
|
|
return !ignore;
|
|
}
|
|
}
|
|
|
|
const baseStepOptions: Omit<Deno.TestStepDefinition, "name" | "fn"> = {
|
|
ignore: false,
|
|
sanitizeExit: true,
|
|
sanitizeOps: true,
|
|
sanitizeResources: true,
|
|
};
|
|
|
|
const baseOptions: Omit<Deno.TestDefinition, "name" | "fn"> = {
|
|
...baseStepOptions,
|
|
only: false,
|
|
permissions: "inherit",
|
|
};
|
|
|
|
interface GlobalContext {
|
|
allTimer: number;
|
|
eachTimer: number;
|
|
x?: number;
|
|
y?: number;
|
|
}
|
|
|
|
let timerIdx = 1;
|
|
const timers = new Map<number, number>();
|
|
function hookFns() {
|
|
timerIdx = 1;
|
|
timers.clear();
|
|
return {
|
|
beforeAllFn: spy(async function (this: GlobalContext) {
|
|
await Promise.resolve();
|
|
this.allTimer = timerIdx++;
|
|
timers.set(this.allTimer, setTimeout(() => {}, 10000));
|
|
}),
|
|
afterAllFn: spy(async function (this: GlobalContext) {
|
|
await Promise.resolve();
|
|
clearTimeout(timers.get(this.allTimer));
|
|
}),
|
|
beforeEachFn: spy(async function (this: GlobalContext) {
|
|
await Promise.resolve();
|
|
this.eachTimer = timerIdx++;
|
|
timers.set(this.eachTimer, setTimeout(() => {}, 10000));
|
|
}),
|
|
afterEachFn: spy(async function (this: GlobalContext) {
|
|
await Promise.resolve();
|
|
clearTimeout(timers.get(this.eachTimer));
|
|
}),
|
|
};
|
|
}
|
|
|
|
Deno.test("beforeAll(), afterAll(), beforeEach() and afterEach()", async () => {
|
|
using test = stub(Deno, "test");
|
|
const fns = [spy(), spy()] as const;
|
|
const { beforeAllFn, afterAllFn, beforeEachFn, afterEachFn } = hookFns();
|
|
|
|
const context = new TestContext("global");
|
|
try {
|
|
beforeAll(beforeAllFn);
|
|
afterAll(afterAllFn);
|
|
|
|
beforeEach(beforeEachFn);
|
|
afterEach(afterEachFn);
|
|
|
|
assertEquals(it({ name: "example 1", fn: fns[0] }), undefined);
|
|
assertEquals(it({ name: "example 2", fn: fns[1] }), undefined);
|
|
|
|
assertSpyCalls(fns[0], 0);
|
|
assertSpyCalls(fns[1], 0);
|
|
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(Object.keys(options).sort(), ["fn", "name"]);
|
|
assertEquals(options.name, "global");
|
|
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 2);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
|
|
let fn = fns[0];
|
|
assertSpyCall(fn, 0, {
|
|
self: { allTimer: 1, eachTimer: 2 },
|
|
args: [context.steps[0]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
fn = fns[1];
|
|
assertSpyCall(fn, 0, {
|
|
self: { allTimer: 1, eachTimer: 3 },
|
|
args: [context.steps[1]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
assertSpyCalls(beforeAllFn, 1);
|
|
assertSpyCalls(afterAllFn, 1);
|
|
assertSpyCalls(beforeEachFn, 2);
|
|
assertSpyCalls(afterEachFn, 2);
|
|
});
|
|
|
|
Deno.test("it()", async (t) => {
|
|
/**
|
|
* Asserts that `Deno.test` is called with the correct options for the `it` call in the callback function.
|
|
* This is used to reduce code duplication when testing calling `it` with different call signatures.
|
|
*/
|
|
async function assertOptions<T>(
|
|
expectedOptions: Omit<Deno.TestDefinition, "name" | "fn">,
|
|
cb: (fn: Spy) => void,
|
|
) {
|
|
using test = stub(Deno, "test");
|
|
const fn = spy();
|
|
try {
|
|
cb(fn);
|
|
|
|
assertSpyCalls(fn, 0);
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(
|
|
Object.keys(options).sort(),
|
|
["name", "fn", ...Object.keys(expectedOptions)].sort(),
|
|
);
|
|
assertObjectMatch(options, {
|
|
name: "example",
|
|
...expectedOptions,
|
|
});
|
|
|
|
const context = new TestContext("example");
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 0);
|
|
assertSpyCall(fn, 0, {
|
|
self: {},
|
|
args: [context],
|
|
returned: undefined,
|
|
});
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with just the name and function for the `it` call in the callback function.
|
|
* This is used to reduce code duplication when testing calling `it` with different call signatures.
|
|
*/
|
|
async function assertMinimumOptions(
|
|
cb: (fn: Spy) => void,
|
|
) {
|
|
await assertOptions({}, cb);
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with all of the options for the `it` call in the callback function.
|
|
* This is used to reduce code duplication when testing calling `it` with different call signatures.
|
|
*/
|
|
async function assertAllOptions(
|
|
cb: (fn: Spy) => void,
|
|
) {
|
|
await assertOptions(baseOptions, cb);
|
|
}
|
|
|
|
await t.step("signature 1", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it({ name: "example", fn }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it({
|
|
name: "example",
|
|
fn,
|
|
...baseOptions,
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 2", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it("example", { fn }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it("example", {
|
|
fn,
|
|
...baseOptions,
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 3", async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it("example", fn), undefined);
|
|
}));
|
|
|
|
await t.step("signature 4", async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(
|
|
it(function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
|
|
await t.step("signature 5", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it("example", {}, fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it("example", {
|
|
...baseOptions,
|
|
}, fn),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 6", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it({ name: "example" }, fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it({
|
|
name: "example",
|
|
...baseOptions,
|
|
}, fn),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 7", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(
|
|
it({}, function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it({
|
|
...baseOptions,
|
|
}, function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("only", async (t) => {
|
|
/**
|
|
* Asserts that `Deno.test` is called with just the name, only, and function for the `it.only` call in the callback function.
|
|
* This is used to reduce code duplication when testing calling `it.only` with different call signatures.
|
|
*/
|
|
async function assertMinimumOptions(
|
|
cb: (fn: Spy) => void,
|
|
) {
|
|
await assertOptions({ only: true }, cb);
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with all of the options for the `it.only` call in the callback function.
|
|
* This is used to reduce code duplication when testing calling `it.only` with different call signatures.
|
|
*/
|
|
async function assertAllOptions(
|
|
cb: (fn: Spy) => void,
|
|
) {
|
|
await assertOptions({ ...baseOptions, only: true }, cb);
|
|
}
|
|
|
|
await t.step("signature 1", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.only({ name: "example", fn }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.only({
|
|
name: "example",
|
|
fn,
|
|
...baseOptions,
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 2", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.only("example", { fn }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.only("example", {
|
|
fn,
|
|
...baseOptions,
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step(
|
|
"signature 3",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.only("example", fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"signature 4",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(
|
|
it.only(function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 5", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.only("example", {}, fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.only("example", {
|
|
...baseOptions,
|
|
}, fn),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 6", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.only({ name: "example" }, fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.only({
|
|
name: "example",
|
|
...baseOptions,
|
|
}, fn),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 7", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(
|
|
it.only({}, function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.only({
|
|
...baseOptions,
|
|
}, function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
});
|
|
|
|
await t.step("ignore", async (t) => {
|
|
/**
|
|
* Asserts that `Deno.test` is called with just the name, ignore, and function for the `it.ignore` call in the callback function.
|
|
* This is used to reduce code duplication when testing calling `it.ignore` with different call signatures.
|
|
*/
|
|
async function assertMinimumOptions(
|
|
cb: (fn: Spy) => void,
|
|
) {
|
|
await assertOptions({ ignore: true }, cb);
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with all of the options for the `it.ignore` call in the callback function.
|
|
* This is used to reduce code duplication when testing calling `it.ignore` with different call signatures.
|
|
*/
|
|
async function assertAllOptions(
|
|
cb: (fn: Spy) => void,
|
|
) {
|
|
await assertOptions({ ...baseOptions, ignore: true }, cb);
|
|
}
|
|
|
|
await t.step("signature 1", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.ignore({ name: "example", fn }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"minimum options (skip)",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
it.skip({ name: "example", fn });
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.ignore({
|
|
name: "example",
|
|
fn,
|
|
...baseOptions,
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 2", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.ignore("example", { fn }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.ignore("example", {
|
|
fn,
|
|
...baseOptions,
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step(
|
|
"signature 3",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.ignore("example", fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"signature 4",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(
|
|
it.ignore(function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 5", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.ignore("example", {}, fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.ignore("example", {
|
|
...baseOptions,
|
|
}, fn),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 6", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(it.ignore({ name: "example" }, fn), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.ignore({
|
|
name: "example",
|
|
...baseOptions,
|
|
}, fn),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 7", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fn) => {
|
|
assertEquals(
|
|
it.ignore({}, function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fn) => {
|
|
assertEquals(
|
|
it.ignore({
|
|
...baseOptions,
|
|
}, function example(this: void, ...args) {
|
|
fn.apply(this, args);
|
|
}),
|
|
undefined,
|
|
);
|
|
}));
|
|
});
|
|
});
|
|
});
|
|
|
|
Deno.test("describe()", async (t) => {
|
|
/**
|
|
* Asserts that `Deno.test` is called with the correct options for the `describe` call in the callback function.
|
|
* In addition to that, it asserts that the individual test cases registered with `it` use the test step API correctly.
|
|
* This is used to reduce code duplication when testing calling `describe` with different call signatures.
|
|
*/
|
|
async function assertOptions(
|
|
expectedOptions: Omit<Deno.TestDefinition, "name" | "fn">,
|
|
cb: (fns: readonly [Spy, Spy]) => void,
|
|
) {
|
|
using test = stub(Deno, "test");
|
|
const fns = [spy(), spy()] as const;
|
|
try {
|
|
cb(fns);
|
|
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(
|
|
Object.keys(options).sort(),
|
|
["name", "fn", ...Object.keys(expectedOptions)].sort(),
|
|
);
|
|
assertObjectMatch(options, {
|
|
name: "example",
|
|
...expectedOptions,
|
|
});
|
|
|
|
assertSpyCalls(fns[0], 0);
|
|
assertSpyCalls(fns[1], 0);
|
|
|
|
const context = new TestContext("example");
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 2);
|
|
|
|
let fn = fns[0];
|
|
assertSpyCall(fn, 0, {
|
|
self: {},
|
|
args: [context.steps[0]],
|
|
returned: undefined,
|
|
});
|
|
|
|
fn = fns[1];
|
|
assertSpyCall(fn, 0, {
|
|
self: {},
|
|
args: [context.steps[1]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with just the name and function for the `describe` call in the callback function.
|
|
* In addition to that, it asserts that the individual test cases registered with `it` use the test step API correctly.
|
|
* This is used to reduce code duplication when testing calling `describe` with different call signatures.
|
|
*/
|
|
async function assertMinimumOptions(
|
|
cb: (fns: readonly [Spy, Spy]) => void,
|
|
) {
|
|
await assertOptions({}, cb);
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with all of the options for the `describe` call in the callback function.
|
|
* In addition to that, it asserts that the individual test cases registered with `it` use the test step API correctly.
|
|
* This is used to reduce code duplication when testing calling `describe` with different call signatures.
|
|
*/
|
|
async function assertAllOptions(
|
|
cb: (fns: readonly [Spy, Spy]) => void,
|
|
) {
|
|
await assertOptions({ ...baseOptions }, cb);
|
|
}
|
|
|
|
await t.step("signature 1", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe({ name: "example" });
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe({
|
|
name: "example",
|
|
fn: () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
},
|
|
...baseOptions,
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}));
|
|
});
|
|
|
|
await t.step(
|
|
"signature 2",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe("example");
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 3", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe("example", {});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe("example", {
|
|
fn: () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
},
|
|
...baseOptions,
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}));
|
|
});
|
|
|
|
await t.step(
|
|
"signature 4",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe("example", () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"signature 5",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe(function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 6", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe("example", {}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe("example", {
|
|
...baseOptions,
|
|
}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 7", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe({ name: "example" }, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe({
|
|
name: "example",
|
|
...baseOptions,
|
|
}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}));
|
|
});
|
|
|
|
await t.step("signature 8", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe({}, function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("all options", async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe({
|
|
...baseOptions,
|
|
}, function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}));
|
|
});
|
|
|
|
await t.step("only", async (t) => {
|
|
/**
|
|
* Asserts that `Deno.test` is called with just the name, only, and function for the `describe.only` call in the callback function.
|
|
* In addition to that, it asserts that the individual test cases registered with `it` use the test step API correctly.
|
|
* This is used to reduce code duplication when testing calling `describe.only` with different call signatures.
|
|
*/
|
|
async function assertMinimumOptions(
|
|
cb: (fns: readonly [Spy, Spy]) => void,
|
|
) {
|
|
await assertOptions({ only: true }, cb);
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with all of the options for the `describe.only` call in the callback function.
|
|
* In addition to that, it asserts that the individual test cases registered with `it` use the test step API correctly.
|
|
* This is used to reduce code duplication when testing calling `describe.only` with different call signatures.
|
|
*/
|
|
async function assertAllOptions(
|
|
cb: (fns: readonly [Spy, Spy]) => void,
|
|
) {
|
|
await assertOptions({ ...baseOptions, only: true }, cb);
|
|
}
|
|
|
|
await t.step("signature 1", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only({ name: "example" });
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.only({
|
|
name: "example",
|
|
fn: () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
},
|
|
...baseOptions,
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step(
|
|
"signature 2",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only("example");
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 3", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only("example", {});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.only("example", {
|
|
fn: () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
},
|
|
...baseOptions,
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step(
|
|
"signature 4",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only("example", () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"signature 5",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only(function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 6", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only("example", {}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.only("example", {
|
|
...baseOptions,
|
|
}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step("signature 7", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only({ name: "example" }, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.only({
|
|
name: "example",
|
|
...baseOptions,
|
|
}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step("signature 8", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.only({}, function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.only({
|
|
...baseOptions,
|
|
}, function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
await t.step("ignore", async (t) => {
|
|
/**
|
|
* Asserts that `Deno.test` is called with just the name, ignore, and function for the `describe.ignore` call in the callback function.
|
|
* In addition to that, it asserts that the individual test cases registered with `it` use the test step API correctly.
|
|
* This is used to reduce code duplication when testing calling `describe.ignore` with different call signatures.
|
|
*/
|
|
async function assertMinimumOptions(
|
|
cb: (fns: readonly [Spy, Spy]) => void,
|
|
) {
|
|
await assertOptions({ ignore: true }, cb);
|
|
}
|
|
|
|
/**
|
|
* Asserts that `Deno.test` is called with all of the options for the `describe.ignore` call in the callback function.
|
|
* In addition to that, it asserts that the individual test cases registered with `it` use the test step API correctly.
|
|
* This is used to reduce code duplication when testing calling `describe.ignore` with different call signatures.
|
|
*/
|
|
async function assertAllOptions(
|
|
cb: (fns: readonly [Spy, Spy]) => void,
|
|
) {
|
|
await assertOptions({ ...baseOptions, ignore: true }, cb);
|
|
}
|
|
|
|
await t.step("signature 1", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore({ name: "example" });
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"minimum options (skip)",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.skip({ name: "example" });
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
it({ suite, name: "a", fn: fns[0] });
|
|
it({ suite, name: "b", fn: fns[1] });
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.ignore({
|
|
name: "example",
|
|
fn: () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
},
|
|
...baseOptions,
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step(
|
|
"signature 2",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore("example");
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 3", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore("example", {});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.ignore("example", {
|
|
fn: () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
},
|
|
...baseOptions,
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step(
|
|
"signature 4",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore("example", () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"signature 5",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore(function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step("signature 6", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore("example", {}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.ignore("example", {
|
|
...baseOptions,
|
|
}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step("signature 7", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore({ name: "example" }, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.ignore({
|
|
name: "example",
|
|
...baseOptions,
|
|
}, () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step("signature 8", async (t) => {
|
|
await t.step(
|
|
"minimum options",
|
|
async () =>
|
|
await assertMinimumOptions((fns) => {
|
|
const suite = describe.ignore({}, function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
|
|
await t.step(
|
|
"all options",
|
|
async () =>
|
|
await assertAllOptions((fns) => {
|
|
const suite = describe.ignore({
|
|
...baseOptions,
|
|
}, function example() {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
});
|
|
assert(suite && typeof suite.symbol === "symbol");
|
|
assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
await t.step("nested only", async (t) => {
|
|
/**
|
|
* Asserts that when only is used on a nested `describe` or `it` call, it will be the only test case or suite that runs in the file.
|
|
* This is used to reduce code duplication when testing calling `describe.ignore` with different call signatures.
|
|
*/
|
|
async function assertOnly(
|
|
cb: (fns: readonly [Spy, Spy, Spy]) => void,
|
|
) {
|
|
using test = stub(Deno, "test");
|
|
const fns = [spy(), spy(), spy()] as const;
|
|
try {
|
|
cb(fns);
|
|
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(
|
|
Object.keys(options).sort(),
|
|
["name", "only", "fn"].sort(),
|
|
);
|
|
assertObjectMatch(options, {
|
|
name: "example",
|
|
only: true,
|
|
});
|
|
|
|
assertSpyCalls(fns[0], 0);
|
|
assertSpyCalls(fns[1], 0);
|
|
|
|
const context = new TestContext("example");
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 1);
|
|
|
|
let fn = fns[0];
|
|
assertSpyCalls(fn, 0);
|
|
|
|
fn = fns[1];
|
|
assertSpyCall(fn, 0, {
|
|
self: {},
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
fn = fns[2];
|
|
assertSpyCalls(fn, 0);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
}
|
|
|
|
await t.step("it", async () =>
|
|
await assertOnly((fns) => {
|
|
describe("example", () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
assertEquals(it.only({ name: "b", fn: fns[1] }), undefined);
|
|
assertEquals(it({ name: "c", fn: fns[2] }), undefined);
|
|
});
|
|
}));
|
|
|
|
await t.step("nested it", async () =>
|
|
await assertOnly((fns) => {
|
|
describe("example", () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
describe("nested", () => {
|
|
assertEquals(it.only({ name: "b", fn: fns[1] }), undefined);
|
|
});
|
|
assertEquals(it({ name: "c", fn: fns[2] }), undefined);
|
|
});
|
|
}));
|
|
|
|
await t.step("describe", async () =>
|
|
await assertOnly((fns) => {
|
|
describe("example", () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
describe.only("nested", () => {
|
|
assertEquals(it({ name: "b", fn: fns[1] }), undefined);
|
|
});
|
|
assertEquals(it({ name: "c", fn: fns[2] }), undefined);
|
|
});
|
|
}));
|
|
|
|
await t.step("nested describe", async () =>
|
|
await assertOnly((fns) => {
|
|
describe("example", () => {
|
|
assertEquals(it({ name: "a", fn: fns[0] }), undefined);
|
|
describe("nested", () => {
|
|
describe.only("nested 2", () => {
|
|
assertEquals(it({ name: "b", fn: fns[1] }), undefined);
|
|
});
|
|
});
|
|
assertEquals(it({ name: "c", fn: fns[2] }), undefined);
|
|
});
|
|
}));
|
|
});
|
|
|
|
await t.step("flat child only", async (t) => {
|
|
/**
|
|
* Asserts that when only is used on a child `describe` or `it` call, it will be the only test case or suite that runs within the top test suite.
|
|
* This demonstrates the issue where `Deno.test` is called without `only` even though one of it's child steps are focused.
|
|
* This is used to reduce code duplication when testing calling `describe.ignore` with different call signatures.
|
|
*/
|
|
async function assertOnly(
|
|
cb: (fns: readonly [Spy, Spy, Spy]) => void,
|
|
) {
|
|
using test = stub(Deno, "test");
|
|
const fns = [spy(), spy(), spy()] as const;
|
|
try {
|
|
cb(fns);
|
|
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(
|
|
Object.keys(options).sort(),
|
|
["name", "fn"].sort(),
|
|
);
|
|
assertObjectMatch(options, {
|
|
name: "example",
|
|
});
|
|
|
|
assertSpyCalls(fns[0], 0);
|
|
assertSpyCalls(fns[1], 0);
|
|
|
|
const context = new TestContext("example");
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 1);
|
|
|
|
let fn = fns[0];
|
|
assertSpyCalls(fn, 0);
|
|
|
|
fn = fns[1];
|
|
assertSpyCall(fn, 0, {
|
|
self: {},
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
fn = fns[2];
|
|
assertSpyCalls(fn, 0);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
}
|
|
|
|
await t.step("it", async () =>
|
|
await assertOnly((fns) => {
|
|
const suite = describe("example");
|
|
assertEquals(it({ name: "a", suite, fn: fns[0] }), undefined);
|
|
assertEquals(it.only({ name: "b", suite, fn: fns[1] }), undefined);
|
|
assertEquals(it({ name: "c", suite, fn: fns[2] }), undefined);
|
|
}));
|
|
|
|
await t.step("deep child it", async () =>
|
|
await assertOnly((fns) => {
|
|
const suite = describe("example");
|
|
assertEquals(it({ name: "a", suite, fn: fns[0] }), undefined);
|
|
const childSuite = describe(suite, "child");
|
|
assertEquals(
|
|
it.only({ name: "b", suite: childSuite, fn: fns[1] }),
|
|
undefined,
|
|
);
|
|
assertEquals(it({ name: "c", suite, fn: fns[2] }), undefined);
|
|
}));
|
|
|
|
await t.step("describe", async () =>
|
|
await assertOnly((fns) => {
|
|
const suite = describe("example");
|
|
assertEquals(it({ name: "a", suite, fn: fns[0] }), undefined);
|
|
const childSuite = describe.only(suite, "child");
|
|
assertEquals(
|
|
it({ name: "b", suite: childSuite, fn: fns[1] }),
|
|
undefined,
|
|
);
|
|
assertEquals(it({ name: "c", suite, fn: fns[2] }), undefined);
|
|
}));
|
|
|
|
await t.step(
|
|
"deep child describe",
|
|
async () =>
|
|
await assertOnly((fns) => {
|
|
const suite = describe("example");
|
|
assertEquals(it({ name: "a", suite, fn: fns[0] }), undefined);
|
|
const childSuite = describe(suite, "child");
|
|
const child2Suite = describe.only(childSuite, "child 2");
|
|
assertEquals(
|
|
it({ name: "b", suite: child2Suite, fn: fns[1] }),
|
|
undefined,
|
|
);
|
|
assertEquals(it({ name: "c", suite, fn: fns[2] }), undefined);
|
|
}),
|
|
);
|
|
});
|
|
|
|
await t.step("with hooks", async (t) => {
|
|
/**
|
|
* Asserts that all the different hook types are called in the correct order when the tests run.
|
|
* This is used to reduce code duplication when testing calling `describe` with different call signatures.
|
|
*/
|
|
async function assertHooks(
|
|
cb: (
|
|
options: {
|
|
beforeAllFn: Spy;
|
|
afterAllFn: Spy;
|
|
beforeEachFn: Spy;
|
|
afterEachFn: Spy;
|
|
fns: readonly [Spy, Spy];
|
|
},
|
|
) => void,
|
|
) {
|
|
using test = stub(Deno, "test");
|
|
const fns = [spy(), spy()] as const;
|
|
const { beforeAllFn, afterAllFn, beforeEachFn, afterEachFn } = hookFns();
|
|
|
|
const context = new TestContext("example");
|
|
try {
|
|
cb({ beforeAllFn, afterAllFn, beforeEachFn, afterEachFn, fns });
|
|
|
|
assertSpyCalls(fns[0], 0);
|
|
assertSpyCalls(fns[1], 0);
|
|
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(Object.keys(options).sort(), ["fn", "name"]);
|
|
assertEquals(options.name, "example");
|
|
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 2);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
|
|
let fn = fns[0];
|
|
assertSpyCall(fn, 0, {
|
|
self: { allTimer: 1, eachTimer: 2 },
|
|
args: [context.steps[0]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
fn = fns[1];
|
|
assertSpyCall(fn, 0, {
|
|
self: { allTimer: 1, eachTimer: 3 },
|
|
args: [context.steps[1]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
assertSpyCalls(beforeAllFn, 1);
|
|
assertSpyCalls(afterAllFn, 1);
|
|
assertSpyCalls(beforeEachFn, 2);
|
|
assertSpyCalls(afterEachFn, 2);
|
|
}
|
|
|
|
await t.step(
|
|
"in callback",
|
|
async () =>
|
|
await assertHooks(
|
|
({ beforeAllFn, afterAllFn, beforeEachFn, afterEachFn, fns }) => {
|
|
describe("example", () => {
|
|
beforeAll(beforeAllFn);
|
|
afterAll(afterAllFn);
|
|
|
|
beforeEach(beforeEachFn);
|
|
afterEach(afterEachFn);
|
|
|
|
assertEquals(it({ name: "example 1", fn: fns[0] }), undefined);
|
|
assertEquals(it({ name: "example 2", fn: fns[1] }), undefined);
|
|
});
|
|
},
|
|
),
|
|
);
|
|
|
|
await t.step(
|
|
"in callback (using after, before aliases)",
|
|
async () =>
|
|
await assertHooks(
|
|
({ beforeAllFn, afterAllFn, beforeEachFn, afterEachFn, fns }) => {
|
|
describe("example", () => {
|
|
before(beforeAllFn);
|
|
after(afterAllFn);
|
|
|
|
beforeEach(beforeEachFn);
|
|
afterEach(afterEachFn);
|
|
|
|
it({ name: "example 1", fn: fns[0] });
|
|
it({ name: "example 2", fn: fns[1] });
|
|
});
|
|
},
|
|
),
|
|
);
|
|
|
|
await t.step(
|
|
"in options",
|
|
async () =>
|
|
await assertHooks(
|
|
({ beforeAllFn, afterAllFn, beforeEachFn, afterEachFn, fns }) => {
|
|
describe({
|
|
name: "example",
|
|
beforeAll: beforeAllFn,
|
|
afterAll: afterAllFn,
|
|
beforeEach: beforeEachFn,
|
|
afterEach: afterEachFn,
|
|
fn: () => {
|
|
assertEquals(
|
|
it({ name: "example 1", fn: fns[0] }),
|
|
undefined,
|
|
);
|
|
assertEquals(
|
|
it({ name: "example 2", fn: fns[1] }),
|
|
undefined,
|
|
);
|
|
},
|
|
});
|
|
},
|
|
),
|
|
);
|
|
|
|
await t.step(
|
|
"nested",
|
|
async () => {
|
|
using test = stub(Deno, "test");
|
|
const fns = [spy(), spy()] as const;
|
|
const { beforeAllFn, afterAllFn, beforeEachFn, afterEachFn } =
|
|
hookFns();
|
|
|
|
const context = new TestContext("example");
|
|
try {
|
|
describe("example", () => {
|
|
beforeAll(beforeAllFn);
|
|
afterAll(afterAllFn);
|
|
|
|
beforeEach(beforeEachFn);
|
|
afterEach(afterEachFn);
|
|
|
|
describe("nested", () => {
|
|
assertEquals(it({ name: "example 1", fn: fns[0] }), undefined);
|
|
assertEquals(it({ name: "example 2", fn: fns[1] }), undefined);
|
|
});
|
|
});
|
|
|
|
assertSpyCalls(fns[0], 0);
|
|
assertSpyCalls(fns[1], 0);
|
|
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(Object.keys(options).sort(), ["fn", "name"]);
|
|
assertEquals(options.name, "example");
|
|
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 1);
|
|
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.steps[0]!.spies.step, 2);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
|
|
let fn = fns[0];
|
|
assertSpyCall(fn, 0, {
|
|
self: { allTimer: 1, eachTimer: 2 },
|
|
args: [context.steps[0]!.steps[0]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
fn = fns[1];
|
|
assertSpyCall(fn, 0, {
|
|
self: { allTimer: 1, eachTimer: 3 },
|
|
args: [context.steps[0]!.steps[1]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
assertSpyCalls(beforeAllFn, 1);
|
|
assertSpyCalls(afterAllFn, 1);
|
|
assertSpyCalls(beforeEachFn, 2);
|
|
assertSpyCalls(afterEachFn, 2);
|
|
},
|
|
);
|
|
|
|
interface NestedContext extends GlobalContext {
|
|
allTimerNested: number;
|
|
eachTimerNested: number;
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
await t.step(
|
|
"nested with hooks",
|
|
async () => {
|
|
using test = stub(Deno, "test");
|
|
const fns = [
|
|
spy(function (this: NestedContext) {
|
|
this.x = 2;
|
|
}),
|
|
spy(function (this: NestedContext) {
|
|
this.y = 3;
|
|
}),
|
|
] as const;
|
|
const { beforeAllFn, afterAllFn, beforeEachFn, afterEachFn } =
|
|
hookFns();
|
|
const beforeAllFnNested = spy(async function (this: NestedContext) {
|
|
await Promise.resolve();
|
|
this.x = 1;
|
|
this.allTimerNested = timerIdx++;
|
|
timers.set(
|
|
this.allTimerNested,
|
|
setTimeout(() => {}, 10000),
|
|
);
|
|
});
|
|
const afterAllFnNested = spy(
|
|
async function (this: NestedContext) {
|
|
await Promise.resolve();
|
|
clearTimeout(timers.get(this.allTimerNested));
|
|
},
|
|
);
|
|
const beforeEachFnNested = spy(async function (this: NestedContext) {
|
|
await Promise.resolve();
|
|
this.y = 2;
|
|
this.eachTimerNested = timerIdx++;
|
|
timers.set(
|
|
this.eachTimerNested,
|
|
setTimeout(() => {}, 10000),
|
|
);
|
|
});
|
|
const afterEachFnNested = spy(
|
|
async function (this: NestedContext) {
|
|
await Promise.resolve();
|
|
clearTimeout(timers.get(this.eachTimerNested));
|
|
},
|
|
);
|
|
|
|
const context = new TestContext("example");
|
|
try {
|
|
describe("example", () => {
|
|
beforeAll(beforeAllFn);
|
|
afterAll(afterAllFn);
|
|
|
|
beforeEach(beforeEachFn);
|
|
afterEach(afterEachFn);
|
|
|
|
describe("nested", () => {
|
|
beforeAll(beforeAllFnNested);
|
|
afterAll(afterAllFnNested);
|
|
|
|
beforeEach(beforeEachFnNested);
|
|
afterEach(afterEachFnNested);
|
|
|
|
assertEquals(it({ name: "example 1", fn: fns[0] }), undefined);
|
|
assertEquals(it({ name: "example 2", fn: fns[1] }), undefined);
|
|
});
|
|
});
|
|
|
|
assertSpyCalls(fns[0], 0);
|
|
assertSpyCalls(fns[1], 0);
|
|
|
|
assertSpyCall(test, 0);
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
assertEquals(Object.keys(options).sort(), ["fn", "name"]);
|
|
assertEquals(options.name, "example");
|
|
|
|
const result = options.fn(context);
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.spies.step, 1);
|
|
|
|
assertStrictEquals(Promise.resolve(result), result);
|
|
assertEquals(await result, undefined);
|
|
assertSpyCalls(context.steps[0]!.spies.step, 2);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
|
|
let fn = fns[0];
|
|
assertSpyCall(fn, 0, {
|
|
self: {
|
|
allTimer: 1,
|
|
allTimerNested: 2,
|
|
eachTimer: 3,
|
|
eachTimerNested: 4,
|
|
x: 2,
|
|
y: 2,
|
|
},
|
|
args: [context.steps[0]!.steps[0]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
fn = fns[1];
|
|
assertSpyCall(fn, 0, {
|
|
self: {
|
|
allTimer: 1,
|
|
allTimerNested: 2,
|
|
eachTimer: 5,
|
|
eachTimerNested: 6,
|
|
x: 1,
|
|
y: 3,
|
|
},
|
|
args: [context.steps[0]!.steps[1]],
|
|
returned: undefined,
|
|
});
|
|
assertSpyCalls(fn, 1);
|
|
|
|
assertSpyCalls(beforeAllFn, 1);
|
|
|
|
assertSpyCall(afterAllFn, 0, {
|
|
self: {
|
|
allTimer: 1,
|
|
} as GlobalContext,
|
|
});
|
|
assertSpyCalls(afterAllFn, 1);
|
|
|
|
assertSpyCalls(beforeEachFn, 2);
|
|
|
|
assertSpyCall(afterEachFn, 0, {
|
|
self: {
|
|
allTimer: 1,
|
|
allTimerNested: 2,
|
|
eachTimer: 3,
|
|
eachTimerNested: 4,
|
|
x: 2,
|
|
y: 2,
|
|
} as NestedContext,
|
|
});
|
|
assertSpyCall(afterEachFn, 1, {
|
|
self: {
|
|
allTimer: 1,
|
|
allTimerNested: 2,
|
|
eachTimer: 5,
|
|
eachTimerNested: 6,
|
|
x: 1,
|
|
y: 3,
|
|
} as NestedContext,
|
|
});
|
|
assertSpyCalls(afterEachFn, 2);
|
|
|
|
assertSpyCalls(beforeAllFnNested, 1);
|
|
|
|
assertSpyCall(afterAllFnNested, 0, {
|
|
self: {
|
|
allTimer: 1,
|
|
allTimerNested: 2,
|
|
x: 1,
|
|
} as NestedContext,
|
|
});
|
|
assertSpyCalls(afterAllFnNested, 1);
|
|
|
|
assertSpyCalls(beforeEachFnNested, 2);
|
|
|
|
assertSpyCall(afterEachFnNested, 0, {
|
|
self: {
|
|
allTimer: 1,
|
|
allTimerNested: 2,
|
|
eachTimer: 3,
|
|
eachTimerNested: 4,
|
|
x: 2,
|
|
y: 2,
|
|
},
|
|
});
|
|
assertSpyCall(afterEachFnNested, 1, {
|
|
self: {
|
|
allTimer: 1,
|
|
allTimerNested: 2,
|
|
eachTimer: 5,
|
|
eachTimerNested: 6,
|
|
x: 1,
|
|
y: 3,
|
|
},
|
|
});
|
|
assertSpyCalls(afterEachFnNested, 2);
|
|
},
|
|
);
|
|
});
|
|
|
|
await t.step(
|
|
"mutiple hook calls",
|
|
async () => {
|
|
using test = stub(Deno, "test");
|
|
const context = new TestContext("example");
|
|
const beforeAllFn = spy();
|
|
const afterAllFn = spy();
|
|
const beforeEachFn = spy();
|
|
const afterEachFn = spy();
|
|
|
|
const nested = {
|
|
beforeAllFn: spy(),
|
|
afterAllFn: spy(),
|
|
beforeEachFn: spy(),
|
|
afterEachFn: spy(),
|
|
};
|
|
try {
|
|
describe("example multiple hooks", () => {
|
|
beforeAll(beforeAllFn);
|
|
beforeAll(beforeAllFn);
|
|
afterAll(afterAllFn);
|
|
afterAll(afterAllFn);
|
|
beforeEach(beforeEachFn);
|
|
beforeEach(beforeEachFn);
|
|
afterEach(afterEachFn);
|
|
afterEach(afterEachFn);
|
|
|
|
describe("nested", () => {
|
|
beforeAll(nested.beforeAllFn);
|
|
beforeAll(nested.beforeAllFn);
|
|
afterAll(nested.afterAllFn);
|
|
afterAll(nested.afterAllFn);
|
|
beforeEach(nested.beforeEachFn);
|
|
beforeEach(nested.beforeEachFn);
|
|
afterEach(nested.afterEachFn);
|
|
afterEach(nested.afterEachFn);
|
|
it({ name: "example 1", fn() {} });
|
|
it({ name: "example 2", fn() {} });
|
|
});
|
|
});
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
await options.fn(context);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
|
|
assertSpyCalls(beforeAllFn, 2);
|
|
assertSpyCalls(afterAllFn, 2);
|
|
assertSpyCalls(beforeEachFn, 4);
|
|
assertSpyCalls(afterEachFn, 4);
|
|
|
|
assertSpyCalls(nested.beforeAllFn, 2);
|
|
assertSpyCalls(nested.afterAllFn, 2);
|
|
assertSpyCalls(nested.beforeEachFn, 4);
|
|
assertSpyCalls(nested.afterEachFn, 4);
|
|
},
|
|
);
|
|
|
|
await t.step("throws if called with wrong suite object", () => {
|
|
assertThrows(
|
|
// deno-lint-ignore no-explicit-any
|
|
() => describe({ name: "", suite: {} as any, fn: () => {} }),
|
|
Error,
|
|
"suite does not represent a registered test suite",
|
|
);
|
|
});
|
|
|
|
await t.step(
|
|
"throws if nested test case is called with permission option",
|
|
async () => {
|
|
using test = stub(Deno, "test");
|
|
const context = new TestContext("example");
|
|
try {
|
|
describe("foo", () => {
|
|
describe("bar", { permissions: { read: true } }, () => {
|
|
it("baz", () => {});
|
|
});
|
|
});
|
|
const call = test.calls[0];
|
|
const options = call?.args[0] as Deno.TestDefinition;
|
|
await assertRejects(
|
|
async () => await options.fn(context),
|
|
Error,
|
|
"permissions option not available for nested tests",
|
|
);
|
|
} finally {
|
|
TestSuiteInternal.reset();
|
|
}
|
|
},
|
|
);
|
|
|
|
await t.step(
|
|
"cause type error if async function is passed as describe definition",
|
|
() => {
|
|
try {
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe({ name: "example", fn: async () => {} });
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe("example", { fn: async () => {} });
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe("example", async () => {});
|
|
// TODO(kt3k): This case should be type error but it's checked as
|
|
// DescribeDefinition<T> and passes the type check
|
|
// describe(async function example() {});
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe("example", {}, async () => {});
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe({ name: "example" }, async () => {});
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe({}, async function example() {});
|
|
|
|
const suite = describe("example");
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe(suite, "example", { fn: async () => {} });
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe(suite, "example", async () => {});
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe(suite, async () => {});
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe(suite, "example", {}, async () => {});
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe(suite, { name: "example" }, async () => {});
|
|
// @ts-expect-error async function is not assignable to describe argument
|
|
describe(suite, {}, async function example() {});
|
|
} catch {
|
|
// Ignores runtime errors as this case is for static type checking
|
|
}
|
|
},
|
|
);
|
|
|
|
await t.step(
|
|
"throws runtime error if async function is passed as describe fn",
|
|
() => {
|
|
assertThrows(
|
|
// deno-lint-ignore no-explicit-any
|
|
() => describe("async describe", (async () => {}) as any),
|
|
Error,
|
|
'Returning a Promise from "describe" is not supported: tests must be defined synchronously',
|
|
);
|
|
},
|
|
);
|
|
});
|