import { describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; import { ingestJUnit, isRelativeSourcePath, JUnitParseError, parseJUnit, readJUnit, recordedCases, } from "./junit.ts"; // The shape `deno test --junit-path` emits for a bdd file: the file's suite // holds a container per top-level group (carrying the aggregated failure) // plus bare Deno.test cases; nested describes land in an ext:cli suite; the // leaves land in a framework-named suite. const DENO_SAMPLE = ` 1 test step failed. stack with > escapes and newlines `; // The synthesized pattern-unit shape: no classname at all. const SYNTHESIZED_SAMPLE = ` `; // The shape `deno test --junit-path` reports for a `Deno.test` whose body threw // after its step passed, one whose steps failed and whose body did not, // and a describe whose `afterAll` threw after its leaf passed. const OWN_FAILURE_SAMPLE = ` Error: the body failed 2 test steps failed. Error: the teardown failed Error: a Error: b `; // Two files of bdd tests whose hooks take time: the outermost describe of // each holds what its hooks took on top of its leaves, and a nested // describe's own case holds its leaves again. The second file's tests // were all skipped, and a bare Deno.test sits beside them. const HOOKS_SAMPLE = ` `; describe("junit", () => { describe("parseJUnit()", () => { it("returns every testcase with its suite, outcome, and time", () => { const cases = parseJUnit(DENO_SAMPLE); expect(cases.length).toBe(7); const failed = cases.find((c) => c.name === "glaze > fails on purpose"); expect(failed?.outcome).toBe("fail"); const skipped = cases.find((c) => c.name === "glaze > is skipped"); expect(skipped?.outcome).toBe("skip"); const timed = cases.find( (c) => c.name === "glaze > thickness > thickens when heated", ); expect(timed?.timeSeconds).toBe(0.01); }); it("decodes entities in attribute values", () => { const cases = parseJUnit(DENO_SAMPLE); const escaped = cases.find((c) => c.name.includes("cooled")); expect(escaped?.name).toBe( 'glaze > thickness > thins when "cooled" & ', ); }); it("parses self-closing testcases", () => { const cases = parseJUnit(SYNTHESIZED_SAMPLE); expect(cases.length).toBe(2); expect(cases[0]?.classname).toBeUndefined(); }); it("drops a negative time rather than record it", () => { const cases = parseJUnit( '', ); expect(cases[0]?.timeSeconds).toBeUndefined(); }); it("throws for an unterminated tag", () => { expect(() => parseJUnit(" { expect(() => parseJUnit("")).toThrow( JUnitParseError, ); }); it("skips comments, CDATA sections, and declarations", () => { const cases = parseJUnit( "" + '' + ']]>' + '', ); expect(cases.map((c) => c.name)).toEqual(["real"]); }); it("reads single-quoted attributes and numeric entities", () => { const cases = parseJUnit( "" + "", ); expect(cases[0]?.name).toBe("a > b & c"); }); it("throws for every unterminated construct", () => { expect(() => parseJUnit(' parseJUnit("