Skip to content

Commit f948375

Browse files
authored
JUnit Support (#133)
1 parent 6848cc3 commit f948375

File tree

2 files changed

+81
-42
lines changed

2 files changed

+81
-42
lines changed

src/cli.ts

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import detectPort from "detect-port";
1717
import { spawn } from "node:child_process";
1818
import { hideBin } from "yargs/helpers";
1919
import { run } from "node:test";
20-
import { tap } from "node:test/reporters";
20+
import { junit, tap } from "node:test/reporters";
2121
import { styleText } from "node:util";
2222
import { killPortProcess } from "kill-port-process";
2323
import { dirname, extname, join } from "node:path";
@@ -184,7 +184,7 @@ yargs(hideBin(process.argv))
184184

185185
process.stdout.write("\n");
186186

187-
await killPortIfRunning(readPort(argv.graphql)).catch(() => {});
187+
await killPortIfRunning(readPort(argv.graphql)).catch(() => { });
188188

189189
const gatewayExit = Promise.withResolvers<void>();
190190
let gatewayExited = false;
@@ -245,6 +245,10 @@ yargs(hideBin(process.argv))
245245
choices: ["dot", "tap"],
246246
default: "tap",
247247
})
248+
.option("junit", {
249+
describe: "Write test results to a JUnit XML file",
250+
type: "boolean",
251+
})
248252
.demandOption("test")
249253
.demandOption("graphql")
250254
.demandOption("healthcheck")
@@ -274,7 +278,7 @@ yargs(hideBin(process.argv))
274278
mkdirSync(resolvePath(argv, "./logs"));
275279
}
276280

