export class TeamcityReporter { /** * Escape special characters for TeamCity service messages * https://www.jetbrains.com/help/teamcity/service-messages.html#Escaped+values */ private static escape(str: string): string { return str .replace(/\|/g, "||") .replace(/'/g, "|'") .replace(/\n/g, "|n") .replace(/\r/g, "|r") .replace(/\[/g, "|[") .replace(/]/g, "|]"); } public static reportSuiteStart(suiteName: string): void { console.log( `##teamcity[testSuiteStarted name='${this.escape(suiteName)}']`, ); } public static reportSuiteEnd(suiteName: string): void { console.log( `##teamcity[testSuiteFinished name='${this.escape(suiteName)}']`, ); } public static reportTestStart(testName: string): void { console.log(`##teamcity[testStarted name='${this.escape(testName)}']`); } public static reportTestFailed( testName: string, failureMessage: string, details: string, { comparisonFailure, expected, actual, }: { comparisonFailure: boolean; expected: string; actual: string }, ): void { const attrs = [ `name='${this.escape(testName)}'`, `message='${this.escape(failureMessage)}'`, `details='${this.escape(details)}'`, ]; if (comparisonFailure) { attrs.push(`type='comparisonFailure'`); attrs.push(`expected='${this.escape(expected)}'`); attrs.push(`actual='${this.escape(actual)}'`); } console.log(`##teamcity[testFailed ${attrs.join(" ")}]`); } public static reportTestEnd(testName: string, duration?: number): void { const durationAttr = duration !== undefined ? ` duration='${duration}'` : ""; console.log( `##teamcity[testFinished name='${this.escape(testName)}'${durationAttr}]`, ); } public static reportTestError(testName: string, error: Error): void { const message = this.escape(error.message || "Unknown error"); const details = this.escape(error.stack || ""); console.log( `##teamcity[testFailed name='${this.escape(testName)}' message='${message}' details='${details}']`, ); } public static reportTestIgnored(testName: string, message?: string): void { const msgAttr = message ? ` message='${this.escape(message)}'` : ""; console.log( `##teamcity[testIgnored name='${this.escape(testName)}'${msgAttr}]`, ); } }