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("