mirror of
https://github.com/oven-sh/bun
synced 2026-02-14 04:49:06 +00:00
315 lines
10 KiB
JavaScript
315 lines
10 KiB
JavaScript
import { file, spawn } from "bun";
|
|
import { describe, expect, it } from "bun:test";
|
|
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
|
|
|
|
const xml2js = require("xml2js");
|
|
|
|
describe("junit reporter", () => {
|
|
it.each([false, true])("should generate valid junit xml for passing tests %s", async withCIEnvironmentVariables => {
|
|
const tmpDir = tempDirWithFiles("junit", {
|
|
"package.json": "{}",
|
|
"passing.test.js": `
|
|
describe("root describe", () => {
|
|
it("should pass", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
it("second test", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
it("failing test", () => {
|
|
expect(1 + 1).toBe(3);
|
|
});
|
|
|
|
it.skip("skipped test", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
it.todo("todo test");
|
|
|
|
describe("nested describe", () => {
|
|
it("should pass inside nested describe", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
it("should fail inside nested describe", () => {
|
|
expect(1 + 1).toBe(3);
|
|
});
|
|
});
|
|
});
|
|
`,
|
|
"test-2.test.js": `
|
|
describe("root describe", () => {
|
|
it("should pass", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
it("failing test", () => {
|
|
expect(1 + 1).toBe(3);
|
|
});
|
|
|
|
describe("nested describe", () => {
|
|
it("should pass inside nested describe", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
it("should fail inside nested describe", () => {
|
|
expect(1 + 1).toBe(3);
|
|
});
|
|
});
|
|
});
|
|
`,
|
|
});
|
|
|
|
let env = bunEnv;
|
|
|
|
if (withCIEnvironmentVariables) {
|
|
env = {
|
|
...env,
|
|
CI_JOB_URL: "https://ci.example.com/123",
|
|
CI_COMMIT_SHA: "1234567890",
|
|
};
|
|
}
|
|
|
|
const junitPath = `${tmpDir}/junit.xml`;
|
|
const proc = spawn([bunExe(), "test", "--reporter=junit", "--reporter-outfile", junitPath], {
|
|
cwd: tmpDir,
|
|
env,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
await proc.exited;
|
|
|
|
expect(proc.exitCode).toBe(1);
|
|
const xmlContent = await file(junitPath).text();
|
|
|
|
const result = await new Promise((resolve, reject) => {
|
|
xml2js.parseString(xmlContent, (err, result) => {
|
|
if (err) reject(err);
|
|
else resolve(result);
|
|
});
|
|
});
|
|
|
|
expect(result.testsuites).toBeDefined();
|
|
expect(result.testsuites.testsuite).toBeDefined();
|
|
|
|
let firstSuite = result.testsuites.testsuite[0];
|
|
let secondSuite = result.testsuites.testsuite[1];
|
|
|
|
if (firstSuite.$.name === "passing.test.js") {
|
|
[firstSuite, secondSuite] = [secondSuite, firstSuite];
|
|
}
|
|
|
|
expect(firstSuite.$.name).toBe("test-2.test.js");
|
|
expect(firstSuite.$.file).toBe("test-2.test.js");
|
|
expect(firstSuite.$.tests).toBe("4");
|
|
expect(firstSuite.$.failures).toBe("2");
|
|
expect(firstSuite.$.skipped).toBe("0");
|
|
expect(Number.parseFloat(firstSuite.$.time)).toBeGreaterThanOrEqual(0.0);
|
|
|
|
const firstNestedSuite = firstSuite.testsuite[0];
|
|
expect(firstNestedSuite.$.name).toBe("root describe");
|
|
expect(firstNestedSuite.$.file).toBe("test-2.test.js");
|
|
expect(firstNestedSuite.$.line).toBe("2");
|
|
|
|
expect(firstNestedSuite.testcase[0].$.name).toBe("should pass");
|
|
expect(firstNestedSuite.testcase[0].$.file).toBe("test-2.test.js");
|
|
expect(firstNestedSuite.testcase[0].$.line).toBe("3");
|
|
|
|
expect(secondSuite.$.name).toBe("passing.test.js");
|
|
expect(secondSuite.$.file).toBe("passing.test.js");
|
|
expect(secondSuite.$.tests).toBe("7");
|
|
expect(secondSuite.$.failures).toBe("2");
|
|
expect(secondSuite.$.skipped).toBe("2");
|
|
expect(Number.parseFloat(secondSuite.$.time)).toBeGreaterThanOrEqual(0.0);
|
|
|
|
const secondNestedSuite = secondSuite.testsuite[0];
|
|
expect(secondNestedSuite.$.name).toBe("root describe");
|
|
expect(secondNestedSuite.$.file).toBe("passing.test.js");
|
|
expect(secondNestedSuite.$.line).toBe("2");
|
|
|
|
const nestedTestCase = secondNestedSuite.testcase[0];
|
|
expect(nestedTestCase.$.name).toBe("should pass");
|
|
expect(nestedTestCase.$.file).toBe("passing.test.js");
|
|
expect(nestedTestCase.$.line).toBe("3");
|
|
|
|
expect(result.testsuites.$.tests).toBe("11");
|
|
expect(result.testsuites.$.failures).toBe("4");
|
|
expect(result.testsuites.$.skipped).toBe("2");
|
|
expect(Number.parseFloat(result.testsuites.$.time)).toBeGreaterThanOrEqual(0.0);
|
|
|
|
if (withCIEnvironmentVariables) {
|
|
expect(firstSuite.properties).toHaveLength(1);
|
|
expect(firstSuite.properties[0].property).toHaveLength(2);
|
|
expect(firstSuite.properties[0].property[0].$.name).toBe("ci");
|
|
expect(firstSuite.properties[0].property[0].$.value).toBe("https://ci.example.com/123");
|
|
expect(firstSuite.properties[0].property[1].$.name).toBe("commit");
|
|
expect(firstSuite.properties[0].property[1].$.value).toBe("1234567890");
|
|
}
|
|
});
|
|
|
|
it("more scenarios", async () => {
|
|
const tmpDir = tempDirWithFiles("junit-comprehensive", {
|
|
"package.json": "{}",
|
|
"comprehensive.test.js": `
|
|
import { test, expect, describe } from "bun:test";
|
|
|
|
describe("comprehensive test suite", () => {
|
|
test("basic passing test", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
test("basic failing test", () => {
|
|
expect(1 + 1).toBe(3);
|
|
});
|
|
|
|
test.skip("basic skipped test", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
test.todo("basic todo test");
|
|
|
|
test.each([
|
|
[1, 2, 3],
|
|
[2, 3, 5],
|
|
[4, 5, 9]
|
|
])("addition %i + %i = %i", (a, b, expected) => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
|
|
test.each([
|
|
["hello", "world", "helloworld"],
|
|
["foo", "bar", "foobar"]
|
|
])("string concat %s + %s = %s", (a, b, expected) => {
|
|
expect(a + b).toBe(expected);
|
|
});
|
|
|
|
test.if(true)("conditional test that runs", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
test.if(false)("conditional test that skips", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
test.skipIf(true)("skip if true", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
test.skipIf(false)("skip if false", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
test.todoIf(true)("todo if true");
|
|
|
|
test.todoIf(false)("todo if false", () => {
|
|
expect(1 + 1).toBe(2);
|
|
});
|
|
|
|
test.failing("test marked as failing", () => {
|
|
expect(1 + 1).toBe(3);
|
|
});
|
|
|
|
test("should match this test", () => {
|
|
expect(2 + 2).toBe(4);
|
|
});
|
|
|
|
test("should not be matched by filter", () => {
|
|
expect(3 + 3).toBe(6);
|
|
});
|
|
|
|
describe.each([
|
|
[10, 5],
|
|
[20, 10]
|
|
])("division suite %i / %i", (dividend, divisor) => {
|
|
test("should divide correctly", () => {
|
|
expect(dividend / divisor).toBe(dividend / divisor);
|
|
});
|
|
});
|
|
|
|
describe.if(true)("conditional describe that runs", () => {
|
|
test("nested test in conditional describe", () => {
|
|
expect(2 + 2).toBe(4);
|
|
});
|
|
});
|
|
|
|
describe.if(false)("conditional describe that skips", () => {
|
|
test("nested test that gets skipped", () => {
|
|
expect(2 + 2).toBe(4);
|
|
});
|
|
});
|
|
});
|
|
`,
|
|
});
|
|
|
|
const junitPath1 = `${tmpDir}/junit-all.xml`;
|
|
const proc1 = spawn([bunExe(), "test", "--reporter=junit", "--reporter-outfile", junitPath1], {
|
|
cwd: tmpDir,
|
|
env: { ...bunEnv, BUN_DEBUG_QUIET_LOGS: "1" },
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
await proc1.exited;
|
|
|
|
const xmlContent1 = await file(junitPath1).text();
|
|
const result1 = await new Promise((resolve, reject) => {
|
|
xml2js.parseString(xmlContent1, (err, result) => {
|
|
if (err) reject(err);
|
|
else resolve(result);
|
|
});
|
|
});
|
|
|
|
expect(result1.testsuites).toBeDefined();
|
|
expect(result1.testsuites.testsuite).toBeDefined();
|
|
|
|
const suite1 = result1.testsuites.testsuite[0];
|
|
expect(suite1.$.name).toBe("comprehensive.test.js");
|
|
expect(Number.parseInt(suite1.$.tests)).toBeGreaterThan(10);
|
|
|
|
const junitPath2 = `${tmpDir}/junit-filtered.xml`;
|
|
const proc2 = spawn(
|
|
[bunExe(), "test", "-t", "should match", "--reporter=junit", "--reporter-outfile", junitPath2],
|
|
{
|
|
cwd: tmpDir,
|
|
env: { ...bunEnv, BUN_DEBUG_QUIET_LOGS: "1" },
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
},
|
|
);
|
|
await proc2.exited;
|
|
|
|
const xmlContent2 = await file(junitPath2).text();
|
|
const result2 = await new Promise((resolve, reject) => {
|
|
xml2js.parseString(xmlContent2, (err, result) => {
|
|
if (err) reject(err);
|
|
else resolve(result);
|
|
});
|
|
});
|
|
|
|
const suite2 = result2.testsuites.testsuite[0];
|
|
expect(suite2.$.name).toBe("comprehensive.test.js");
|
|
expect(Number.parseInt(suite2.$.tests)).toBeGreaterThan(5);
|
|
expect(Number.parseInt(suite2.$.skipped)).toBeGreaterThan(3);
|
|
|
|
expect(xmlContent2).toContain("should match this test");
|
|
// even though it's not matched, juint should still include it
|
|
expect(xmlContent2).toContain("should not be matched by filter");
|
|
|
|
expect(xmlContent1).toContain("addition 1 + 2 = 3");
|
|
expect(xmlContent1).toContain("addition 2 + 3 = 5");
|
|
expect(xmlContent1).toContain("addition 4 + 5 = 9");
|
|
|
|
expect(xmlContent2).toContain("addition 1 + 2 = 3");
|
|
expect(xmlContent2).toContain("conditional describe that skips");
|
|
expect(xmlContent2).toContain("division suite 10 / 5");
|
|
expect(xmlContent2).toContain("division suite 20 / 10");
|
|
|
|
expect(xmlContent1).toContain("string concat hello + world = helloworld");
|
|
expect(xmlContent1).toContain("string concat foo + bar = foobar");
|
|
|
|
expect(xmlContent1).toContain("line=");
|
|
expect(xmlContent2).toContain("line=");
|
|
});
|
|
});
|