vitest-github-action
Version:
GitHub actions error and coverage reporter for vitest.
135 lines (131 loc) • 3.76 kB
JavaScript
// src/action.ts
import * as url from "url";
import { getInput } from "@actions/core";
import { startVitest } from "vitest/node";
import { join } from "path";
// src/GithubReporter.ts
import { endGroup, startGroup, error as actionsError } from "@actions/core";
var GithubReporter = class {
ctx;
onInit(ctx) {
this.ctx = ctx;
}
onFinished(files, _errors) {
if (!files?.length) {
return;
}
startGroup("Vitest annotations:");
const tests = this.identifyTests(files);
const failedTests = tests.filter(({ result }) => result?.state === "fail");
const formattedErrors = this.getFormattedErrors(failedTests);
formattedErrors.forEach((error) => {
actionsError(
error.stack ? `Stack trace:
${error.stack}` : "Vitest Error",
error.annotation
);
});
console.log("Formatted Errors", formattedErrors);
endGroup();
}
identifyTests(tasks, tests = []) {
tasks.forEach((task) => {
if (task.type === "suite") {
this.identifyTests(task.tasks, tests);
} else if (task.type === "test") {
tests.push(task);
}
});
return tests;
}
getFullNameOfTest(test, name = "") {
if (!test.suite) {
return "";
}
name = test.name;
const suiteName = this.getFullNameOfTest(test.suite, name);
return `${suiteName ? `${suiteName} > ` : ""}${name}`;
}
getAllErrors(tests) {
let errors = [];
tests.forEach((test) => {
const errs = test.result?.errors?.map((error) => ({
...error,
file: test.file,
testName: this.getFullNameOfTest(test)
}));
if (errs?.length) {
errors = errors.concat(errs);
}
});
return errors;
}
getErrorLocation(stackTrace, fileName) {
const errorLine = stackTrace.split("\n").find((stackTraceLine) => stackTraceLine.includes(fileName)) ?? "";
const bracketRegex = /\((.*):(\d+):(\d+)\)$/;
const atRegex = /at (.*):(\d+):(\d+)$/;
let match;
match = bracketRegex.exec(errorLine);
if (!match) {
match = atRegex.exec(errorLine);
}
if (match && match.length >= 3) {
return {
file: match[1],
line: parseInt(match[2], 10),
col: parseInt(match[3], 10)
};
}
}
getFormattedErrors(failedTests) {
const errors = this.getAllErrors(failedTests);
const formattedErrors = [];
errors.forEach((error) => {
if (!error?.stack) {
return;
}
error.stack = this.removeAnsiColors(error.stack);
error.message = this.removeAnsiColors(error.message);
error.name = this.removeAnsiColors(error.name);
const { file, line, col } = this.getErrorLocation(error.stack, error.file?.name ?? "") ?? {};
if (file && line && col) {
const annotation = {
file,
startLine: line,
startColumn: col,
title: `${error.name}: ${error.testName}`
};
formattedErrors.push({
annotation,
stack: error.stack
});
}
});
return formattedErrors;
}
removeAnsiColors(str) {
const colorRegex = /^$/;
return str.replace(colorRegex, "");
}
};
// src/action.ts
async function main() {
const configFile = getInput("config");
const coverage = Boolean(getInput("coverage") ?? true);
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
const vitest = await startVitest("test", [], {
watch: false,
config: configFile
}, {
test: {
reporters: [new GithubReporter(), "default"],
coverage: {
enabled: coverage,
provider: "custom",
customProviderModule: join(__dirname, "github-istanbul-coverage-provider")
}
}
});
await vitest?.close();
}
void main();