coder/site/e2e/reporter.ts

148 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable no-console -- Logging is sort of the whole point here */
import type {
FullConfig,
Suite,
TestCase,
TestResult,
FullResult,
Reporter,
TestError,
} from "@playwright/test/reporter";
import axios from "axios";
import * as fs from "fs/promises";
import type { Writable } from "stream";
class CoderReporter implements Reporter {
config: FullConfig | null = null;
testOutput = new Map<string, Array<[Writable, string]>>();
passedCount = 0;
failedTests: TestCase[] = [];
timedOutTests: TestCase[] = [];
onBegin(config: FullConfig, suite: Suite) {
this.config = config;
console.log(`==> Running ${suite.allTests().length} tests`);
}
onTestBegin(test: TestCase) {
this.testOutput.set(test.id, []);
console.log(`==> Starting test ${test.title}`);
}
onStdOut(chunk: string, test?: TestCase, _?: TestResult): void {
for (const line of filteredServerLogLines(chunk)) {
console.log(`[stdout] ${line}`);
}
if (!test) {
return;
}
this.testOutput.get(test.id)!.push([process.stdout, chunk]);
}
onStdErr(chunk: string, test?: TestCase, _?: TestResult): void {
for (const line of filteredServerLogLines(chunk)) {
console.error(`[stderr] ${line}`);
}
if (!test) {
return;
}
this.testOutput.get(test.id)!.push([process.stderr, chunk]);
}
async onTestEnd(test: TestCase, result: TestResult) {
console.log(`==> Finished test ${test.title}: ${result.status}`);
if (result.status === "passed") {
this.passedCount++;
}
if (result.status === "failed") {
this.failedTests.push(test);
}
if (result.status === "timedOut") {
this.timedOutTests.push(test);
}
const fsTestTitle = test.title.replaceAll(" ", "-");
const outputFile = `test-results/debug-pprof-goroutine-${fsTestTitle}.txt`;
await exportDebugPprof(outputFile);
if (result.status !== "passed") {
console.log(`Data from pprof has been saved to ${outputFile}`);
console.log("==> Output");
const output = this.testOutput.get(test.id)!;
for (const [target, chunk] of output) {
target.write(`${chunk.replace(/\n$/g, "")}\n`);
}
if (result.errors.length > 0) {
console.log("==> Errors");
for (const error of result.errors) {
reportError(error);
}
}
if (result.attachments.length > 0) {
console.log("==> Attachments");
for (const attachment of result.attachments) {
console.log(attachment);
}
}
}
this.testOutput.delete(test.id);
}
onEnd(result: FullResult) {
console.log(`==> Tests ${result.status}`);
console.log(`${this.passedCount} passed`);
if (this.failedTests.length > 0) {
console.log(`${this.failedTests.length} failed`);
for (const test of this.failedTests) {
console.log(` ${test.location.file} ${test.title}`);
}
}
if (this.timedOutTests.length > 0) {
console.log(`${this.timedOutTests.length} timed out`);
for (const test of this.timedOutTests) {
console.log(` ${test.location.file} ${test.title}`);
}
}
}
}
const shouldPrintLine = (line: string) =>
[" error=EOF", "coderd: audit_log"].every((noise) => !line.includes(noise));
const filteredServerLogLines = (chunk: string): string[] =>
chunk.trimEnd().split("\n").filter(shouldPrintLine);
const exportDebugPprof = async (outputFile: string) => {
const response = await axios.get(
"http://127.0.0.1:6062/debug/pprof/goroutine?debug=1",
);
if (response.status !== 200) {
throw new Error(`Error: Received status code ${response.status}`);
}
await fs.writeFile(outputFile, response.data);
};
const reportError = (error: TestError) => {
if (error.location) {
console.log(`${error.location.file}:${error.location.line}:`);
}
if (error.snippet) {
console.log(error.snippet);
}
if (error.message) {
console.log(error.message);
} else {
console.log(error);
}
};
// eslint-disable-next-line no-unused-vars -- Playwright config uses it
export default CoderReporter;