@sentinelqa/playwright-reporter 0.1.33 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/reporter.js +27 -13
- package/dist/runHistory.d.ts +7 -0
- package/dist/runHistory.js +93 -0
- package/package.json +1 -1
package/dist/reporter.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const node_1 = require("@sentinelqa/uploader/node");
|
|
3
3
|
const env_1 = require("./env");
|
|
4
4
|
const quickDiagnosis_1 = require("./quickDiagnosis");
|
|
5
|
+
const runHistory_1 = require("./runHistory");
|
|
5
6
|
const { sentinelCaptureFailureContextFromReporter } = require("@sentinelqa/uploader/playwright");
|
|
6
7
|
const colorize = (value, code) => {
|
|
7
8
|
if (!process.stdout.isTTY)
|
|
@@ -43,19 +44,31 @@ class SentinelReporter {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
async onEnd() {
|
|
46
|
-
|
|
47
|
-
console.log(green("✔ Artifacts collected"));
|
|
47
|
+
const hasWorkspaceToken = Boolean(process.env.SENTINEL_TOKEN);
|
|
48
48
|
const quickDiagnosis = (0, quickDiagnosis_1.buildQuickDiagnosis)(this.options.playwrightJsonPath);
|
|
49
|
+
const runDiff = (0, runHistory_1.buildRunDiffSummary)(this.options.playwrightJsonPath);
|
|
50
|
+
console.log("");
|
|
49
51
|
if (quickDiagnosis?.lines.length) {
|
|
50
|
-
console.log("");
|
|
51
52
|
console.log(yellow("Quick diagnosis"));
|
|
52
53
|
for (const line of quickDiagnosis.lines) {
|
|
53
54
|
console.log(` ${dim(line)}`);
|
|
54
55
|
}
|
|
56
|
+
console.log("");
|
|
57
|
+
}
|
|
58
|
+
if (runDiff) {
|
|
59
|
+
console.log(yellow("Run-to-run diff"));
|
|
60
|
+
console.log(` ${dim(`New failures: ${runDiff.newFailures}`)}`);
|
|
61
|
+
console.log(` ${dim(`Fixed since last run: ${runDiff.fixedTests}`)}`);
|
|
62
|
+
console.log(` ${dim(`Still failing: ${runDiff.stillFailing}`)}`);
|
|
63
|
+
console.log("");
|
|
64
|
+
}
|
|
65
|
+
if (hasWorkspaceToken) {
|
|
66
|
+
console.log("");
|
|
67
|
+
console.log(green("✔ Artifacts collected"));
|
|
68
|
+
console.log("");
|
|
69
|
+
console.log("Uploading hosted debugging report to Sentinel...");
|
|
70
|
+
console.log("");
|
|
55
71
|
}
|
|
56
|
-
console.log("");
|
|
57
|
-
console.log("Uploading hosted debugging report to Sentinel...");
|
|
58
|
-
console.log("");
|
|
59
72
|
const upload = await (0, node_1.runSentinelUpload)({
|
|
60
73
|
playwrightJsonPath: this.options.playwrightJsonPath,
|
|
61
74
|
playwrightReportDir: this.options.playwrightReportDir,
|
|
@@ -71,14 +84,15 @@ class SentinelReporter {
|
|
|
71
84
|
throw new Error(`Sentinel upload failed with exit code ${upload.exitCode}`);
|
|
72
85
|
}
|
|
73
86
|
console.log("");
|
|
74
|
-
console.log("
|
|
75
|
-
|
|
87
|
+
console.log("Sentinel report");
|
|
88
|
+
console.log(` ${upload.shareRunUrl || upload.internalRunUrl}`);
|
|
89
|
+
if (upload.shareLabel) {
|
|
90
|
+
console.log(` ${dim(upload.shareLabel)}`);
|
|
91
|
+
}
|
|
92
|
+
if (!hasWorkspaceToken) {
|
|
76
93
|
console.log("");
|
|
77
|
-
console.log("
|
|
78
|
-
console.log(` ${
|
|
79
|
-
if (upload.shareLabel) {
|
|
80
|
-
console.log(` ${dim(upload.shareLabel)}`);
|
|
81
|
-
}
|
|
94
|
+
console.log("Upgrade for free to get full AI debugging suggestions");
|
|
95
|
+
console.log(` ${dim("https://app.sentinelqa.com/register")}`);
|
|
82
96
|
}
|
|
83
97
|
}
|
|
84
98
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildRunDiffSummary = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const quickDiagnosis_1 = require("./quickDiagnosis");
|
|
10
|
+
const SENTINEL_HISTORY_DIR = node_path_1.default.join(".sentinel", "history");
|
|
11
|
+
const ensureDir = (dirPath) => {
|
|
12
|
+
if (!node_fs_1.default.existsSync(dirPath)) {
|
|
13
|
+
node_fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const getCurrentBranch = () => {
|
|
17
|
+
const fromEnv = process.env.GITHUB_REF_NAME ||
|
|
18
|
+
process.env.CI_COMMIT_REF_NAME ||
|
|
19
|
+
process.env.CI_COMMIT_BRANCH ||
|
|
20
|
+
process.env.BRANCH_NAME ||
|
|
21
|
+
null;
|
|
22
|
+
return fromEnv && fromEnv.trim() ? fromEnv.trim() : "main";
|
|
23
|
+
};
|
|
24
|
+
const getCurrentGitSha = () => {
|
|
25
|
+
const fromEnv = process.env.GITHUB_SHA ||
|
|
26
|
+
process.env.CI_COMMIT_SHA ||
|
|
27
|
+
process.env.VERCEL_GIT_COMMIT_SHA ||
|
|
28
|
+
null;
|
|
29
|
+
return fromEnv && fromEnv.trim() ? fromEnv.trim() : "unknown";
|
|
30
|
+
};
|
|
31
|
+
const buildMatchKey = (failure) => [
|
|
32
|
+
failure.signal,
|
|
33
|
+
failure.locator || "unknown-locator",
|
|
34
|
+
failure.expected || "unknown-expected",
|
|
35
|
+
failure.received || "unknown-received",
|
|
36
|
+
failure.titlePath.join(" > ") || failure.title
|
|
37
|
+
].join("|");
|
|
38
|
+
const buildSnapshot = (playwrightJsonPath) => {
|
|
39
|
+
const failures = (0, quickDiagnosis_1.collectFailureFacts)(playwrightJsonPath);
|
|
40
|
+
return {
|
|
41
|
+
generatedAt: new Date().toISOString(),
|
|
42
|
+
branch: getCurrentBranch(),
|
|
43
|
+
gitSha: getCurrentGitSha(),
|
|
44
|
+
failures: failures.map((failure) => ({
|
|
45
|
+
id: failure.titlePath.join(" > ") || failure.title,
|
|
46
|
+
matchKey: buildMatchKey(failure),
|
|
47
|
+
title: failure.title,
|
|
48
|
+
status: failure.status
|
|
49
|
+
}))
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
const getPointerPaths = (branch) => [
|
|
53
|
+
node_path_1.default.join(".sentinel", "latest.json"),
|
|
54
|
+
node_path_1.default.join(".sentinel", `latest-${branch}.json`),
|
|
55
|
+
...(branch === "main" ? [node_path_1.default.join(".sentinel", "latest-main.json")] : [])
|
|
56
|
+
];
|
|
57
|
+
const readSnapshot = (filePath) => {
|
|
58
|
+
if (!node_fs_1.default.existsSync(filePath))
|
|
59
|
+
return null;
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(node_fs_1.default.readFileSync(filePath, "utf8"));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const writeSnapshot = (snapshot) => {
|
|
68
|
+
ensureDir(node_path_1.default.resolve(process.cwd(), ".sentinel"));
|
|
69
|
+
ensureDir(node_path_1.default.resolve(process.cwd(), SENTINEL_HISTORY_DIR));
|
|
70
|
+
const fileName = `${snapshot.generatedAt.replace(/[:.]/g, "-")}-${snapshot.gitSha}.json`;
|
|
71
|
+
const historyPath = node_path_1.default.resolve(process.cwd(), SENTINEL_HISTORY_DIR, fileName);
|
|
72
|
+
node_fs_1.default.writeFileSync(historyPath, JSON.stringify(snapshot, null, 2), "utf8");
|
|
73
|
+
for (const pointerPath of getPointerPaths(snapshot.branch)) {
|
|
74
|
+
node_fs_1.default.writeFileSync(node_path_1.default.resolve(process.cwd(), pointerPath), JSON.stringify({ path: historyPath, ...snapshot }, null, 2), "utf8");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const buildRunDiffSummary = (playwrightJsonPath) => {
|
|
78
|
+
const snapshot = buildSnapshot(playwrightJsonPath);
|
|
79
|
+
const previous = readSnapshot(node_path_1.default.resolve(process.cwd(), ".sentinel", `latest-${snapshot.branch}.json`)) ||
|
|
80
|
+
readSnapshot(node_path_1.default.resolve(process.cwd(), ".sentinel", "latest.json"));
|
|
81
|
+
const currentFailureIds = new Set(snapshot.failures.map((test) => test.id));
|
|
82
|
+
const currentFailureMatchKeys = new Set(snapshot.failures.map((test) => test.matchKey));
|
|
83
|
+
const diff = previous && previous.generatedAt !== snapshot.generatedAt
|
|
84
|
+
? {
|
|
85
|
+
newFailures: snapshot.failures.filter((test) => !previous.failures.some((prev) => prev.id === test.id || prev.matchKey === test.matchKey)).length,
|
|
86
|
+
fixedTests: previous.failures.filter((test) => !currentFailureIds.has(test.id) && !currentFailureMatchKeys.has(test.matchKey)).length,
|
|
87
|
+
stillFailing: snapshot.failures.filter((test) => previous.failures.some((prev) => prev.id === test.id || prev.matchKey === test.matchKey)).length
|
|
88
|
+
}
|
|
89
|
+
: null;
|
|
90
|
+
writeSnapshot(snapshot);
|
|
91
|
+
return diff;
|
|
92
|
+
};
|
|
93
|
+
exports.buildRunDiffSummary = buildRunDiffSummary;
|