Implement TeamCity test reporting integration by adding new scripts and documentation. Update package.json to include a new test command for TeamCity. Introduce TeamcityReporter class for formatting service messages and enhance test setup for compatibility with TeamCity environment.
This commit is contained in:
109
scripts/test-teamcity.ts
Executable file
109
scripts/test-teamcity.ts
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { TeamcityReporter } from "../tests/TeamcityReporter.ts";
|
||||
|
||||
const isTeamCity = process.env.TEAMCITY_VERSION !== undefined;
|
||||
|
||||
// Strip ANSI color codes
|
||||
function stripAnsi(str: string): string {
|
||||
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
if (!isTeamCity) {
|
||||
// Just pass through if not in TeamCity
|
||||
const proc = Bun.spawn(["bun", "test", ...process.argv.slice(2)], {
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
env: { ...process.env },
|
||||
});
|
||||
const exitCode = await proc.exited;
|
||||
process.exit(exitCode);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error("TeamCity reporter enabled");
|
||||
|
||||
// Run tests and capture output
|
||||
const proc = Bun.spawn(["bun", "test", ...process.argv.slice(2)], {
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: { ...process.env },
|
||||
});
|
||||
|
||||
const suiteStack: string[] = [];
|
||||
|
||||
// Read stdout as text
|
||||
const output = await new Response(proc.stdout).text();
|
||||
const lines = output.split("\n");
|
||||
|
||||
for (const rawLine of lines) {
|
||||
// Pass through original output (with colors)
|
||||
console.log(rawLine);
|
||||
|
||||
// Parse without colors
|
||||
const line = stripAnsi(rawLine).trim();
|
||||
|
||||
// Check for test suite/describe blocks (files ending with .test.ts)
|
||||
if (line.match(/tests\/.+\.test\.ts:$/)) {
|
||||
const fileName = line.replace(":", "").replace("tests/", "").replace(".test.ts", "");
|
||||
|
||||
// End previous suite if exists
|
||||
while (suiteStack.length > 0) {
|
||||
const oldSuite = suiteStack.pop();
|
||||
if (oldSuite) {
|
||||
TeamcityReporter.reportSuiteEnd(oldSuite);
|
||||
}
|
||||
}
|
||||
|
||||
suiteStack.push(fileName);
|
||||
TeamcityReporter.reportSuiteStart(fileName);
|
||||
}
|
||||
|
||||
// Check for test pass: (pass) HashMap > constructor > should create an empty map...
|
||||
const passMatch = line.match(/^\(pass\)\s+(.+?)(?:\s+\[[\d.]+m?s\])?$/);
|
||||
if (passMatch) {
|
||||
const testName = passMatch[1].trim();
|
||||
TeamcityReporter.reportTestStart(testName);
|
||||
TeamcityReporter.reportTestEnd(testName);
|
||||
}
|
||||
|
||||
// Check for test fail: (fail) HashMap > constructor > should...
|
||||
const failMatch = line.match(/^\(fail\)\s+(.+?)(?:\s+\[[\d.]+m?s\])?$/);
|
||||
if (failMatch) {
|
||||
const testName = failMatch[1].trim();
|
||||
TeamcityReporter.reportTestStart(testName);
|
||||
TeamcityReporter.reportTestFailed(
|
||||
testName,
|
||||
"Test failed",
|
||||
line,
|
||||
{ comparisonFailure: false, expected: "", actual: "" }
|
||||
);
|
||||
TeamcityReporter.reportTestEnd(testName);
|
||||
}
|
||||
}
|
||||
|
||||
// Capture stderr
|
||||
const errorOutput = await new Response(proc.stderr).text();
|
||||
if (errorOutput) {
|
||||
console.error(errorOutput);
|
||||
}
|
||||
|
||||
const exitCode = await proc.exited;
|
||||
|
||||
// Close all open suites
|
||||
while (suiteStack.length > 0) {
|
||||
const suite = suiteStack.pop();
|
||||
if (suite) {
|
||||
TeamcityReporter.reportSuiteEnd(suite);
|
||||
}
|
||||
}
|
||||
|
||||
console.error("TeamCity reporter finished");
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
runTests().catch((error) => {
|
||||
console.error("Error running tests:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user