UNPKG

vitest-teamcity-reporter

Version:

A TeamCity reporter for vitest.

289 lines (288 loc) 9.16 kB
//#region src/app/error/missing-result.error.ts var MissingResultError = class extends Error { constructor(testCase) { super(`Test: "${testCase.fullName}" from - "${testCase.module.relativeModuleId}" missing a result after file test process finished`); } }; //#endregion //#region src/app/escape.ts const escapeSpecials = (str = "") => { return str.toString().replace(/\x1B.*?m/g, "").replace(/\|/g, "||").replace(/\n/g, "|n").replace(/\r/g, "|r").replace(/\[/g, "|[").replace(/]/g, "|]").replace(/\u0085/g, "|x").replace(/\u2028/g, "|l").replace(/\u2029/g, "|p").replace(/'/g, "|'"); }; //#endregion //#region src/app/messages/message.ts var Message = class { constructor(id, name) { this.id = id; this.name = name; } generateParameters(parameters) { return Object.entries(parameters).map(([key, value]) => `${key}='${escapeSpecials(value)}'`).join(" "); } generateTeamcityMessage(type, flowId, parameters) { return `##teamcity[${type} flowId='${flowId}' ${this.generateParameters(parameters)}]`; } }; //#endregion //#region src/app/messages/run-error-message.ts var RunErrorMessage = class extends Message { constructor(flowId, name, identity) { super(flowId, name); this.identity = identity; } generate(type, parameters = {}) { return this.generateTeamcityMessage(type, this.id, { ...parameters, name: this.name }); } started() { return this.generate("testStarted"); } failed(error) { const message = this.getErrorMessage(error); return this.generate("testFailed", { message, details: error.stack ?? message }); } finished() { return this.generate("testFinished", { duration: 0 }); } buildProblem(error) { return `##teamcity[buildProblem description='${escapeSpecials(`Vitest unhandled error: ${this.getErrorMessage(error)}`)}' identity='${escapeSpecials(this.identity)}']`; } getErrorMessage(error) { return error.message || "Unknown unhandled error"; } }; //#endregion //#region src/app/messages/suite-message.ts var SuiteMessage = class extends Message { generate(type, parameters = {}) { return this.generateTeamcityMessage(type, this.id, { ...parameters, name: this.name }); } started() { return this.generate("testSuiteStarted"); } finished() { return this.generate("testSuiteFinished"); } }; //#endregion //#region src/app/messages/test-message.ts var TestMessage = class extends Message { constructor(testCase) { super(testCase.module.moduleId, testCase.name); } generate(type, parameters = {}) { return this.generateTeamcityMessage(type, this.id, { ...parameters, name: this.name }); } fail(error) { return this.generate("testFailed", { message: error.message, details: error.stack ?? "", actual: String(error.actual ?? ""), expected: String(error.expected ?? "") }); } started() { return this.generate("testStarted"); } finished(duration) { return this.generate("testFinished", { duration }); } ignored() { return this.generate("testIgnored"); } stdOut(out) { return this.generate("testStdOut", { out }); } stdErr(out) { return this.generate("testStdErr", { out }); } log(type, out) { return type === "stdout" ? this.stdOut(out) : this.stdErr(out); } }; //#endregion //#region src/app/messages/test-metadata-message.ts var TestMetadataMessage = class extends Message { constructor(testCase) { super(testCase.module.moduleId, testCase.name); this.testCase = testCase; } generate(type, parameters = {}) { return this.generateTeamcityMessage(type, this.id, parameters); } sourceFile() { return this.generate("testMetadata", { name: "sourceFile", value: this.testCase.module.relativeModuleId }); } vitestFullName() { return this.generate("testMetadata", { name: "vitestFullName", value: this.testCase.fullName }); } }; //#endregion //#region src/app/printer.ts var Printer = class { constructor(logger) { this.logger = logger; this.testConsoleMap = /* @__PURE__ */ new Map(); this.reportedSuites = /* @__PURE__ */ new Set(); this.startedTests = /* @__PURE__ */ new Set(); } onModuleCollected(testModule) { const suiteMessage = new SuiteMessage(escapeSpecials(testModule.moduleId), escapeSpecials(testModule.relativeModuleId)); this.log(suiteMessage.started()); this.reportedSuites.add(testModule.moduleId); } onSuiteReady(testSuite) { if (this.isSkippedOrTodo(testSuite)) return; const suiteMessage = new SuiteMessage(escapeSpecials(testSuite.module.moduleId), escapeSpecials(testSuite.name)); this.log(suiteMessage.started()); this.reportedSuites.add(testSuite.id); } onTestReady(testCase) { if (!this.isTestInReportedSuite(testCase)) return; if (testCase.result().state === "skipped") { const testMessage = new TestMessage(testCase); this.log(testMessage.ignored()); return; } const testMessage = new TestMessage(testCase); this.log(testMessage.started()); this.startedTests.add(testCase.id); } onTestResult(testCase) { if (!this.isTestInReportedSuite(testCase)) return; if (this.isSkippedOrTodo(testCase)) return; const testMessage = new TestMessage(testCase); if (!this.startedTests.has(testCase.id)) this.log(testMessage.started()); this.startedTests.delete(testCase.id); const metadataMessage = new TestMetadataMessage(testCase); this.log(metadataMessage.sourceFile()); this.log(metadataMessage.vitestFullName()); const result = testCase.result(); (this.testConsoleMap.get(testCase.id) ?? []).forEach((log) => { this.log(testMessage.log(log.type, log.content)); }); this.testConsoleMap.delete(testCase.id); const errors = this.getTestErrors(testCase); const hasRealErrors = errors.length > 0 && !(errors[0] instanceof MissingResultError); if (result.state === "failed" || hasRealErrors) errors.forEach((error) => { this.log(testMessage.fail(error)); }); const diagnostic = testCase.diagnostic(); this.log(testMessage.finished(diagnostic?.duration ?? 0)); } onSuiteResult(testSuite) { if (this.isSkippedOrTodo(testSuite)) return; const suiteMessage = new SuiteMessage(escapeSpecials(testSuite.module.moduleId), escapeSpecials(testSuite.name)); this.log(suiteMessage.finished()); this.reportedSuites.delete(testSuite.id); } onModuleEnd(testModule) { const suiteMessage = new SuiteMessage(escapeSpecials(testModule.moduleId), escapeSpecials(testModule.moduleId)); this.log(suiteMessage.finished()); this.reportedSuites.delete(testModule.moduleId); } onTestRunEnd(unhandledErrors) { if (unhandledErrors.length === 0) return; const flowId = "vitest-unhandled-errors"; const suiteMessage = new SuiteMessage(flowId, "Vitest unhandled errors"); this.log(suiteMessage.started()); unhandledErrors.forEach((error, index) => { const errorMessage = new RunErrorMessage(flowId, `Unhandled error ${index + 1}`, `vitest-unhandled-error-${index}`); this.log(errorMessage.started()); this.log(errorMessage.failed(error)); this.log(errorMessage.finished()); this.log(errorMessage.buildProblem(error)); }); this.log(suiteMessage.finished()); } addTestConsoleLog(id, log) { const messages = this.testConsoleMap.get(id); if (messages != null) messages.push(log); else this.testConsoleMap.set(id, [log]); } log(message) { this.logger.console.info(message); } isSkippedOrTodo(item) { if (item.options.mode === void 0) return false; return ["skip", "todo"].includes(item.options.mode); } isTestInReportedSuite(testCase) { let current = testCase; while (current.type !== "module") { if (current.type === "suite" && !this.reportedSuites.has(current.id)) return false; current = current.parent; } return true; } getTestErrors(testCase) { const result = testCase.result(); if (result.errors !== void 0 && result.errors.length > 0) return [...result.errors]; let current = testCase.parent; while (current.type !== "module") { if (current.type === "suite") { const suiteErrors = current.errors(); if (suiteErrors.length > 0) return suiteErrors; } current = current.parent; } const moduleErrors = current.errors(); if (moduleErrors.length > 0) return moduleErrors; return [new MissingResultError(testCase)]; } }; //#endregion //#region src/app/reporter.ts var TeamCityReporter = class { onInit(ctx) { this.logger = ctx.logger; this.printer = new Printer(this.logger); } onTestModuleCollected(testModule) { this.printer.onModuleCollected(testModule); } onTestSuiteReady(testSuite) { this.printer.onSuiteReady(testSuite); } onTestCaseReady(testCase) { this.printer.onTestReady(testCase); } onTestCaseResult(testCase) { this.printer.onTestResult(testCase); } onTestSuiteResult(testSuite) { this.printer.onSuiteResult(testSuite); } onTestModuleEnd(testModule) { this.printer.onModuleEnd(testModule); } onTestRunEnd(_testModules, unhandledErrors) { this.printer.onTestRunEnd(unhandledErrors); } onUserConsoleLog(log) { if (log.taskId != null) this.printer.addTestConsoleLog(log.taskId, log); else this.logger.console.log(log); } }; //#endregion //#region src/app/index.ts var app_default = TeamCityReporter; //#endregion module.exports = app_default;