playwright-reborn 0.2.2 → 0.2.4
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/CHANGELOG.md +18 -0
- package/README.md +26 -9
- package/dist/fixture.js +4 -3
- package/dist/fixture.js.map +1 -1
- package/dist/format.js +15 -2
- package/dist/format.js.map +1 -1
- package/dist/options.js +4 -3
- package/dist/options.js.map +1 -1
- package/dist/paths.js +19 -11
- package/dist/paths.js.map +1 -1
- package/dist/render.js +43 -25
- package/dist/render.js.map +1 -1
- package/dist/serialize.d.ts +1 -0
- package/dist/serialize.js +20 -8
- package/dist/serialize.js.map +1 -1
- package/dist/status.js +4 -4
- package/dist/status.js.map +1 -1
- package/dist/ui/report.css +432 -288
- package/dist/ui/report.js +301 -335
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.4
|
|
4
|
+
|
|
5
|
+
- Refresh the report UI with a persistent sidebar, dashboard overview, and clearer test exploration.
|
|
6
|
+
- Add Tags and Sort dropdowns, remembered light and dark themes, improved filter controls, and responsive long-name handling.
|
|
7
|
+
|
|
8
|
+
## 0.2.3
|
|
9
|
+
|
|
10
|
+
- `steps: 'user'` leaves expectations and `test.attach` calls out of the step tree.
|
|
11
|
+
- Protect unrelated output folders, symbolic links, and project ancestors; custom report filenames can be replaced on later runs.
|
|
12
|
+
- Build reports in a temporary folder before replacing the previous report, preserving attachment sources and the last report when assets are missing.
|
|
13
|
+
- Keep interrupted outcomes separate from skipped tests and explain unexpected passes on tests marked to fail.
|
|
14
|
+
- Remove duplicate step attachments, normalize embedded image MIME types, and preserve UTF-8 across log chunks.
|
|
15
|
+
- Capture failing final attempts consistently in `last` mode; automatic opening defaults to `never` in CI.
|
|
16
|
+
- Fix hash navigation, browser back, malformed IDs, pending searches, log links, and keyboard focus; add filter reset and useful empty states.
|
|
17
|
+
- Improve status colors, run status, contrast, spacing, mobile navigation, long content, chart sizing, and screenshot presentation.
|
|
18
|
+
- Bound test and timeline rendering to batches of 200 and support prototype-like filenames and a project named `all`.
|
|
19
|
+
- Add reporter/fixture integration coverage and reusable browser regression checks; stop verification when the build or demo outcomes are wrong.
|
|
20
|
+
|
|
3
21
|
## 0.2.2
|
|
4
22
|
|
|
5
23
|
- The company name has a subtitle. The default is Test Automation.
|
package/README.md
CHANGED
|
@@ -61,9 +61,9 @@ Pass any of these next to the reporter. Omitted settings use the default.
|
|
|
61
61
|
| Setting | Default | Allowed |
|
|
62
62
|
|---|---|---|
|
|
63
63
|
| `company` | Project folder name | Any name. This is the large title. It does not change when a run fails. |
|
|
64
|
-
| `accent` | `green` | `green`, `red`, `blue`, `amber`, `violet`. Colors the light field,
|
|
65
|
-
| `open` | `on-failure` | `always`, `never`, `on-failure`. When to open the HTML file after the run. Use `never` when `CI` is set. |
|
|
66
|
-
| `outputFolder` | `reborn-report` | A folder path. Each run replaces
|
|
64
|
+
| `accent` | `green` | `green`, `red`, `blue`, `amber`, `violet`. Colors the light field, selection, charts, and links. Failures stay coral and flaky results stay amber for consistent recognition. |
|
|
65
|
+
| `open` | `on-failure`, or `never` in CI | `always`, `never`, `on-failure`. When to open the HTML file after the run. Use `never` when `CI` is set. |
|
|
66
|
+
| `outputFolder` | `reborn-report` | A dedicated folder path. Each run replaces its previous report. Existing empty folders are accepted; unrelated non-empty folders and symbolic links are refused. If you leave this unset, a sharded run writes `reborn-report-shard-1` and so on. |
|
|
67
67
|
| `reportFileName` | `index.html` | One `.html` file name, with no folders. Jenkins HTML Publisher can point at this file. |
|
|
68
68
|
| `inline` | `true` | `true` or `false`. `true` writes one HTML file with the CSS, script, and fonts inside it, and embeds screenshots. Traces and other files stay as relative links in the report folder. |
|
|
69
69
|
| `overview` | `chart` | `chart` or `timeline`. Which view the overview opens on. The buttons on the page can still switch it. |
|
|
@@ -76,7 +76,7 @@ Pass any of these next to the reporter. Omitted settings use the default.
|
|
|
76
76
|
| `commitShort` | `true` | `true` shows the first 7 characters of the commit. `false` shows the full value. |
|
|
77
77
|
| `ignoreTags` | `^@?HC2T-` | A pattern for tags to hide, or `false` to keep every tag. |
|
|
78
78
|
| `screenshots` | `failure` | `off`, `failure`, `last`, or `steps`. When the fixture takes a picture. The reporter sets `REBORN_SCREENSHOTS` in `onBegin`. Do not set that variable yourself. The Screenshots page stays in the report either way. |
|
|
79
|
-
| `steps` | `user` | `user` or `all`. `user`
|
|
79
|
+
| `steps` | `user` | `user` or `all`. `user` shows the steps a person would follow. It leaves expectations, `test.attach` calls, hooks, and Playwright's own API calls out. `all` keeps those calls too. |
|
|
80
80
|
| `showLogs` | `true` | `true` or `false`. `false` leaves the Logs page out. |
|
|
81
81
|
| `showFiles` | `true` | `true` or `false`. `false` leaves the Files page out. |
|
|
82
82
|
| `suite` | empty | Suite name shown on the overview. |
|
|
@@ -94,17 +94,20 @@ The reporter cannot photograph the page. Import the fixture in the tests that sh
|
|
|
94
94
|
```ts
|
|
95
95
|
import { test, expect, step } from 'playwright-reborn/fixture';
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
test('receipt shows the order id', async ({ page }) => {
|
|
98
|
+
await page.setContent('<p id="order">1842</p>');
|
|
99
|
+
const order = await step('read the receipt', async () => {
|
|
100
|
+
return page.locator('#order').textContent();
|
|
101
|
+
});
|
|
102
|
+
expect(order).toBe('1842');
|
|
99
103
|
});
|
|
100
|
-
await expect(order).toBe('1842');
|
|
101
104
|
```
|
|
102
105
|
|
|
103
106
|
`step` returns the value of its body, the same way `test.step` does. `test` is a normal Playwright test object, so a suite can `test.extend(...)` with its own fixtures.
|
|
104
107
|
|
|
105
108
|
The page fixture keeps each screenshot on the test that is running, including when tests run in parallel.
|
|
106
109
|
|
|
107
|
-
`failure`
|
|
110
|
+
`failure` takes a picture of failing attempts. `last` takes one picture at the end of every attempt, for tests that use `page`; failing attempts appear under Last failing step. `steps` also takes a picture after each `step()`. A plain `test.step` is not photographed. Turn Playwright's own `screenshot` option off if you do not want a second copy of the same picture.
|
|
108
111
|
|
|
109
112
|
Files larger than 25MB are listed and not copied.
|
|
110
113
|
|
|
@@ -122,7 +125,21 @@ publishHTML([
|
|
|
122
125
|
|
|
123
126
|
`inline: true` is the default, so that HTML file opens from a Jenkins artifact or from disk without a CDN and without `assets/report.css`. Keep the report folder together if the Files page should still download traces and video. Those links are relative paths inside the folder.
|
|
124
127
|
|
|
125
|
-
|
|
128
|
+
When `CI` is set, automatic opening defaults to `never`. An explicit `open` option still wins.
|
|
129
|
+
|
|
130
|
+
## Development and verification
|
|
131
|
+
|
|
132
|
+
Run `npm test` to build and run the unit tests plus real Playwright reporter/fixture integration tests. The integration tests stub browser I/O and check parallel screenshot isolation, return values, failures, and every screenshot mode.
|
|
133
|
+
|
|
134
|
+
Run `npm run verify` for the full demo and browser checks. The demo deliberately includes one failed test and one flaky test. The verifier requires those exact outcomes and fails on additional failures. `REBORN_BROWSER_EXECUTABLE` selects a browser executable for both the demo and verifier; `REBORN_BROWSER_CDP_URL` can connect the verifier to an existing compatible browser.
|
|
135
|
+
|
|
136
|
+
To check an existing exported report in your normal browser without launching Playwright, run:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm run verify:browser -- reborn-report/index.html /tmp/reborn-browser-checks.html
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Open the generated HTML file. It runs the same interaction regressions in browser frames, including navigation, keyboard focus, filters, empty reports, pagination, and long content at 320px, 390px, and 768px widths. This verifier expects the demo report's logs and screenshots. Both reports and this verification page work from disk.
|
|
126
143
|
|
|
127
144
|
## License
|
|
128
145
|
|
package/dist/fixture.js
CHANGED
|
@@ -35,10 +35,11 @@ exports.test = test_1.test.extend({
|
|
|
35
35
|
try {
|
|
36
36
|
await use(page);
|
|
37
37
|
const mode = screenshotMode();
|
|
38
|
-
const failed = testInfo.status === 'failed' || testInfo.status === 'timedOut'
|
|
39
|
-
|
|
38
|
+
const failed = testInfo.status === 'failed' || testInfo.status === 'timedOut' || testInfo.status === 'interrupted'
|
|
39
|
+
|| (testInfo.status === 'passed' && testInfo.expectedStatus === 'failed');
|
|
40
|
+
if ((mode === 'last' || mode === 'steps') && !failed)
|
|
40
41
|
await shot('reborn:last', testInfo);
|
|
41
|
-
if ((mode === 'failure' || mode === 'steps') && failed)
|
|
42
|
+
if ((mode === 'failure' || mode === 'last' || mode === 'steps') && failed)
|
|
42
43
|
await shot('reborn:failure', testInfo);
|
|
43
44
|
}
|
|
44
45
|
finally {
|
package/dist/fixture.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture.js","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"fixture.js","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":";;;AA4CA,oBAYC;AAxDD,2CAAuE;AA0C9D,uFA1Cc,aAAM,OA0Cd;AAzCf,uCAA8D;AAE9D,KAAK,UAAU,IAAI,CAAC,IAAY,EAAE,QAAmB;IACnD,IAAI,IAAI,GAAG,QAAQ,CAAC;IACpB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,CAAC;YACH,IAAI,GAAG,YAAI,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,IAAA,iBAAO,EAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACvD,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;AACH,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS,CAAC;AACrD,CAAC;AAEY,QAAA,IAAI,GAAG,WAAI,CAAC,MAAM,CAAC;IAC9B,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE;QACtC,IAAA,sBAAY,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;YAChB,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,IAAI,QAAQ,CAAC,MAAM,KAAK,aAAa;mBAC7G,CAAC,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC;YAC5E,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;YAC1F,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO,CAAC,IAAI,MAAM;gBAAE,MAAM,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QACpH,CAAC;gBAAS,CAAC;YACT,IAAA,oBAAU,EAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAII,KAAK,UAAU,IAAI,CAAI,KAAa,EAAE,IAA0B;IACrE,MAAM,QAAQ,GAAG,YAAI,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,YAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QACjC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,IAAI,EAAE,CAAC;YAC3B,IAAI,cAAc,EAAE,KAAK,OAAO;gBAAE,MAAM,IAAI,CAAC,eAAe,KAAK,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC/E,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,cAAc,EAAE,KAAK,OAAO;gBAAE,MAAM,IAAI,CAAC,eAAe,KAAK,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC/E,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/format.js
CHANGED
|
@@ -11,9 +11,11 @@ exports.attachmentKind = attachmentKind;
|
|
|
11
11
|
exports.parseShotName = parseShotName;
|
|
12
12
|
exports.durationBand = durationBand;
|
|
13
13
|
exports.safeFileName = safeFileName;
|
|
14
|
+
const node_string_decoder_1 = require("node:string_decoder");
|
|
15
|
+
const node_util_1 = require("node:util");
|
|
14
16
|
const LOG_LIMIT = 100_000;
|
|
15
17
|
function stripAnsi(value) {
|
|
16
|
-
return
|
|
18
|
+
return (0, node_util_1.stripVTControlCharacters)(value);
|
|
17
19
|
}
|
|
18
20
|
function capText(value, limit = LOG_LIMIT) {
|
|
19
21
|
if (value.length <= limit)
|
|
@@ -23,7 +25,18 @@ function capText(value, limit = LOG_LIMIT) {
|
|
|
23
25
|
function joinChunks(chunks) {
|
|
24
26
|
if (!chunks || chunks.length === 0)
|
|
25
27
|
return '';
|
|
26
|
-
|
|
28
|
+
let decoder = new node_string_decoder_1.StringDecoder('utf8');
|
|
29
|
+
let text = '';
|
|
30
|
+
for (const chunk of chunks) {
|
|
31
|
+
if (typeof chunk === 'string') {
|
|
32
|
+
text += decoder.end() + chunk;
|
|
33
|
+
decoder = new node_string_decoder_1.StringDecoder('utf8');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
text += decoder.write(chunk);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
text += decoder.end();
|
|
27
40
|
return capText(stripAnsi(text));
|
|
28
41
|
}
|
|
29
42
|
function embedJson(value) {
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/options.js
CHANGED
|
@@ -11,7 +11,7 @@ function parseOptions(options = {}) {
|
|
|
11
11
|
if (options === null || typeof options !== 'object' || Array.isArray(options)) {
|
|
12
12
|
throw new Error('Reborn options must be an object.');
|
|
13
13
|
}
|
|
14
|
-
const open = options.open ?? 'on-failure';
|
|
14
|
+
const open = options.open ?? (process.env.CI ? 'never' : 'on-failure');
|
|
15
15
|
if (!OPENS.has(open)) {
|
|
16
16
|
throw new Error('Reborn open must be "always", "never", or "on-failure".');
|
|
17
17
|
}
|
|
@@ -34,7 +34,8 @@ function parseOptions(options = {}) {
|
|
|
34
34
|
const showCredit = flag(options.showCredit, 'showCredit', false);
|
|
35
35
|
const showPlaywrightVersion = flag(options.showPlaywrightVersion, 'showPlaywrightVersion', false);
|
|
36
36
|
const showProjects = flag(options.showProjects, 'showProjects', false);
|
|
37
|
-
const
|
|
37
|
+
const projectFilter = flag(options.showProjectFilter, 'showProjectFilter', false);
|
|
38
|
+
const showProjectFilter = showProjects && projectFilter;
|
|
38
39
|
const commitShort = flag(options.commitShort, 'commitShort', true);
|
|
39
40
|
const overview = options.overview ?? 'chart';
|
|
40
41
|
if (!OVERVIEWS.has(overview)) {
|
|
@@ -50,7 +51,7 @@ function parseOptions(options = {}) {
|
|
|
50
51
|
throw new Error('Reborn outputFolder must be a non-empty path.');
|
|
51
52
|
}
|
|
52
53
|
const reportFileName = options.reportFileName ?? 'index.html';
|
|
53
|
-
if (!/^[A-Za-z0-9._-]+\.html$/.test(reportFileName)) {
|
|
54
|
+
if (typeof reportFileName !== 'string' || !/^[A-Za-z0-9._-]+\.html$/.test(reportFileName)) {
|
|
54
55
|
throw new Error('Reborn reportFileName must be a single .html file name.');
|
|
55
56
|
}
|
|
56
57
|
return {
|
package/dist/options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":";;AAkEA,
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.js
CHANGED
|
@@ -10,7 +10,7 @@ const promises_1 = __importDefault(require("node:fs/promises"));
|
|
|
10
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
11
11
|
function relativePosix(rootDir, file) {
|
|
12
12
|
const relative = node_path_1.default.relative(rootDir, file);
|
|
13
|
-
if (!relative || relative.startsWith(
|
|
13
|
+
if (!relative || relative === '..' || relative.startsWith(`..${node_path_1.default.sep}`) || node_path_1.default.isAbsolute(relative)) {
|
|
14
14
|
return file.split(node_path_1.default.sep).join('/');
|
|
15
15
|
}
|
|
16
16
|
return relative.split(node_path_1.default.sep).join('/');
|
|
@@ -27,6 +27,10 @@ function resolveOutputFolder(rootDir, outputFolder) {
|
|
|
27
27
|
if (resolved === root) {
|
|
28
28
|
throw new Error('Reborn output folder must not be the project root.');
|
|
29
29
|
}
|
|
30
|
+
const fromOutput = node_path_1.default.relative(resolved, root);
|
|
31
|
+
if (fromOutput && fromOutput !== '..' && !fromOutput.startsWith(`..${node_path_1.default.sep}`) && !node_path_1.default.isAbsolute(fromOutput)) {
|
|
32
|
+
throw new Error('Reborn output folder must not be an ancestor of the project root.');
|
|
33
|
+
}
|
|
30
34
|
return resolved;
|
|
31
35
|
}
|
|
32
36
|
const relative = node_path_1.default.relative(root, resolved);
|
|
@@ -36,32 +40,36 @@ function resolveOutputFolder(rootDir, outputFolder) {
|
|
|
36
40
|
return resolved;
|
|
37
41
|
}
|
|
38
42
|
async function isRebornReport(dir) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const entries = await promises_1.default.readdir(dir, { withFileTypes: true });
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
if (!entry.isFile() || !entry.name.endsWith('.html'))
|
|
46
|
+
continue;
|
|
47
|
+
const html = await promises_1.default.readFile(node_path_1.default.join(dir, entry.name), 'utf8');
|
|
48
|
+
if (html.includes('<script id="reborn-data" type="application/json">'))
|
|
49
|
+
return true;
|
|
45
50
|
}
|
|
51
|
+
return false;
|
|
46
52
|
}
|
|
47
53
|
async function prepareOutput(dir) {
|
|
48
54
|
let stat;
|
|
49
55
|
try {
|
|
50
|
-
stat = await promises_1.default.
|
|
56
|
+
stat = await promises_1.default.lstat(dir);
|
|
51
57
|
}
|
|
52
58
|
catch (error) {
|
|
53
59
|
const code = typeof error === 'object' && error && 'code' in error ? error.code : undefined;
|
|
54
60
|
if (code !== 'ENOENT')
|
|
55
61
|
throw error;
|
|
56
62
|
}
|
|
63
|
+
if (stat?.isSymbolicLink()) {
|
|
64
|
+
throw new Error(`Refusing to replace symbolic link ${dir}.`);
|
|
65
|
+
}
|
|
57
66
|
if (stat && !stat.isDirectory()) {
|
|
58
67
|
throw new Error(`Reborn output folder ${dir} exists and is not a directory.`);
|
|
59
68
|
}
|
|
60
69
|
if (stat) {
|
|
61
|
-
const
|
|
62
|
-
const named = base === 'reborn-report' || base.startsWith('reborn-report-');
|
|
70
|
+
const empty = (await promises_1.default.readdir(dir)).length === 0;
|
|
63
71
|
const ours = await isRebornReport(dir);
|
|
64
|
-
if (!
|
|
72
|
+
if (!empty && !ours) {
|
|
65
73
|
throw new Error(`Refusing to replace ${dir} because it is not a Reborn report folder.`);
|
|
66
74
|
}
|
|
67
75
|
await promises_1.default.rm(dir, { recursive: true, force: true });
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":";;;;;AAGA,sCAMC;AAED,
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|
package/dist/render.js
CHANGED
|
@@ -35,10 +35,11 @@ function renderHtml(json, assets) {
|
|
|
35
35
|
<title>Report</title>
|
|
36
36
|
${style}
|
|
37
37
|
</head>
|
|
38
|
-
<body style="margin:0;background:#
|
|
38
|
+
<body style="margin:0;background:#f3f7fd;color:#17324f">
|
|
39
39
|
<div class="light"></div>
|
|
40
40
|
<a class="skip" href="#tests">Skip to tests</a>
|
|
41
41
|
<main class="page" id="app"></main>
|
|
42
|
+
<noscript><p style="padding:24px">Enable JavaScript to view this report.</p></noscript>
|
|
42
43
|
<script id="reborn-data" type="application/json">${json}</script>
|
|
43
44
|
${script}
|
|
44
45
|
</body>
|
|
@@ -66,16 +67,10 @@ async function tooLarge(file) {
|
|
|
66
67
|
return false;
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
|
-
function markOmitted(
|
|
70
|
-
for (const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (attachment.path !== target)
|
|
74
|
-
continue;
|
|
75
|
-
attachment.omitted = true;
|
|
76
|
-
attachment.path = '';
|
|
77
|
-
}
|
|
78
|
-
}
|
|
70
|
+
function markOmitted(attachments) {
|
|
71
|
+
for (const attachment of attachments) {
|
|
72
|
+
attachment.omitted = true;
|
|
73
|
+
attachment.path = '';
|
|
79
74
|
}
|
|
80
75
|
}
|
|
81
76
|
async function writePending(outputDir, file) {
|
|
@@ -98,15 +93,9 @@ async function fileBody(file) {
|
|
|
98
93
|
return promises_1.default.readFile(file.sourcePath);
|
|
99
94
|
throw new Error('Attachment had no file and no body.');
|
|
100
95
|
}
|
|
101
|
-
function replacePath(
|
|
102
|
-
for (const
|
|
103
|
-
|
|
104
|
-
for (const attachment of attempt.attachments) {
|
|
105
|
-
if (attachment.path === target)
|
|
106
|
-
attachment.path = next;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
96
|
+
function replacePath(attachments, next) {
|
|
97
|
+
for (const attachment of attachments)
|
|
98
|
+
attachment.path = next;
|
|
110
99
|
}
|
|
111
100
|
function isImage(file) {
|
|
112
101
|
return (file.contentType || '').toLowerCase().startsWith('image/');
|
|
@@ -127,7 +116,7 @@ async function replaceFontFiles(css, fontDir) {
|
|
|
127
116
|
}
|
|
128
117
|
return embedFontUrls(css, (file) => uris.get(file) || `fonts/${file}`);
|
|
129
118
|
}
|
|
130
|
-
async function
|
|
119
|
+
async function writeReportContents(outputDir, report, files, uiDir, output = { inline: true, reportFileName: 'index.html' }) {
|
|
131
120
|
const stylesheet = node_path_1.default.join(uiDir, 'report.css');
|
|
132
121
|
const script = node_path_1.default.join(uiDir, 'report.js');
|
|
133
122
|
try {
|
|
@@ -137,7 +126,6 @@ async function writeReport(outputDir, report, files, uiDir, output = { inline: t
|
|
|
137
126
|
catch {
|
|
138
127
|
throw new Error('Reborn UI assets are missing. Build the package before running tests.');
|
|
139
128
|
}
|
|
140
|
-
await (0, paths_1.prepareOutput)(outputDir);
|
|
141
129
|
const assets = node_path_1.default.join(outputDir, 'assets');
|
|
142
130
|
await promises_1.default.mkdir(node_path_1.default.join(assets, 'attachments'), { recursive: true });
|
|
143
131
|
await promises_1.default.mkdir(node_path_1.default.join(assets, 'fonts'), { recursive: true });
|
|
@@ -149,17 +137,28 @@ async function writeReport(outputDir, report, files, uiDir, output = { inline: t
|
|
|
149
137
|
await promises_1.default.copyFile(script, node_path_1.default.join(assets, 'report.js'));
|
|
150
138
|
await promises_1.default.cp(node_path_1.default.join(uiDir, 'fonts'), node_path_1.default.join(assets, 'fonts'), { recursive: true });
|
|
151
139
|
}
|
|
140
|
+
const attachmentIndex = new Map();
|
|
141
|
+
for (const test of report.tests) {
|
|
142
|
+
for (const attempt of test.attempts) {
|
|
143
|
+
for (const attachment of attempt.attachments) {
|
|
144
|
+
const entries = attachmentIndex.get(attachment.path) || [];
|
|
145
|
+
entries.push(attachment);
|
|
146
|
+
attachmentIndex.set(attachment.path, entries);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
152
150
|
for (const file of files) {
|
|
151
|
+
const attachments = attachmentIndex.get(file.target) || [];
|
|
153
152
|
if (await tooLarge(file)) {
|
|
154
|
-
markOmitted(
|
|
153
|
+
markOmitted(attachments);
|
|
155
154
|
report.warnings.push(`Did not copy ${file.name} because it is larger than 25MB.`);
|
|
156
155
|
continue;
|
|
157
156
|
}
|
|
158
157
|
try {
|
|
159
158
|
if (output.inline && isImage(file)) {
|
|
160
159
|
const body = await fileBody(file);
|
|
161
|
-
const type = file.contentType || 'image/png';
|
|
162
|
-
replacePath(
|
|
160
|
+
const type = (0, format_1.baseType)(file.contentType || 'image/png');
|
|
161
|
+
replacePath(attachments, `data:${type};base64,${body.toString('base64')}`);
|
|
163
162
|
continue;
|
|
164
163
|
}
|
|
165
164
|
await writePending(outputDir, file);
|
|
@@ -179,4 +178,23 @@ async function writeReport(outputDir, report, files, uiDir, output = { inline: t
|
|
|
179
178
|
await promises_1.default.writeFile(indexPath, renderHtml(json, documentAssets), 'utf8');
|
|
180
179
|
return indexPath;
|
|
181
180
|
}
|
|
181
|
+
async function writeReport(outputDir, report, files, uiDir, output = { inline: true, reportFileName: 'index.html' }) {
|
|
182
|
+
if (!/^[A-Za-z0-9._-]+\.html$/.test(output.reportFileName)) {
|
|
183
|
+
throw new Error('Reborn reportFileName must be a single .html file name.');
|
|
184
|
+
}
|
|
185
|
+
// Build before replacing the old report: attachments may live inside it, and
|
|
186
|
+
// missing assets should leave the last successful report available.
|
|
187
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(outputDir), { recursive: true });
|
|
188
|
+
const staging = await promises_1.default.mkdtemp(node_path_1.default.join(node_path_1.default.dirname(outputDir), '.reborn-staging-'));
|
|
189
|
+
try {
|
|
190
|
+
await writeReportContents(staging, report, files, uiDir, output);
|
|
191
|
+
await (0, paths_1.prepareOutput)(outputDir);
|
|
192
|
+
await promises_1.default.rmdir(outputDir);
|
|
193
|
+
await promises_1.default.rename(staging, outputDir);
|
|
194
|
+
return node_path_1.default.join(outputDir, output.reportFileName);
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
await promises_1.default.rm(staging, { recursive: true, force: true });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
182
200
|
//# sourceMappingURL=render.js.map
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":";;;;;AAMA,sCAEC;AAUD,
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
package/dist/serialize.d.ts
CHANGED
package/dist/serialize.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.buildReport = buildReport;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
9
|
const format_1 = require("./format");
|
|
9
10
|
const paths_1 = require("./paths");
|
|
10
11
|
const status_1 = require("./status");
|
|
@@ -67,13 +68,15 @@ function simplifyError(error, rootDir) {
|
|
|
67
68
|
}
|
|
68
69
|
return simplified;
|
|
69
70
|
}
|
|
71
|
+
// Playwright records these around the steps a person would follow.
|
|
72
|
+
const PLAYWRIGHT_STEP = new Set(['pw:api', 'fixture', 'expect', 'test.attach']);
|
|
70
73
|
function mapSteps(steps, rootDir, depth, detail) {
|
|
71
74
|
if (!steps || depth > 12)
|
|
72
75
|
return [];
|
|
73
76
|
const mapped = [];
|
|
74
77
|
for (const step of steps) {
|
|
75
78
|
const kids = mapSteps(step.steps, rootDir, depth + 1, detail);
|
|
76
|
-
const hide = detail === 'user' && (step.category
|
|
79
|
+
const hide = detail === 'user' && PLAYWRIGHT_STEP.has(step.category);
|
|
77
80
|
const quietHook = detail === 'user' && step.category === 'hook' && !step.error;
|
|
78
81
|
if (hide || quietHook) {
|
|
79
82
|
mapped.push(...kids);
|
|
@@ -102,18 +105,19 @@ function mapSteps(steps, rootDir, depth, detail) {
|
|
|
102
105
|
function collectAttachments(result) {
|
|
103
106
|
const found = [];
|
|
104
107
|
const seen = new Set();
|
|
105
|
-
const visit = (attachments) => {
|
|
108
|
+
const visit = (attachments, primary = false) => {
|
|
106
109
|
for (const attachment of attachments || []) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
const source = attachment.path || (attachment.body ? (0, node_crypto_1.createHash)('sha256').update(attachment.body).digest('hex') : 'empty');
|
|
111
|
+
const key = `${attachment.name}:${attachment.contentType}:${source}`;
|
|
112
|
+
// The reporter can receive cloned buffers on both the result and the
|
|
113
|
+
// step. Preserve deliberate repeats in the result, but omit step copies.
|
|
114
|
+
if (!primary && seen.has(key))
|
|
111
115
|
continue;
|
|
112
116
|
seen.add(key);
|
|
113
117
|
found.push(attachment);
|
|
114
118
|
}
|
|
115
119
|
};
|
|
116
|
-
visit(result.attachments);
|
|
120
|
+
visit(result.attachments, true);
|
|
117
121
|
const visitSteps = (steps) => {
|
|
118
122
|
for (const step of steps || []) {
|
|
119
123
|
visit(step.attachments);
|
|
@@ -127,6 +131,8 @@ function buildReport(input) {
|
|
|
127
131
|
const files = [];
|
|
128
132
|
let fileIndex = 0;
|
|
129
133
|
const seenIds = new Map();
|
|
134
|
+
const originalIds = new Set(input.tests.map((test) => test.id));
|
|
135
|
+
const usedIds = new Set();
|
|
130
136
|
const sorted = [...input.tests].sort((a, b) => {
|
|
131
137
|
const aStart = a.results[0]?.startTime ? a.results[0].startTime.getTime() : Number.POSITIVE_INFINITY;
|
|
132
138
|
const bStart = b.results[0]?.startTime ? b.results[0].startTime.getTime() : Number.POSITIVE_INFINITY;
|
|
@@ -137,7 +143,10 @@ function buildReport(input) {
|
|
|
137
143
|
const tests = sorted.map((test, order) => {
|
|
138
144
|
const count = seenIds.get(test.id) || 0;
|
|
139
145
|
seenIds.set(test.id, count + 1);
|
|
140
|
-
|
|
146
|
+
let id = count === 0 ? test.id : `${test.id}-${count}`;
|
|
147
|
+
while ((originalIds.has(id) && id !== test.id) || usedIds.has(id))
|
|
148
|
+
id += '-duplicate';
|
|
149
|
+
usedIds.add(id);
|
|
141
150
|
const results = test.results || [];
|
|
142
151
|
const last = results[results.length - 1];
|
|
143
152
|
const rawStatus = last?.status || 'skipped';
|
|
@@ -151,6 +160,9 @@ function buildReport(input) {
|
|
|
151
160
|
const attempts = results.map((result) => {
|
|
152
161
|
const errors = (result.errors && result.errors.length > 0 ? result.errors : result.error ? [result.error] : [])
|
|
153
162
|
.map((error) => simplifyError(error, input.rootDir));
|
|
163
|
+
if (result.status === 'passed' && test.expectedStatus === 'failed') {
|
|
164
|
+
errors.push({ message: 'Expected to fail, but passed.' });
|
|
165
|
+
}
|
|
154
166
|
const attachments = collectAttachments(result).map((attachment) => {
|
|
155
167
|
const parsed = (0, format_1.parseShotName)(attachment.name || 'file');
|
|
156
168
|
const ext = (0, format_1.extensionFor)(attachment.contentType, attachment.path);
|