@sentinelqa/playwright-reporter 0.1.29 → 0.1.32
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/README.md +69 -52
- package/dist/index.d.ts +0 -3
- package/dist/index.js +1 -4
- package/dist/localReport.js +44 -16
- package/dist/quickDiagnosis.js +2 -2
- package/dist/reporter.d.ts +0 -4
- package/dist/reporter.js +15 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,38 +1,53 @@
|
|
|
1
1
|
# Playwright Reporter
|
|
2
2
|
|
|
3
|
+
After every failed run, Sentinel prints a shareable debugging link:
|
|
4
|
+
|
|
5
|
+
👉 https://sentinelqa.com/run/abc123
|
|
6
|
+
|
|
7
|
+
Open it to inspect failures instantly or share it in Slack, PRs, or GitHub issues.
|
|
8
|
+
|
|
3
9
|
[](https://www.npmjs.com/package/@sentinelqa/playwright-reporter)
|
|
4
10
|
[](https://www.npmjs.com/package/@sentinelqa/playwright-reporter)
|
|
5
11
|
[](./LICENSE)
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
From failed CI run → root cause in seconds.
|
|
14
|
+
Get a shareable Playwright debugging link with traces, screenshots, and failure context — no setup required.
|
|
9
15
|
|
|
10
|
-
Works
|
|
16
|
+
Works with no account or API key required.
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
Use it to get a shareable hosted run link from CI or local development, then upgrade to Sentinel Cloud for richer history and intelligence.
|
|
13
19
|
|
|
14
20
|

|
|
21
|
+

|
|
22
|
+

|
|
15
23
|
|
|
16
24
|
## Features
|
|
17
25
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
26
|
+
- Free hosted debugging links by default, with no account or API key required
|
|
27
|
+
- Public run page that opens on unified failures across the run
|
|
28
|
+
- Within-run failure grouping so repeated failures collapse into one issue
|
|
29
|
+
- Public failure pages with screenshots, evidence, parsed errors and light summaries
|
|
30
|
+
- Copyable share actions for Slack, PRs, and debugging handoff
|
|
31
|
+
- Deterministic quick diagnosis in the terminal after failed runs
|
|
32
|
+
- Playwright traces, screenshots, videos and logs uploaded automatically
|
|
33
|
+
- 48-hour public share links on the free hosted flow
|
|
25
34
|
- Works with existing Playwright reporter setup
|
|
26
|
-
- Optional Sentinel Cloud
|
|
27
|
-
- CI run history and AI debugging
|
|
35
|
+
- Optional live failure capture for richer Sentinel Cloud analysis
|
|
36
|
+
- CI run history, retention, and deeper AI debugging in Sentinel Cloud
|
|
28
37
|
|
|
29
38
|
## Why this exists
|
|
30
39
|
|
|
31
|
-
Debugging Playwright
|
|
32
|
-
|
|
40
|
+
Debugging Playwright failures usually means downloading traces, screenshots, and logs separately from CI.
|
|
41
|
+
|
|
42
|
+
Reporter uploads those artifacts into a single hosted Sentinel run page so you can open one link, inspect failures fast, and share that link with the rest of the team.
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
## Why teams use the free version
|
|
45
|
+
|
|
46
|
+
- Drop one wrapper into `playwright.config.ts` and keep running `npx playwright test`
|
|
47
|
+
- Get a hosted Sentinel debugging link automatically on failed runs
|
|
48
|
+
- Share one public URL in Slack, PRs, or GitHub issues instead of passing around raw CI artifacts
|
|
49
|
+
- See unified failures, grouped failure patterns, screenshots, and evidence in one place
|
|
50
|
+
- Let teammates inspect the failure without needing your CI system or local machine
|
|
36
51
|
|
|
37
52
|
## Requirements
|
|
38
53
|
|
|
@@ -45,8 +60,8 @@ so you can quickly understand what failed.
|
|
|
45
60
|
|
|
46
61
|
- best for free and local users
|
|
47
62
|
- zero-friction setup
|
|
48
|
-
-
|
|
49
|
-
-
|
|
63
|
+
- hosted Sentinel report link is generated automatically
|
|
64
|
+
- no `SENTINEL_TOKEN` required
|
|
50
65
|
- AI summaries use trace and reporter evidence, but are less precise than live page capture
|
|
51
66
|
|
|
52
67
|
Install:
|
|
@@ -85,58 +100,59 @@ Run your Playwright tests:
|
|
|
85
100
|
npx playwright test
|
|
86
101
|
```
|
|
87
102
|
|
|
88
|
-
If tests fail and
|
|
103
|
+
If tests fail, Sentinel uploads a hosted debugging report and prints the shareable link in the terminal.
|
|
104
|
+
|
|
105
|
+
Open the hosted report to inspect:
|
|
89
106
|
|
|
90
|
-
-
|
|
91
|
-
-
|
|
107
|
+
- failed tests across jobs
|
|
108
|
+
- within-run grouped failures
|
|
109
|
+
- screenshots and videos
|
|
110
|
+
- trace links
|
|
111
|
+
- parsed failure details
|
|
112
|
+
- light summaries
|
|
113
|
+
- shareable public debugging page
|
|
92
114
|
|
|
93
|
-
|
|
115
|
+
The free hosted public flow is designed for distribution:
|
|
94
116
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
- screenshots
|
|
100
|
-
- videos
|
|
101
|
-
- trace files
|
|
102
|
-
- logs
|
|
117
|
+
- one shareable debugging link per run
|
|
118
|
+
- public read-only pages
|
|
119
|
+
- fast enough to use in CI comments and Slack threads
|
|
120
|
+
- clear upgrade path into a full Sentinel workspace when teams want history, retention, and deeper analysis
|
|
103
121
|
|
|
104
122
|
## Modes
|
|
105
123
|
|
|
106
|
-
###
|
|
124
|
+
### Free hosted mode
|
|
107
125
|
|
|
108
|
-
If `SENTINEL_TOKEN` is not set, the reporter
|
|
126
|
+
If `SENTINEL_TOKEN` is not set, the reporter uploads the run to a hosted public Sentinel report and prints the shareable URL.
|
|
109
127
|
|
|
110
|
-
|
|
128
|
+
This free public flow includes:
|
|
111
129
|
|
|
112
|
-
|
|
130
|
+
- hosted run page
|
|
131
|
+
- hosted failure pages
|
|
132
|
+
- grouped failures inside the run
|
|
133
|
+
- light summaries
|
|
134
|
+
- copy/share actions
|
|
135
|
+
- 48-hour share links
|
|
113
136
|
|
|
114
|
-
|
|
115
|
-
SENTINEL_TOKEN=your_project_ingest_token npx playwright test
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
For intentional uploads outside CI, also set `SENTINEL_UPLOAD_LOCAL=1` and provide the usual commit and run metadata expected by the uploader.
|
|
137
|
+
### Workspace mode
|
|
119
138
|
|
|
120
|
-
|
|
139
|
+
If `SENTINEL_TOKEN` is set, the reporter uploads into your Sentinel workspace instead of the free hosted public flow.
|
|
121
140
|
|
|
122
141
|
```bash
|
|
123
|
-
SENTINEL_TOKEN=your_project_ingest_token
|
|
124
|
-
SENTINEL_UPLOAD_LOCAL=1 \
|
|
125
|
-
GITHUB_SHA=abc123 \
|
|
126
|
-
GITHUB_REF_NAME=main \
|
|
127
|
-
GITHUB_RUN_ID=local-dev \
|
|
128
|
-
npx playwright test
|
|
142
|
+
SENTINEL_TOKEN=your_project_ingest_token npx playwright test
|
|
129
143
|
```
|
|
130
144
|
|
|
145
|
+
For local runs outside CI, Sentinel will use your local git metadata automatically when available.
|
|
146
|
+
|
|
131
147
|
## What `withSentinel()` does
|
|
132
148
|
|
|
133
149
|
- Preserves your existing reporter configuration
|
|
134
150
|
- Injects a Playwright JSON reporter if one is missing
|
|
135
|
-
- Reuses your existing Playwright HTML reporter path when configured
|
|
136
151
|
- Sets sensible artifact defaults:
|
|
137
152
|
- trace: `retain-on-failure`
|
|
138
153
|
- screenshot: `only-on-failure`
|
|
139
154
|
- video: `retain-on-failure`
|
|
155
|
+
- Uploads the run to hosted Sentinel at the end of the test run
|
|
140
156
|
|
|
141
157
|
## Recommended Cloud Setup
|
|
142
158
|
|
|
@@ -173,7 +189,7 @@ Use this cloud setup when you want:
|
|
|
173
189
|
- richer DOM-aware diagnosis
|
|
174
190
|
- more reliable code patches grounded in real page state
|
|
175
191
|
|
|
176
|
-
Free and local-only users do not need this. The standard `withSentinel()` setup remains the simplest path and
|
|
192
|
+
Free and local-only users do not need this. The standard `withSentinel()` setup remains the simplest path and will upload a hosted Sentinel report automatically.
|
|
177
193
|
|
|
178
194
|
## Options
|
|
179
195
|
|
|
@@ -185,9 +201,6 @@ withSentinel(config, {
|
|
|
185
201
|
testResultsDir: "test-results",
|
|
186
202
|
artifactDirs: ["tmp/extra-artifacts"],
|
|
187
203
|
verbose: true,
|
|
188
|
-
localReportDir: "sentinel-report",
|
|
189
|
-
localReportFileName: "index.html",
|
|
190
|
-
localRedirectFileName: "sentinel-debug.html",
|
|
191
204
|
});
|
|
192
205
|
```
|
|
193
206
|
|
|
@@ -200,6 +213,10 @@ Sentinel Cloud adds:
|
|
|
200
213
|
- AI-generated failure summaries
|
|
201
214
|
- flaky test detection
|
|
202
215
|
- shareable run links
|
|
216
|
+
- longer retention
|
|
217
|
+
- compare against previous runs
|
|
218
|
+
- recurring failure history
|
|
219
|
+
- richer fix suggestions and team workflows
|
|
203
220
|
|
|
204
221
|
Free for up to 100 CI runs per month.
|
|
205
222
|
Create an account at [sentinelqa.com](https://sentinelqa.com).
|
package/dist/index.d.ts
CHANGED
|
@@ -23,9 +23,6 @@ export type SentinelPlaywrightOptions = {
|
|
|
23
23
|
testResultsDir?: string;
|
|
24
24
|
artifactDirs?: string[];
|
|
25
25
|
verbose?: boolean;
|
|
26
|
-
localReportDir?: string;
|
|
27
|
-
localReportFileName?: string;
|
|
28
|
-
localRedirectFileName?: string;
|
|
29
26
|
};
|
|
30
27
|
export declare function withSentinel(config: PlaywrightConfig, options?: SentinelPlaywrightOptions): PlaywrightConfig;
|
|
31
28
|
export declare function resolveSentinelPaths(config: PlaywrightConfig, options?: SentinelPlaywrightOptions): SentinelResolvedPaths;
|
package/dist/index.js
CHANGED
|
@@ -105,10 +105,7 @@ function withSentinel(config, options = {}) {
|
|
|
105
105
|
playwrightReportDir,
|
|
106
106
|
testResultsDir,
|
|
107
107
|
artifactDirs,
|
|
108
|
-
verbose: options.verbose ?? false
|
|
109
|
-
localReportDir: options.localReportDir,
|
|
110
|
-
localReportFileName: options.localReportFileName,
|
|
111
|
-
localRedirectFileName: options.localRedirectFileName
|
|
108
|
+
verbose: options.verbose ?? false
|
|
112
109
|
};
|
|
113
110
|
const sentinelIndex = reporters.findIndex((entry) => getReporterName(entry) === sentinelReporterPath);
|
|
114
111
|
if (sentinelIndex !== -1) {
|
package/dist/localReport.js
CHANGED
|
@@ -362,6 +362,20 @@ const groupSimilarFailures = (tests) => {
|
|
|
362
362
|
.filter((group) => group.tests.length > 1)
|
|
363
363
|
.sort((a, b) => b.tests.length - a.tests.length);
|
|
364
364
|
};
|
|
365
|
+
const groupFailureDigest = (tests) => {
|
|
366
|
+
const groups = new Map();
|
|
367
|
+
for (const test of getFailureTests(tests)) {
|
|
368
|
+
const summary = test.diagnosis
|
|
369
|
+
? (0, quickDiagnosis_1.describeFailure)(test.diagnosis)
|
|
370
|
+
: (test.errors[0]?.split(/\r?\n/)[0]?.trim() || "Open the failure details to inspect the exact Playwright error.");
|
|
371
|
+
const key = summary;
|
|
372
|
+
if (!groups.has(key)) {
|
|
373
|
+
groups.set(key, { key, summary, tests: [] });
|
|
374
|
+
}
|
|
375
|
+
groups.get(key).tests.push(test);
|
|
376
|
+
}
|
|
377
|
+
return Array.from(groups.values()).sort((a, b) => b.tests.length - a.tests.length);
|
|
378
|
+
};
|
|
365
379
|
const buildRunSnapshot = (tests, summary) => ({
|
|
366
380
|
generatedAt: new Date().toISOString(),
|
|
367
381
|
branch: getCurrentBranch(),
|
|
@@ -601,24 +615,30 @@ const renderFailureDigest = (tests) => {
|
|
|
601
615
|
if (!failedTests.length) {
|
|
602
616
|
return `<div class="empty-state">No failed tests were detected in this run.</div>`;
|
|
603
617
|
}
|
|
618
|
+
const digestGroups = groupFailureDigest(tests);
|
|
604
619
|
return `
|
|
605
620
|
<div class="digest-grid">
|
|
606
|
-
${
|
|
607
|
-
.map((
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
const
|
|
611
|
-
?
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
:
|
|
621
|
+
${digestGroups
|
|
622
|
+
.map((group) => {
|
|
623
|
+
const primary = group.tests[0];
|
|
624
|
+
const diagnosis = primary.diagnosis;
|
|
625
|
+
const debugSummary = escapeHtml(group.tests.length > 1
|
|
626
|
+
? [
|
|
627
|
+
`Grouped failure summary: ${group.summary}`,
|
|
628
|
+
...group.tests.map((test) => `- ${test.title}`)
|
|
629
|
+
].join("\n")
|
|
630
|
+
: diagnosis
|
|
631
|
+
? (0, quickDiagnosis_1.buildDebugSummary)(diagnosis)
|
|
632
|
+
: `Test: ${primary.title}\nDiagnosis: Review trace and error details in the expanded card.`);
|
|
633
|
+
const uniqueLocators = Array.from(new Set(group.tests.map((test) => test.diagnosis?.locator).filter(Boolean)));
|
|
616
634
|
return `
|
|
617
635
|
<article class="digest-card">
|
|
618
636
|
<div class="digest-head">
|
|
619
637
|
<div>
|
|
620
|
-
<span class="artifact-kind">${escapeHtml(
|
|
621
|
-
<h3>${
|
|
638
|
+
<span class="artifact-kind">${escapeHtml(primary.status)}</span>
|
|
639
|
+
<h3>${group.tests.length > 1
|
|
640
|
+
? `${group.tests.length} tests share this failure`
|
|
641
|
+
: escapeHtml(primary.title)}</h3>
|
|
622
642
|
</div>
|
|
623
643
|
<button
|
|
624
644
|
type="button"
|
|
@@ -629,12 +649,20 @@ const renderFailureDigest = (tests) => {
|
|
|
629
649
|
Copy summary
|
|
630
650
|
</button>
|
|
631
651
|
</div>
|
|
632
|
-
<p>${summary}</p>
|
|
652
|
+
<p>${escapeHtml(group.summary)}</p>
|
|
633
653
|
<div class="fact-row">
|
|
634
|
-
${diagnosis?.
|
|
635
|
-
|
|
636
|
-
|
|
654
|
+
${diagnosis?.expected && diagnosis?.received
|
|
655
|
+
? `<span class="fact-chip">Expected: ${escapeHtml(diagnosis.expected)}</span><span class="fact-chip">Observed: ${escapeHtml(diagnosis.received)}</span>`
|
|
656
|
+
: ""}
|
|
657
|
+
${uniqueLocators.length === 1
|
|
658
|
+
? `<span class="fact-chip">Locator: ${escapeHtml(uniqueLocators[0])}</span>`
|
|
659
|
+
: uniqueLocators.length > 1
|
|
660
|
+
? `<span class="fact-chip">${uniqueLocators.length} locators involved</span>`
|
|
661
|
+
: ""}
|
|
637
662
|
</div>
|
|
663
|
+
${group.tests.length > 1
|
|
664
|
+
? `<ul class="group-list">${group.tests.map((test) => `<li>${escapeHtml(test.title)}</li>`).join("\n")}</ul>`
|
|
665
|
+
: ""}
|
|
638
666
|
</article>
|
|
639
667
|
`;
|
|
640
668
|
})
|
package/dist/quickDiagnosis.js
CHANGED
|
@@ -28,11 +28,11 @@ const toMessage = (result) => {
|
|
|
28
28
|
};
|
|
29
29
|
const classifySignal = (message) => {
|
|
30
30
|
const lower = message.toLowerCase();
|
|
31
|
-
if (/timeout|timed out|waiting for/.test(lower))
|
|
32
|
-
return "timeout";
|
|
33
31
|
if (/expected substring|expected string|received string|tohavetext|tocontaintext/.test(lower)) {
|
|
34
32
|
return "assertion_mismatch";
|
|
35
33
|
}
|
|
34
|
+
if (/timeout|timed out|waiting for/.test(lower))
|
|
35
|
+
return "timeout";
|
|
36
36
|
if (/resolved to 0 elements|locator.*not found|never appeared|strict mode violation/.test(lower)) {
|
|
37
37
|
return "locator_not_found";
|
|
38
38
|
}
|
package/dist/reporter.d.ts
CHANGED
|
@@ -5,9 +5,6 @@ type ReporterOptions = {
|
|
|
5
5
|
testResultsDir: string;
|
|
6
6
|
artifactDirs?: string[];
|
|
7
7
|
verbose?: boolean;
|
|
8
|
-
localReportDir?: string;
|
|
9
|
-
localReportFileName?: string;
|
|
10
|
-
localRedirectFileName?: string;
|
|
11
8
|
};
|
|
12
9
|
declare class SentinelReporter {
|
|
13
10
|
private failedCount;
|
|
@@ -16,7 +13,6 @@ declare class SentinelReporter {
|
|
|
16
13
|
constructor(options: ReporterOptions);
|
|
17
14
|
onBegin(config: any, suite: any): void;
|
|
18
15
|
onTestEnd(test: any, result: any): Promise<void>;
|
|
19
|
-
private printLocalReport;
|
|
20
16
|
onEnd(): Promise<void>;
|
|
21
17
|
}
|
|
22
18
|
export = SentinelReporter;
|
package/dist/reporter.js
CHANGED
|
@@ -1,33 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
const path_1 = __importDefault(require("path"));
|
|
6
|
-
const url_1 = require("url");
|
|
7
2
|
const node_1 = require("@sentinelqa/uploader/node");
|
|
8
3
|
const env_1 = require("./env");
|
|
9
|
-
const localReport_1 = require("./localReport");
|
|
10
4
|
const quickDiagnosis_1 = require("./quickDiagnosis");
|
|
11
5
|
const { sentinelCaptureFailureContextFromReporter } = require("@sentinelqa/uploader/playwright");
|
|
12
|
-
const pluralize = (count, singular, plural) => {
|
|
13
|
-
return count === 1 ? singular : plural;
|
|
14
|
-
};
|
|
15
|
-
const formatTerminalLink = (label, target) => {
|
|
16
|
-
if (!process.stdout.isTTY)
|
|
17
|
-
return label;
|
|
18
|
-
return `\u001B]8;;${target}\u0007${label}\u001B]8;;\u0007`;
|
|
19
|
-
};
|
|
20
6
|
const colorize = (value, code) => {
|
|
21
7
|
if (!process.stdout.isTTY)
|
|
22
8
|
return value;
|
|
23
9
|
return `\u001b[${code}m${value}\u001b[0m`;
|
|
24
10
|
};
|
|
25
|
-
const bold = (value) => colorize(value, "1");
|
|
26
11
|
const green = (value) => colorize(value, "32");
|
|
27
|
-
const cyan = (value) => colorize(value, "36");
|
|
28
12
|
const yellow = (value) => colorize(value, "33");
|
|
29
13
|
const dim = (value) => colorize(value, "2");
|
|
30
|
-
const magenta = (value) => colorize(value, "35");
|
|
31
14
|
class SentinelReporter {
|
|
32
15
|
constructor(options) {
|
|
33
16
|
this.failedCount = 0;
|
|
@@ -59,75 +42,21 @@ class SentinelReporter {
|
|
|
59
42
|
this.failedCount += 1;
|
|
60
43
|
}
|
|
61
44
|
}
|
|
62
|
-
|
|
63
|
-
const localReportPath = localReport.htmlPath;
|
|
64
|
-
const relativeReportPath = path_1.default
|
|
65
|
-
.relative(process.cwd(), localReportPath)
|
|
66
|
-
.replace(/\\/g, "/");
|
|
67
|
-
const displayPath = relativeReportPath.startsWith(".")
|
|
68
|
-
? relativeReportPath
|
|
69
|
-
: `./${relativeReportPath}`;
|
|
70
|
-
const openCommand = `open ${displayPath}`;
|
|
45
|
+
async onEnd() {
|
|
71
46
|
console.log("");
|
|
72
47
|
console.log(green("✔ Artifacts collected"));
|
|
73
|
-
console.log(green("✔ Sentinel HTML debugging report created"));
|
|
74
|
-
console.log("");
|
|
75
|
-
console.log(bold("Report"));
|
|
76
|
-
console.log(` ${cyan(formatTerminalLink(displayPath, (0, url_1.pathToFileURL)(localReportPath).href))}`);
|
|
77
|
-
console.log("");
|
|
78
|
-
console.log(bold("Open"));
|
|
79
|
-
console.log(` ${cyan(openCommand)}`);
|
|
80
|
-
console.log("");
|
|
81
48
|
const quickDiagnosis = (0, quickDiagnosis_1.buildQuickDiagnosis)(this.options.playwrightJsonPath);
|
|
82
49
|
if (quickDiagnosis?.lines.length) {
|
|
50
|
+
console.log("");
|
|
83
51
|
console.log(yellow("Quick diagnosis"));
|
|
84
52
|
for (const line of quickDiagnosis.lines) {
|
|
85
53
|
console.log(` ${dim(line)}`);
|
|
86
54
|
}
|
|
87
|
-
console.log("");
|
|
88
|
-
}
|
|
89
|
-
if (localReport.runDiff) {
|
|
90
|
-
console.log(yellow("Run-to-run diff"));
|
|
91
|
-
console.log(` ${dim(`New failures: ${localReport.runDiff.newFailures.length}`)}`);
|
|
92
|
-
console.log(` ${dim(`Fixed since last run: ${localReport.runDiff.fixedTests.length}`)}`);
|
|
93
|
-
console.log(` ${dim(`Still failing: ${localReport.runDiff.stillFailing.length}`)}`);
|
|
94
|
-
console.log("");
|
|
95
|
-
}
|
|
96
|
-
console.log(yellow("Tip"));
|
|
97
|
-
console.log(` ${dim("Want full AI analysis, shareable run links, and CI history?")}`);
|
|
98
|
-
console.log(` ${dim("Try Sentinel Cloud Beta free:")} ${cyan(formatTerminalLink("https://sentinelqa.com", "https://sentinelqa.com"))}`);
|
|
99
|
-
console.log("");
|
|
100
|
-
console.log(` ${magenta("★ If this reporter helped you debug faster,")}`);
|
|
101
|
-
console.log(` ${dim("consider starring the project:")}`);
|
|
102
|
-
console.log(` ${cyan(formatTerminalLink("https://github.com/adnangradascevic/playwright-reporter", "https://github.com/adnangradascevic/playwright-reporter"))}`);
|
|
103
|
-
}
|
|
104
|
-
async onEnd() {
|
|
105
|
-
const hasSentinelToken = Boolean(process.env.SENTINEL_TOKEN);
|
|
106
|
-
const hasCiEnv = (0, node_1.hasSupportedCiEnv)(process.env);
|
|
107
|
-
const localUploadEnabled = (0, node_1.isLocalUploadEnabled)(process.env);
|
|
108
|
-
if (!hasSentinelToken || (!hasCiEnv && !localUploadEnabled)) {
|
|
109
|
-
const localReport = (0, localReport_1.generateLocalDebugReport)({
|
|
110
|
-
playwrightJsonPath: this.options.playwrightJsonPath,
|
|
111
|
-
playwrightReportDir: this.options.playwrightReportDir,
|
|
112
|
-
testResultsDir: this.options.testResultsDir,
|
|
113
|
-
artifactDirs: this.options.artifactDirs || [],
|
|
114
|
-
reportDir: this.options.localReportDir,
|
|
115
|
-
reportFileName: this.options.localReportFileName,
|
|
116
|
-
redirectFileName: this.options.localRedirectFileName
|
|
117
|
-
});
|
|
118
|
-
this.printLocalReport(localReport);
|
|
119
|
-
console.log("");
|
|
120
|
-
if (hasSentinelToken && !hasCiEnv && !localUploadEnabled) {
|
|
121
|
-
console.log("Sentinel upload skipped for this local run.");
|
|
122
|
-
console.log("To upload local runs, set SENTINEL_UPLOAD_LOCAL=1 and provide the required CI metadata.");
|
|
123
|
-
console.log("");
|
|
124
|
-
}
|
|
125
|
-
return;
|
|
126
55
|
}
|
|
127
56
|
console.log("");
|
|
128
|
-
console.log("Uploading
|
|
57
|
+
console.log("Uploading hosted debugging report to Sentinel...");
|
|
129
58
|
console.log("");
|
|
130
|
-
const
|
|
59
|
+
const upload = await (0, node_1.runSentinelUpload)({
|
|
131
60
|
playwrightJsonPath: this.options.playwrightJsonPath,
|
|
132
61
|
playwrightReportDir: this.options.playwrightReportDir,
|
|
133
62
|
testResultsDir: this.options.testResultsDir,
|
|
@@ -137,11 +66,19 @@ class SentinelReporter {
|
|
|
137
66
|
SENTINEL_REPORTER_PROJECT: this.options.project || undefined
|
|
138
67
|
}
|
|
139
68
|
});
|
|
140
|
-
if (exitCode !== 0) {
|
|
141
|
-
throw new Error(`Sentinel upload failed with exit code ${exitCode}`);
|
|
69
|
+
if (upload.exitCode !== 0) {
|
|
70
|
+
throw new Error(`Sentinel upload failed with exit code ${upload.exitCode}`);
|
|
142
71
|
}
|
|
143
72
|
console.log("");
|
|
144
|
-
console.log("✔
|
|
73
|
+
console.log("✔ Hosted report uploaded to Sentinel");
|
|
74
|
+
if (upload.shareRunUrl || upload.internalRunUrl) {
|
|
75
|
+
console.log("");
|
|
76
|
+
console.log("Sentinel report");
|
|
77
|
+
console.log(` ${upload.shareRunUrl || upload.internalRunUrl}`);
|
|
78
|
+
if (upload.shareLabel) {
|
|
79
|
+
console.log(` ${dim(upload.shareLabel)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
145
82
|
}
|
|
146
83
|
}
|
|
147
84
|
module.exports = SentinelReporter;
|