Skip to main content
Module

std/testing/bdd_test.ts

Deno standard library
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.import { assert, assertEquals, assertObjectMatch, assertStrictEquals,} from "./asserts.ts";import { afterAll, afterEach, beforeAll, beforeEach, describe, it,} from "./bdd.ts";import { TestSuiteInternal } from "./_test_suite.ts";import { assertSpyCall, assertSpyCalls, Spy, spy, stub } from "./mock.ts";
Deno.test("global", async (t) => { 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( tOrName: Deno.TestStepDefinition | string, fn?: (t: Deno.TestContext) => void | Promise<void>, ): Promise<boolean> { let ignore = false; if (typeof tOrName === "object") { ignore = tOrName.ignore ?? false; fn = tOrName.fn; }
const name = typeof tOrName === "string" ? tOrName : tOrName.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)); }), }; }
await t.step("global hooks", async () => { const test = stub(Deno, "test"), fns = [spy(), spy()], { 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(); test.restore(); }
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("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, ): Promise<void> { const 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(); test.restore(); } }
/** * 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, ): Promise<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, ): Promise<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, ): Promise<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, ): Promise<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, ): Promise<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, ): Promise<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("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, ); })); }); }); });
await t.step("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: Spy[]) => void, ): Promise<void> { const test = stub(Deno, "test"); const fns = [spy(), spy()]; 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(); test.restore(); } }
/** * 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: Spy[]) => void, ): Promise<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: Spy[]) => void, ): Promise<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: Spy[]) => void, ): Promise<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: Spy[]) => void, ): Promise<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: Spy[]) => void, ): Promise<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: Spy[]) => void, ): Promise<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( "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: Spy[]) => void, ): Promise<void> { const test = stub(Deno, "test"); const fns = [spy(), spy(), spy()]; 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(); test.restore(); } }
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: Spy[]) => void, ): Promise<void> { const test = stub(Deno, "test"); const fns = [spy(), spy(), spy()]; 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(); test.restore(); } }
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: Spy[]; }, ) => void, ) { const test = stub(Deno, "test"), fns = [spy(), spy()], { 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(); test.restore(); }
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 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 () => { const test = stub(Deno, "test"), fns = [spy(), spy()], { 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(); test.restore(); }
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 () => { const test = stub(Deno, "test"), fns = [ spy(function (this: NestedContext) { this.x = 2; }), spy(function (this: NestedContext) { this.y = 3; }), ], { beforeAllFn, afterAllFn, beforeEachFn, afterEachFn } = hookFns(), beforeAllFnNested = spy(async function (this: NestedContext) { await Promise.resolve(); this.x = 1; this.allTimerNested = timerIdx++; timers.set( this.allTimerNested, setTimeout(() => {}, 10000), ); }), afterAllFnNested = spy( async function (this: NestedContext) { await Promise.resolve(); clearTimeout(timers.get(this.allTimerNested)); }, ), beforeEachFnNested = spy(async function (this: NestedContext) { await Promise.resolve(); this.y = 2; this.eachTimerNested = timerIdx++; timers.set( this.eachTimerNested, setTimeout(() => {}, 10000), ); }), 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(); test.restore(); }
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); }, ); }); });});