diff --git a/README.md b/README.md index e2a5542..a1b6a79 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Test Suite -[![version](https://img.shields.io/badge/release-0.11.0-success)](https://deno.land/x/test_suite@0.11.0) -[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/test_suite@0.11.0/mod.ts) +[![version](https://img.shields.io/badge/release-0.12.0-success)](https://deno.land/x/test_suite@0.12.0) +[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/test_suite@0.12.0/mod.ts) [![CI](https://github.com/udibo/test_suite/workflows/CI/badge.svg)](https://github.com/udibo/test_suite/actions?query=workflow%3ACI) [![codecov](https://codecov.io/gh/udibo/test_suite/branch/main/graph/badge.svg?token=EFKGY72AAV)](https://codecov.io/gh/udibo/test_suite) [![license](https://img.shields.io/github/license/udibo/test_suite)](https://github.com/udibo/test_suite/blob/master/LICENSE) @@ -26,12 +26,12 @@ also be imported directly from GitHub using raw content URLs. ```ts // Import from Deno's third party module registry -import { describe, it } from "https://deno.land/x/test_suite@0.11.0/mod.ts"; +import { describe, it } from "https://deno.land/x/test_suite@0.12.0/mod.ts"; // Import from GitHub import { describe, it, -} from "https://raw.githubusercontent.com/udibo/test_suite/0.11.0/mod.ts"; +} from "https://raw.githubusercontent.com/udibo/test_suite/0.12.0/mod.ts"; ``` ## Usage @@ -52,7 +52,7 @@ except they are called before and after each individual test. Below are some examples of how to use `describe` and `it` in tests. See -[deno docs](https://doc.deno.land/https/deno.land/x/test_suite@0.11.0/mod.ts) +[deno docs](https://doc.deno.land/https/deno.land/x/test_suite@0.12.0/mod.ts) for more information. ### Nested test grouping @@ -65,13 +65,13 @@ import { beforeEach, describe, it, -} from "https://deno.land/x/test_suite@0.11.0/mod.ts"; +} from "https://deno.land/x/test_suite@0.12.0/mod.ts"; import { assertEquals } from "https://deno.land/std@0.128.0/testing/asserts.ts"; import { getUser, resetUsers, User, -} from "https://deno.land/x/test_suite@0.11.0/examples/user.ts"; +} from "https://deno.land/x/test_suite@0.12.0/examples/user.ts"; describe("user describe", () => { let user: User; @@ -129,13 +129,13 @@ test result: ok. 1 passed (5 steps); 0 failed; 0 ignored; 0 measured; 0 filtered The example below can be found [here](examples/user_flat_test.ts). ```ts -import { describe, it } from "https://deno.land/x/test_suite@0.11.0/mod.ts"; +import { describe, it } from "https://deno.land/x/test_suite@0.12.0/mod.ts"; import { assertEquals } from "https://deno.land/std@0.128.0/testing/asserts.ts"; import { getUser, resetUsers, User, -} from "https://deno.land/x/test_suite@0.11.0/examples/user.ts"; +} from "https://deno.land/x/test_suite@0.12.0/examples/user.ts"; interface UserContext { user: User; diff --git a/describe.ts b/describe.ts index cfc8b99..2935ec8 100644 --- a/describe.ts +++ b/describe.ts @@ -3,8 +3,9 @@ import { HookNames, ItDefinition, TestSuite, + TestSuiteInternal, } from "./test_suite.ts"; -export type { DescribeDefinition, ItDefinition }; +export type { DescribeDefinition, ItDefinition, TestSuite }; /** The arguments for an ItFunction. */ type ItArgs = @@ -32,32 +33,32 @@ type ItArgs = fn: (context: T) => void | Promise, ] | [ - suite: symbol, + suite: TestSuite, name: string, options: Omit, "name" | "suite">, ] | [ - suite: symbol, + suite: TestSuite, name: string, fn: (context: T) => void | Promise, ] | [ - suite: symbol, + suite: TestSuite, fn: (context: T) => void | Promise, ] | [ - suite: symbol, + suite: TestSuite, name: string, options: Omit, "fn" | "name" | "suite">, fn: (context: T) => void | Promise, ] | [ - suite: symbol, + suite: TestSuite, options: Omit, "fn" | "suite">, fn: (context: T) => void | Promise, ] | [ - suite: symbol, + suite: TestSuite, options: Omit, "fn" | "name" | "suite">, fn: (context: T) => void | Promise, ]; @@ -70,15 +71,18 @@ function itDefinition(...args: ItArgs): ItDefinition { optionsOrFn, fn, ] = args; - let suite: symbol | undefined = undefined; + let suite: TestSuite | undefined = undefined; let name: string; let options: | ItDefinition | Omit, "fn"> | Omit, "name"> | Omit, "fn" | "name">; - if (typeof suiteOptionsOrNameOrFn === "symbol") { - suite = suiteOptionsOrNameOrFn; + if ( + typeof suiteOptionsOrNameOrFn === "object" && + typeof (suiteOptionsOrNameOrFn as TestSuite).symbol === "symbol" + ) { + suite = suiteOptionsOrNameOrFn as TestSuite; } else { fn = optionsOrFn as typeof fn; optionsOrFn = optionsOrNameOrFn as typeof optionsOrFn; @@ -128,18 +132,20 @@ export interface it { /** Registers an individual test case. */ export function it(...args: ItArgs): void { - if (TestSuite.running) { + if (TestSuiteInternal.running) { throw new Error( "cannot register new test cases after already registered test cases start running", ); } const options = itDefinition(...args); const { suite } = options; - const testSuite = suite ? TestSuite.suites.get(suite) : TestSuite.current; + const testSuite = suite + ? TestSuiteInternal.suites.get(suite.symbol) + : TestSuiteInternal.current; - if (!TestSuite.started) TestSuite.started = true; + if (!TestSuiteInternal.started) TestSuiteInternal.started = true; if (testSuite) { - TestSuite.addStep(testSuite, options); + TestSuiteInternal.addStep(testSuite, options); } else { const { name, @@ -151,7 +157,7 @@ export function it(...args: ItArgs): void { sanitizeOps, sanitizeResources, } = options; - TestSuite.registerTest({ + TestSuiteInternal.registerTest({ name, ignore, only, @@ -160,7 +166,7 @@ export function it(...args: ItArgs): void { sanitizeOps, sanitizeResources, fn: async () => { - if (!TestSuite.running) TestSuite.running = true; + if (!TestSuiteInternal.running) TestSuiteInternal.running = true; await fn!({} as T); }, }); @@ -187,18 +193,18 @@ function addHook( name: HookNames, fn: (context: T) => void | Promise, ): void { - if (!TestSuite.current) { - if (TestSuite.started) { + if (!TestSuiteInternal.current) { + if (TestSuiteInternal.started) { throw new Error( "cannot add global hooks after a global test is registered", ); } - TestSuite.current = new TestSuite({ + TestSuiteInternal.current = new TestSuiteInternal({ name: "global", [name]: fn, }); } else { - TestSuite.setHook(TestSuite.current!, name, fn); + TestSuiteInternal.setHook(TestSuiteInternal.current!, name, fn); } } @@ -254,36 +260,36 @@ type DescribeArgs = fn: () => void, ] | [ - suite: symbol, + suite: TestSuite, name: string, ] | [ - suite: symbol, + suite: TestSuite, name: string, options: Omit, "name" | "suite">, ] | [ - suite: symbol, + suite: TestSuite, name: string, fn: () => void, ] | [ - suite: symbol, + suite: TestSuite, fn: () => void, ] | [ - suite: symbol, + suite: TestSuite, name: string, options: Omit, "fn" | "name" | "suite">, fn: () => void, ] | [ - suite: symbol, + suite: TestSuite, options: Omit, "fn" | "suite">, fn: () => void, ] | [ - suite: symbol, + suite: TestSuite, options: Omit, "fn" | "name" | "suite">, fn: () => void, ]; @@ -298,15 +304,18 @@ function describeDefinition( optionsOrFn, fn, ] = args; - let suite: symbol | undefined = undefined; + let suite: TestSuite | undefined = undefined; let name: string; let options: | DescribeDefinition | Omit, "fn"> | Omit, "name"> | Omit, "fn" | "name">; - if (typeof suiteOptionsOrNameOrFn === "symbol") { - suite = suiteOptionsOrNameOrFn; + if ( + typeof suiteOptionsOrNameOrFn === "object" && + typeof (suiteOptionsOrNameOrFn as TestSuite).symbol === "symbol" + ) { + suite = suiteOptionsOrNameOrFn as TestSuite; } else { fn = optionsOrFn as typeof fn; optionsOrFn = optionsOrNameOrFn as typeof optionsOrFn; @@ -336,7 +345,11 @@ function describeDefinition( } if (!suite) { - suite = options.suite ?? TestSuite.current?.symbol; + suite = options.suite; + } + if (!suite && TestSuiteInternal.current) { + const { symbol } = TestSuiteInternal.current; + suite = { symbol }; } return { @@ -349,32 +362,33 @@ function describeDefinition( /** Registers a test suite. */ export interface describe { - (...args: DescribeArgs): symbol; + (...args: DescribeArgs): TestSuite; /** Registers a test suite with only set to true. */ - only(...args: DescribeArgs): symbol; + only(...args: DescribeArgs): TestSuite; /** Registers a test suite with ignore set to true. */ - ignore(...args: DescribeArgs): symbol; + ignore(...args: DescribeArgs): TestSuite; } /** Registers a test suite. */ export function describe( ...args: DescribeArgs -): symbol { - if (TestSuite.running) { +): TestSuite { + if (TestSuiteInternal.running) { throw new Error( "cannot register new test suites after already registered test cases start running", ); } const options = describeDefinition(...args); - if (!TestSuite.started) TestSuite.started = true; - return (new TestSuite(options)).symbol; + if (!TestSuiteInternal.started) TestSuiteInternal.started = true; + const { symbol } = new TestSuiteInternal(options); + return { symbol }; } describe.only = function describeOnly( ...args: DescribeArgs -): symbol { +): TestSuite { const options = describeDefinition(...args); return describe({ ...options, @@ -384,7 +398,7 @@ describe.only = function describeOnly( describe.ignore = function describeIgnore( ...args: DescribeArgs -): symbol { +): TestSuite { const options = describeDefinition(...args); return describe({ ...options, diff --git a/describe_test.ts b/describe_test.ts index c2daf96..3d4cc33 100644 --- a/describe_test.ts +++ b/describe_test.ts @@ -12,7 +12,7 @@ import { describe, it, } from "./describe.ts"; -import { TestSuite } from "./test_suite.ts"; +import { TestSuiteInternal } from "./test_suite.ts"; import { assertSpyCall, assertSpyCalls, @@ -129,7 +129,7 @@ Deno.test("global", async (t) => { assertEquals(await result, undefined); assertSpyCalls(context.spies.step, 2); } finally { - TestSuite.reset(); + TestSuiteInternal.reset(); test.restore(); time.restore(); } @@ -189,7 +189,7 @@ Deno.test("global", async (t) => { returned: undefined, }); } finally { - TestSuite.reset(); + TestSuiteInternal.reset(); test.restore(); } } @@ -670,7 +670,7 @@ Deno.test("global", async (t) => { }); assertSpyCalls(fn, 1); } finally { - TestSuite.reset(); + TestSuiteInternal.reset(); test.restore(); } } @@ -693,7 +693,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe({ name: "example" }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -708,7 +708,7 @@ Deno.test("global", async (t) => { }, ...baseOptions, }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); })); }); @@ -718,7 +718,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe("example"); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -730,7 +730,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe("example", {}); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -744,7 +744,7 @@ Deno.test("global", async (t) => { }, ...baseOptions, }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); })); }); @@ -756,7 +756,7 @@ Deno.test("global", async (t) => { const suite = describe("example", () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -768,7 +768,7 @@ Deno.test("global", async (t) => { const suite = describe(function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -781,7 +781,7 @@ Deno.test("global", async (t) => { const suite = describe("example", {}, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -793,7 +793,7 @@ Deno.test("global", async (t) => { }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); })); }); @@ -806,7 +806,7 @@ Deno.test("global", async (t) => { const suite = describe({ name: "example" }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -819,7 +819,7 @@ Deno.test("global", async (t) => { }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); })); }); @@ -832,7 +832,7 @@ Deno.test("global", async (t) => { const suite = describe({}, function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -844,7 +844,7 @@ Deno.test("global", async (t) => { }, function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); })); }); @@ -868,7 +868,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe.only({ name: "example" }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -885,7 +885,7 @@ Deno.test("global", async (t) => { }, ...baseOptions, }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -896,7 +896,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe.only("example"); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -908,7 +908,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe.only("example", {}); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -924,7 +924,7 @@ Deno.test("global", async (t) => { }, ...baseOptions, }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -937,7 +937,7 @@ Deno.test("global", async (t) => { const suite = describe.only("example", () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -949,7 +949,7 @@ Deno.test("global", async (t) => { const suite = describe.only(function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -962,7 +962,7 @@ Deno.test("global", async (t) => { const suite = describe.only("example", {}, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -976,7 +976,7 @@ Deno.test("global", async (t) => { }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -990,7 +990,7 @@ Deno.test("global", async (t) => { const suite = describe.only({ name: "example" }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1005,7 +1005,7 @@ Deno.test("global", async (t) => { }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1019,7 +1019,7 @@ Deno.test("global", async (t) => { const suite = describe.only({}, function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1033,7 +1033,7 @@ Deno.test("global", async (t) => { }, function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1059,7 +1059,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe.ignore({ name: "example" }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -1076,7 +1076,7 @@ Deno.test("global", async (t) => { }, ...baseOptions, }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1087,7 +1087,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe.ignore("example"); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -1099,7 +1099,7 @@ Deno.test("global", async (t) => { async () => await assertMinimumOptions((fns) => { const suite = describe.ignore("example", {}); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "a", fn: fns[0] }), undefined); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), @@ -1115,7 +1115,7 @@ Deno.test("global", async (t) => { }, ...baseOptions, }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1128,7 +1128,7 @@ Deno.test("global", async (t) => { const suite = describe.ignore("example", () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1140,7 +1140,7 @@ Deno.test("global", async (t) => { const suite = describe.ignore(function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1153,7 +1153,7 @@ Deno.test("global", async (t) => { const suite = describe.ignore("example", {}, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1167,7 +1167,7 @@ Deno.test("global", async (t) => { }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1181,7 +1181,7 @@ Deno.test("global", async (t) => { const suite = describe.ignore({ name: "example" }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1196,7 +1196,7 @@ Deno.test("global", async (t) => { }, () => { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1210,7 +1210,7 @@ Deno.test("global", async (t) => { const suite = describe.ignore({}, function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1224,7 +1224,7 @@ Deno.test("global", async (t) => { }, function example() { assertEquals(it({ name: "a", fn: fns[0] }), undefined); }); - assert(typeof suite === "symbol"); + assert(suite && typeof suite.symbol === "symbol"); assertEquals(it({ suite, name: "b", fn: fns[1] }), undefined); }), ); @@ -1273,7 +1273,7 @@ Deno.test("global", async (t) => { fn = fns[2]; assertSpyCalls(fn, 0); } finally { - TestSuite.reset(); + TestSuiteInternal.reset(); test.restore(); } } @@ -1358,7 +1358,7 @@ Deno.test("global", async (t) => { assertEquals(await result, undefined); assertSpyCalls(context.spies.step, 2); } finally { - TestSuite.reset(); + TestSuiteInternal.reset(); test.restore(); time.restore(); } @@ -1472,7 +1472,7 @@ Deno.test("global", async (t) => { assertEquals(await result, undefined); assertSpyCalls(context.spies.step, 2); } finally { - TestSuite.reset(); + TestSuiteInternal.reset(); test.restore(); time.restore(); } @@ -1579,7 +1579,7 @@ Deno.test("global", async (t) => { assertEquals(await result, undefined); assertSpyCalls(context.spies.step, 2); } finally { - TestSuite.reset(); + TestSuiteInternal.reset(); test.restore(); time.restore(); } diff --git a/mod.ts b/mod.ts index edd82b2..97777e9 100644 --- a/mod.ts +++ b/mod.ts @@ -6,4 +6,8 @@ export { describe, it, } from "./describe.ts"; -export type { DescribeDefinition, ItDefinition } from "./describe.ts"; +export type { + DescribeDefinition, + ItDefinition, + TestSuite, +} from "./describe.ts"; diff --git a/test_suite.ts b/test_suite.ts index c4ba642..865ef9b 100644 --- a/test_suite.ts +++ b/test_suite.ts @@ -2,11 +2,11 @@ export interface DescribeDefinition extends Omit { fn?: () => void; /** - * The `describe` function returns a symbol representing the group of tests. - * If `describe` is called within another `describe` calls `fn`, the suite will default to that parent `describe` calls returned symbol. - * If `describe` is not called within another `describe` calls `fn`, the suite will default to the symbol representing the global group of tests. + * The `describe` function returns a `TestSuite` representing the group of tests. + * If `describe` is called within another `describe` calls `fn`, the suite will default to that parent `describe` calls returned `TestSuite`. + * If `describe` is not called within another `describe` calls `fn`, the suite will default to the `TestSuite` representing the global group of tests. */ - suite?: symbol; + suite?: TestSuite; /** Run some shared setup before all of the tests in the suite. */ beforeAll?: (context: T) => void | Promise; /** Run some shared teardown after all of the tests in the suite. */ @@ -21,11 +21,11 @@ export interface DescribeDefinition extends Omit { export interface ItDefinition extends Omit { fn: (context: T) => void | Promise; /** - * The `describe` function returns a symbol representing the group of tests. - * If `it` is called within a `describe` calls `fn`, the suite will default to that parent `describe` calls returned symbol. - * If `it` is not called within a `describe` calls `fn`, the suite will default to the symbol representing the global group of tests. + * The `describe` function returns a `TestSuite` representing the group of tests. + * If `it` is called within a `describe` calls `fn`, the suite will default to that parent `describe` calls returned `TestSuite`. + * If `it` is not called within a `describe` calls `fn`, the suite will default to the `TestSuite` representing the global group of tests. */ - suite?: symbol; + suite?: TestSuite; } /** The names of all the different types of hooks. */ @@ -50,12 +50,19 @@ const optionalTestStepDefinitionKeys: (keyof Deno.TestStepDefinition)[] = [ ]; /** - * A group of tests. A test suite can include child test suites. + * A group of tests. */ -export class TestSuite { +export interface TestSuite { + symbol: symbol; +} + +/** + * An internal representation of a group of tests. + */ +export class TestSuiteInternal implements TestSuite { symbol: symbol; protected describe: DescribeDefinition; - protected steps: (TestSuite | ItDefinition)[]; + protected steps: (TestSuiteInternal | ItDefinition)[]; protected hasOnlyStep: boolean; constructor(describe: DescribeDefinition) { @@ -63,27 +70,29 @@ export class TestSuite { this.steps = []; this.hasOnlyStep = false; - const suite = describe.suite ?? TestSuite.current?.symbol; - if (suite && !TestSuite.suites.has(suite)) { - throw new Error("suite symbol does not represent a test suite"); + const { suite } = describe; + if (suite && !TestSuiteInternal.suites.has(suite.symbol)) { + throw new Error("suite does not represent a registered test suite"); } + const testSuite = suite + ? TestSuiteInternal.suites.get(suite.symbol) + : TestSuiteInternal.current; this.symbol = Symbol(); - TestSuite.suites.set(this.symbol, this); - const testSuite = suite && TestSuite.suites.get(suite); + TestSuiteInternal.suites.set(this.symbol, this); const { fn } = describe; if (fn) { - const temp = TestSuite.current; - TestSuite.current = this; + const temp = TestSuiteInternal.current; + TestSuiteInternal.current = this; try { fn(); } finally { - TestSuite.current = temp; + TestSuiteInternal.current = temp; } } if (testSuite) { - TestSuite.addStep(testSuite, this); + TestSuiteInternal.addStep(testSuite, this); } else { const { name, @@ -94,7 +103,7 @@ export class TestSuite { sanitizeOps, sanitizeResources, } = describe; - TestSuite.registerTest({ + TestSuiteInternal.registerTest({ name, ignore, only, @@ -103,16 +112,16 @@ export class TestSuite { sanitizeOps, sanitizeResources, fn: async (t) => { - if (!TestSuite.running) TestSuite.running = true; + if (!TestSuiteInternal.running) TestSuiteInternal.running = true; const context = {} as T; if (this.describe.beforeAll) { await this.describe.beforeAll(context); } try { - TestSuite.active.push(this.symbol); - await TestSuite.run(this, context, t); + TestSuiteInternal.active.push(this.symbol); + await TestSuiteInternal.run(this, context, t); } finally { - TestSuite.active.pop(); + TestSuiteInternal.active.pop(); if (this.describe.afterAll) { await this.describe.afterAll(context); } @@ -130,21 +139,21 @@ export class TestSuite { /** A map of all test suites by symbol. */ // deno-lint-ignore no-explicit-any - static suites = new Map>(); + static suites = new Map>(); /** The current test suite being registered. */ // deno-lint-ignore no-explicit-any - static current: TestSuite | null = null; + static current: TestSuiteInternal | null = null; /** The stack of tests that are actively running. */ static active: symbol[] = []; /** This is used internally for testing this module. */ static reset(): void { - TestSuite.running = false; - TestSuite.started = false; - TestSuite.current = null; - TestSuite.active = []; + TestSuiteInternal.running = false; + TestSuiteInternal.started = false; + TestSuiteInternal.current = null; + TestSuiteInternal.active = []; } /** This is used internally to register tests. */ @@ -157,11 +166,11 @@ export class TestSuite { } /** Updates all steps within top level suite to have ignore set to true if only is not set to true on step. */ - static addingOnlyStep(suite: TestSuite) { + static addingOnlyStep(suite: TestSuiteInternal) { if (!suite.hasOnlyStep) { for (let i = 0; i < suite.steps.length; i++) { const step = suite.steps[i]!; - if (step instanceof TestSuite) { + if (step instanceof TestSuiteInternal) { if (!(step.hasOnlyStep || step.describe.only)) { suite.steps.splice(i--, 1); } @@ -177,22 +186,22 @@ export class TestSuite { /** This is used internally to add steps to a test suite. */ static addStep( - suite: TestSuite, - step: TestSuite | ItDefinition, + suite: TestSuiteInternal, + step: TestSuiteInternal | ItDefinition, ): void { if (!suite.hasOnlyStep) { - if (step instanceof TestSuite) { + if (step instanceof TestSuiteInternal) { if (step.hasOnlyStep || step.describe.only) { - TestSuite.addingOnlyStep(suite); + TestSuiteInternal.addingOnlyStep(suite); } } else { - if (step.only) TestSuite.addingOnlyStep(suite); + if (step.only) TestSuiteInternal.addingOnlyStep(suite); } } let omit = false; if (suite.hasOnlyStep) { - if (step instanceof TestSuite) { + if (step instanceof TestSuiteInternal) { if (!(step.hasOnlyStep || step.describe.only)) omit = true; } else { if (!step.only) omit = true; @@ -204,7 +213,7 @@ export class TestSuite { /** This is used internally to add hooks to a test suite. */ static setHook( - suite: TestSuite, + suite: TestSuiteInternal, name: HookNames, fn: (context: T) => void | Promise, ): void { @@ -216,7 +225,7 @@ export class TestSuite { /** This is used internally to run all steps for a test suite. */ static async run( - suite: TestSuite, + suite: TestSuiteInternal, context: T, t: Deno.TestContext, ): Promise { @@ -229,7 +238,7 @@ export class TestSuite { sanitizeExit, sanitizeOps, sanitizeResources, - } = step instanceof TestSuite ? step.describe : step; + } = step instanceof TestSuiteInternal ? step.describe : step; const options: Deno.TestStepDefinition = { name, @@ -244,21 +253,21 @@ export class TestSuite { ); } context = { ...context }; - if (step instanceof TestSuite) { + if (step instanceof TestSuiteInternal) { if (step.describe.beforeAll) { await step.describe.beforeAll(context); } try { - TestSuite.active.push(step.symbol); - await TestSuite.run(step, context, t); + TestSuiteInternal.active.push(step.symbol); + await TestSuiteInternal.run(step, context, t); } finally { - TestSuite.active.pop(); + TestSuiteInternal.active.pop(); if (step.describe.afterAll) { await step.describe.afterAll(context); } } } else { - await TestSuite.runTest(fn!, context); + await TestSuiteInternal.runTest(fn!, context); } }, }; @@ -274,15 +283,15 @@ export class TestSuite { context: T, activeIndex = 0, ) { - const suite = TestSuite.active[activeIndex]; - const testSuite = suite && TestSuite.suites.get(suite); + const suite = TestSuiteInternal.active[activeIndex]; + const testSuite = suite && TestSuiteInternal.suites.get(suite); if (testSuite) { context = { ...context }; if (testSuite.describe.beforeEach) { await testSuite.describe.beforeEach(context); } try { - await TestSuite.runTest(fn, context, activeIndex + 1); + await TestSuiteInternal.runTest(fn, context, activeIndex + 1); } finally { if (testSuite.describe.afterEach) { await testSuite.describe.afterEach(context);