277-
await killPortIfRunning(readPort(argv.graphql)).catch(() => {});
281+
await killPortIfRunning(readPort(argv.graphql)).catch(() => { });
278282
const logStream = createWriteStream(
279283
resolvePath(argv, `./logs/${argv.test}-gateway.log`),
280284
{
@@ -301,6 +305,7 @@ yargs(hideBin(process.argv))
301305
const result = await runTest({
302306
...argv,
303307
reporter: argv.reporter === "tap" ? "tap" : "dot",
308+
junit: argv.junit,
304309
port,
305310
});
306311

@@ -348,6 +353,10 @@ yargs(hideBin(process.argv))
348353
choices: ["dot", "tap"],
349354
default: "dot",
350355
})
356+
.option("junit", {
357+
describe: "Write test results to a JUnit XML file",
358+
type: "boolean",
359+
})
351360
.option("write", {
352361
describe: "Write test results to a file",
353362
type: "string",
@@ -398,7 +407,7 @@ yargs(hideBin(process.argv))
398407

399408
process.stdout.write("\n");
400409
for await (const id of ids) {
401-
await killPortIfRunning(readPort(argv.graphql)).catch(() => {});
410+
await killPortIfRunning(readPort(argv.graphql)).catch(() => { });
402411
const logStream = createWriteStream(
403412
resolvePath(argv, `./logs/${id}-gateway.log`),
404413
{
@@ -428,6 +437,7 @@ yargs(hideBin(process.argv))
428437
healthcheck: argv.healthcheck,
429438
port: argv.port,
430439
reporter: argv.reporter === "tap" ? "tap" : "dot",
440+
junit: argv.junit,
431441
cwd: argv.cwd,
432442
});
433443
results.push({ id, result });
@@ -507,11 +517,13 @@ async function runTest(args: {
507517
healthcheck?: string;
508518
port: number;
509519
reporter?: "dot" | "tap";
520+
junit?: boolean;
510521
cwd: string;
511522
}): Promise<Array<"." | "X">> {
512523
process.stdout.write(`${args.test}\n`);
513524
process.env.TESTS_ENDPOINT = `http://localhost:${args.port}/${args.test}/tests`;
514525
process.env.GRAPHQL_ENDPOINT = args.graphql;
526+
process.env.TEST_SUITE = args.test;
515527

516528
const logStream = createWriteStream(
517529
resolvePath({ cwd: args.cwd }, `./logs/${args.test}-tests.log`),
@@ -543,13 +555,34 @@ async function runTest(args: {
543555
testStream.compose(tap).pipe(logStream);
544556
testStream.compose(dotan);
545557

558+
if (args.junit) {
559+
const reportsDir = resolvePath({ cwd: args.cwd }, "reports");
560+
if (!existsSync(reportsDir)) {
561+
mkdirSync(reportsDir, { recursive: true });
562+
}
563+
const junitPath = join(
564+
reportsDir,
565+
`${args.test}.xml`
566+
);
567+
const junitStream = createWriteStream(
568+
junitPath,
569+
{
570+
flags: "w+",
571+
}
572+
);
573+
testStream.compose(junit).pipe(junitStream);
574+
}
575+
546576
return promise;
547577
}
548578

549579
function createDotReporter(resolve: (value: Array<"." | "X">) => void) {
550580
const report: Array<"." | "X"> = [];
551581
return async function* dot(source: Parameters<typeof tap>[0]) {
552-
for await (const { type } of source) {
582+
for await (const { type, data } of source) {
583+
if (data != null && 'details' in data && data.details?.type === "suite") {
584+
continue;
585+
}
553586
if (type === "test:pass") {
554587
report.push(".");
555588
}
@@ -561,11 +594,14 @@ function createDotReporter(resolve: (value: Array<"." | "X">) => void) {
561594
};
562595
}
563596

564-
async function* dot(source: any) {
597+
async function* dot(source: Parameters<typeof tap>[0]) {
565598
let count = 0;
566599
let columns = getLineLength();
567600
const failedTests = [];
568601
for await (const { type, data } of source) {
602+
if (data != null && 'details' in data && data.details?.type === "suite") {
603+
continue;
604+
}
569605
if (type === "test:pass") {
570606
yield styleText("green", ".");
571607
}

src/test.ts

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ExecutionResult } from "graphql";
22
import type { createTest } from "./testkit.js";
33
import { diff } from "jest-diff";
4-
import { test } from "node:test";
4+
import { it, describe } from "node:test";
55
import { deepStrictEqual } from "node:assert";
66

77
async function fetchTests(endpoint: string) {
@@ -23,6 +23,7 @@ async function fetchTests(endpoint: string) {
2323
return result as Array<ReturnType<typeof createTest>>;
2424
}
2525

26+
const TEST_SUITE = process.env.TEST_SUITE;
2627
const TESTS_ENDPOINT = process.env.TESTS_ENDPOINT;
2728
const GRAPHQL_ENDPOINT = process.env.GRAPHQL_ENDPOINT;
2829

@@ -36,43 +37,45 @@ console.log(`\n`);
3637
const tests = await fetchTests(TESTS_ENDPOINT);
3738

3839
let index = 0;
39-
for (const { query, expected: expectedResult } of tests) {
40-
test(`${index++}`, async () => {
41-
const response = await graphql(GRAPHQL_ENDPOINT, query);
42-
43-
const errorsOptional = typeof expectedResult.errors !== "boolean";
44-
45-
const received = {
46-
data: response.data ?? null,
47-
errors: errorsOptional ? null : response.errors?.length ? true : false,
48-
};
49-
50-
const expected = {
51-
data: expectedResult.data ?? null,
52-
errors: errorsOptional ? null : (expectedResult.errors ?? false),
53-
};
54-
55-
let retryCount = 0;
56-
const maxRetries = 2;
57-
let testPassed = false;
58-
59-
while (retryCount <= maxRetries && !testPassed) {
60-
try {
61-
deepStrictEqual(
62-
received,
63-
expected,
64-
[`Test failed for query`, query, diff(expected, received)].join("\n")
65-
);
66-
testPassed = true;
67-
} catch (error) {
68-
if (retryCount === maxRetries) {
69-
throw error;
40+
describe(TEST_SUITE, () => {
41+
for (const { query, expected: expectedResult } of tests) {
42+
it(`${TEST_SUITE}_${index++}`, async () => {
43+
const response = await graphql(GRAPHQL_ENDPOINT, query);
44+
45+
const errorsOptional = typeof expectedResult.errors !== "boolean";
46+
47+
const received = {
48+
data: response.data ?? null,
49+
errors: errorsOptional ? null : response.errors?.length ? true : false,
50+
};
51+
52+
const expected = {
53+
data: expectedResult.data ?? null,
54+
errors: errorsOptional ? null : (expectedResult.errors ?? false),
55+
};
56+
57+
let retryCount = 0;
58+
const maxRetries = 2;
59+
let testPassed = false;
60+
61+
while (retryCount <= maxRetries && !testPassed) {
62+
try {
63+
deepStrictEqual(
64+
received,
65+
expected,
66+
[`Test failed for query`, query, diff(expected, received)].join("\n")
67+
);
68+
testPassed = true;
69+
} catch (error) {
70+
if (retryCount === maxRetries) {
71+
throw error;
72+
}
73+
retryCount++;
7074
}
71-
retryCount++;
7275
}
73-
}
74-
});
75-
}
76+
});
77+
}
78+
})
7679

7780
function graphql(endpoint: string, query: string) {
7881
return fetch(endpoint, {

0 commit comments

Comments
 (0)