playwright-reborn 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 World of Z
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # playwright-reborn
2
+
3
+ A Playwright reporter. When a run finishes, it writes one HTML file you can open or archive. The file carries its own styles, fonts, and pictures. It does not need a server.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -D playwright-reborn
9
+ ```
10
+
11
+ ```ts
12
+ import { defineConfig } from '@playwright/test';
13
+
14
+ export default defineConfig({
15
+ reporter: [
16
+ ['list'],
17
+ ['playwright-reborn'],
18
+ ],
19
+ });
20
+ ```
21
+
22
+ That is the whole install. Overview, Tests, and Screenshots are always in the report. Logs and Files are included unless you turn them off.
23
+
24
+ ## Pages
25
+
26
+ - **Overview** shows the company name, how long the run took, the counts, and a duration chart. The chart counts tests under 1 minute, from 1 to 3 minutes, from 3 to 5 minutes, and over 5 minutes. **Chart** and **Timeline** switch that section. Timeline lists every test by name.
27
+ - **Tests** is the list and one test. A test has Steps, Error, Logs, and Screenshots. **Order** can group the list by file, or sort it slowest, fastest, or by the time it started.
28
+ - **Screenshots** is the gallery. Filter it to every step, the last failing step, or the last step of a passing attempt.
29
+ - **Logs** is output from the whole run. It is on by default.
30
+ - **Files** is traces, video, and other files that are not pictures. It is on by default.
31
+
32
+ `#overview`, `#tests`, `#screenshots`, `#logs`, and `#files` open those pages. `#tests/<id>` opens one test. Keys `1` onward switch the pages that are present. `/` jumps to the find field.
33
+
34
+ ## Settings
35
+
36
+ Pass any of these next to the reporter. Omitted settings use the default.
37
+
38
+ ```ts
39
+ ['playwright-reborn', {
40
+ company: 'Sandata',
41
+ accent: 'green',
42
+ open: 'on-failure',
43
+ outputFolder: 'reborn-report',
44
+ overview: 'chart',
45
+ screenshots: 'failure',
46
+ steps: 'user',
47
+ showLogs: true,
48
+ showFiles: true,
49
+ }]
50
+ ```
51
+
52
+ | Setting | Default | Allowed |
53
+ |---|---|---|
54
+ | `company` | `Sandata` | Any name. This is the large title. It does not change when a run fails. |
55
+ | `accent` | `green` | `green`, `red`, `blue`, `amber`, `violet`. Colors the light field, failure marks, and links. |
56
+ | `open` | `on-failure` | `always`, `never`, `on-failure`. When to open the HTML file after the run. |
57
+ | `outputFolder` | `reborn-report` | A folder path. Each run replaces that folder. If you leave this unset, a sharded run writes `reborn-report-shard-1` and so on. |
58
+ | `overview` | `chart` | `chart` or `timeline`. Which view the overview opens on. The buttons on the page can still switch it. |
59
+ | `screenshots` | `failure` | `off`, `failure`, `last`, or `steps`. When the fixture takes a picture. The Screenshots page stays in the report either way. |
60
+ | `steps` | `user` | `user` or `all`. `user` keeps the steps you wrote and the expectations. `all` also keeps Playwright's own API calls. |
61
+ | `showLogs` | `true` | `true` or `false`. `false` leaves the Logs page out. |
62
+ | `showFiles` | `true` | `true` or `false`. `false` leaves the Files page out. |
63
+
64
+ ## Screenshots
65
+
66
+ The reporter cannot photograph the page. Import the fixture in the tests that should capture shots:
67
+
68
+ ```ts
69
+ import { test, expect, step } from 'playwright-reborn/fixture';
70
+
71
+ await step('read the receipt', async () => {
72
+ await expect(page.locator('#order')).toHaveText('1842');
73
+ });
74
+ ```
75
+
76
+ `failure` and `last` take one picture at the end of an attempt, for tests that use `page`. `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.
77
+
78
+ Files larger than 25MB are listed and not copied.
79
+
80
+ ## License
81
+
82
+ Open source by World of Z. The code is MIT.
83
+
84
+ Geist and JetBrains Mono are included under the SIL Open Font License. Those license files are copied into each report.
@@ -0,0 +1,4 @@
1
+ import { expect } from '@playwright/test';
2
+ export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions>;
3
+ export { expect };
4
+ export declare function step(title: string, body: () => Promise<void>): Promise<void>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expect = exports.test = void 0;
4
+ exports.step = step;
5
+ const test_1 = require("@playwright/test");
6
+ Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_1.expect; } });
7
+ let activePage;
8
+ let activeInfo;
9
+ async function shot(name) {
10
+ if (!activePage || !activeInfo)
11
+ return;
12
+ try {
13
+ const body = await activePage.screenshot({ timeout: 5_000 });
14
+ await activeInfo.attach(name, { body, contentType: 'image/png' });
15
+ }
16
+ catch {
17
+ // A closed page should not hide the test result.
18
+ }
19
+ }
20
+ exports.test = test_1.test.extend({
21
+ page: async ({ page }, use, testInfo) => {
22
+ activePage = page;
23
+ activeInfo = testInfo;
24
+ try {
25
+ await use(page);
26
+ const mode = process.env.REBORN_SCREENSHOTS || 'failure';
27
+ const failed = testInfo.status === 'failed' || testInfo.status === 'timedOut';
28
+ if (mode === 'last' || (mode === 'steps' && !failed))
29
+ await shot('reborn:last');
30
+ if ((mode === 'failure' || mode === 'steps') && failed)
31
+ await shot('reborn:failure');
32
+ }
33
+ finally {
34
+ activePage = undefined;
35
+ activeInfo = undefined;
36
+ }
37
+ },
38
+ });
39
+ async function step(title, body) {
40
+ await exports.test.step(title, async () => {
41
+ try {
42
+ await body();
43
+ }
44
+ catch (error) {
45
+ if (process.env.REBORN_SCREENSHOTS === 'steps')
46
+ await shot(`reborn:step:${title}`);
47
+ throw error;
48
+ }
49
+ if (process.env.REBORN_SCREENSHOTS === 'steps')
50
+ await shot(`reborn:step:${title}`);
51
+ });
52
+ }
53
+ //# sourceMappingURL=fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":";;;AAkCA,oBAUC;AA5CD,2CAAkF;AAgCzE,uFAhCc,aAAM,OAgCd;AA9Bf,IAAI,UAA4B,CAAC;AACjC,IAAI,UAAgC,CAAC;AAErC,KAAK,UAAU,IAAI,CAAC,IAAY;IAC9B,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU;QAAE,OAAO;IACvC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;AACH,CAAC;AAEY,QAAA,IAAI,GAAG,WAAI,CAAC,MAAM,CAAC;IAC9B,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE;QACtC,UAAU,GAAG,IAAI,CAAC;QAClB,UAAU,GAAG,QAAQ,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;YAChB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS,CAAC;YACzD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC;YAC9E,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC;YAChF,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,OAAO,CAAC,IAAI,MAAM;gBAAE,MAAM,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACvF,CAAC;gBAAS,CAAC;YACT,UAAU,GAAG,SAAS,CAAC;YACvB,UAAU,GAAG,SAAS,CAAC;QACzB,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAII,KAAK,UAAU,IAAI,CAAC,KAAa,EAAE,IAAyB;IACjE,MAAM,YAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,OAAO;gBAAE,MAAM,IAAI,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC;YACnF,MAAM,KAAK,CAAC;QACd,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,OAAO;YAAE,MAAM,IAAI,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare function stripAnsi(value: string): string;
2
+ export declare function capText(value: string, limit?: number): string;
3
+ export declare function joinChunks(chunks: Array<string | Buffer> | undefined): string;
4
+ export declare function embedJson(value: unknown): string;
5
+ export declare function formatDuration(ms: number): string;
6
+ export declare function baseType(contentType: string): string;
7
+ export declare function extensionFor(contentType: string, sourcePath?: string): string;
8
+ export declare function attachmentKind(name: string, contentType: string): 'image' | 'video' | 'trace' | 'file';
9
+ export declare function parseShotName(name: string): {
10
+ name: string;
11
+ role?: 'step' | 'last' | 'failure';
12
+ stepTitle?: string;
13
+ };
14
+ export type DurationBand = 'under1' | '1to3' | '3to5' | 'over5';
15
+ export declare function durationBand(ms: number): DurationBand;
16
+ export declare function safeFileName(name: string, index: number, ext: string): string;
package/dist/format.js ADDED
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stripAnsi = stripAnsi;
4
+ exports.capText = capText;
5
+ exports.joinChunks = joinChunks;
6
+ exports.embedJson = embedJson;
7
+ exports.formatDuration = formatDuration;
8
+ exports.baseType = baseType;
9
+ exports.extensionFor = extensionFor;
10
+ exports.attachmentKind = attachmentKind;
11
+ exports.parseShotName = parseShotName;
12
+ exports.durationBand = durationBand;
13
+ exports.safeFileName = safeFileName;
14
+ const LOG_LIMIT = 100_000;
15
+ function stripAnsi(value) {
16
+ return value.replace(/\u001b\[[0-9;]*m/g, '');
17
+ }
18
+ function capText(value, limit = LOG_LIMIT) {
19
+ if (value.length <= limit)
20
+ return value;
21
+ return `${value.slice(0, limit)}\n… truncated\n`;
22
+ }
23
+ function joinChunks(chunks) {
24
+ if (!chunks || chunks.length === 0)
25
+ return '';
26
+ const text = chunks.map((chunk) => (typeof chunk === 'string' ? chunk : chunk.toString('utf8'))).join('');
27
+ return capText(stripAnsi(text));
28
+ }
29
+ function embedJson(value) {
30
+ return JSON.stringify(value)
31
+ .replace(/</g, '\\u003c')
32
+ .replace(/\u2028/g, '\\u2028')
33
+ .replace(/\u2029/g, '\\u2029');
34
+ }
35
+ function formatDuration(ms) {
36
+ if (!Number.isFinite(ms) || ms < 0)
37
+ ms = 0;
38
+ if (ms < 1000)
39
+ return `${Math.round(ms)}ms`;
40
+ if (ms < 60000) {
41
+ const seconds = ms / 1000;
42
+ const text = seconds >= 10 ? String(Math.round(seconds)) : seconds.toFixed(1).replace(/\.0$/, '');
43
+ return `${text}s`;
44
+ }
45
+ const total = Math.round(ms / 1000);
46
+ const minutes = Math.floor(total / 60);
47
+ const seconds = total % 60;
48
+ return `${minutes}m ${String(seconds).padStart(2, '0')}s`;
49
+ }
50
+ function baseType(contentType) {
51
+ return contentType.split(';')[0].trim().toLowerCase();
52
+ }
53
+ const EXTENSIONS = {
54
+ 'image/png': '.png',
55
+ 'image/jpeg': '.jpg',
56
+ 'image/jpg': '.jpg',
57
+ 'image/webp': '.webp',
58
+ 'image/gif': '.gif',
59
+ 'image/svg+xml': '.svg',
60
+ 'video/webm': '.webm',
61
+ 'video/mp4': '.mp4',
62
+ 'application/zip': '.zip',
63
+ 'text/plain': '.txt',
64
+ 'text/html': '.html',
65
+ 'application/json': '.json',
66
+ };
67
+ function extensionFor(contentType, sourcePath) {
68
+ const mapped = EXTENSIONS[baseType(contentType)];
69
+ if (mapped)
70
+ return mapped;
71
+ if (!sourcePath)
72
+ return '';
73
+ const match = sourcePath.match(/(\.[a-z0-9]{1,8})$/i);
74
+ return match ? match[1].toLowerCase() : '';
75
+ }
76
+ function attachmentKind(name, contentType) {
77
+ const type = baseType(contentType);
78
+ if (type.startsWith('image/'))
79
+ return 'image';
80
+ if (type.startsWith('video/'))
81
+ return 'video';
82
+ if (name.toLowerCase() === 'trace')
83
+ return 'trace';
84
+ return 'file';
85
+ }
86
+ function parseShotName(name) {
87
+ if (name.startsWith('reborn:step:')) {
88
+ const stepTitle = name.slice('reborn:step:'.length).trim() || 'Step';
89
+ return { name: stepTitle, role: 'step', stepTitle };
90
+ }
91
+ if (name === 'reborn:failure')
92
+ return { name: 'Failure', role: 'failure' };
93
+ if (name === 'reborn:last')
94
+ return { name: 'Last step', role: 'last' };
95
+ return { name: name || 'file' };
96
+ }
97
+ function durationBand(ms) {
98
+ if (!Number.isFinite(ms) || ms < 0)
99
+ ms = 0;
100
+ if (ms < 60_000)
101
+ return 'under1';
102
+ if (ms < 180_000)
103
+ return '1to3';
104
+ if (ms < 300_000)
105
+ return '3to5';
106
+ return 'over5';
107
+ }
108
+ function safeFileName(name, index, ext) {
109
+ const cleanExt = /^\.[a-z0-9]{1,8}$/i.test(ext) ? ext.toLowerCase() : '';
110
+ const base = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'file';
111
+ return `${String(index).padStart(3, '0')}-${base}${cleanExt}`;
112
+ }
113
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { FullConfig, FullResult, Reporter, Suite, TestError } from '@playwright/test/reporter';
2
+ import { type RebornOptions } from './options';
3
+ declare class RebornReporter implements Reporter {
4
+ private readonly parsed;
5
+ private suite;
6
+ private config;
7
+ private readonly errors;
8
+ constructor(options?: RebornOptions);
9
+ onBegin(config: FullConfig, suite: Suite): void;
10
+ onError(error: TestError): void;
11
+ printsToStdio(): boolean;
12
+ onEnd(result: FullResult): Promise<void>;
13
+ private outputBase;
14
+ private folderFor;
15
+ private finish;
16
+ }
17
+ export default RebornReporter;
18
+ export type { RebornOptions, RebornOpen } from './options';
package/dist/index.js ADDED
@@ -0,0 +1,91 @@
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
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const open_1 = require("./open");
8
+ const options_1 = require("./options");
9
+ const paths_1 = require("./paths");
10
+ const render_1 = require("./render");
11
+ const serialize_1 = require("./serialize");
12
+ const format_1 = require("./format");
13
+ class RebornReporter {
14
+ parsed;
15
+ suite;
16
+ config;
17
+ errors = [];
18
+ constructor(options = {}) {
19
+ this.parsed = (0, options_1.parseOptions)(options);
20
+ }
21
+ onBegin(config, suite) {
22
+ this.config = config;
23
+ this.suite = suite;
24
+ process.env.REBORN_SCREENSHOTS = this.parsed.screenshots;
25
+ }
26
+ onError(error) {
27
+ const message = (0, format_1.capText)((0, format_1.stripAnsi)(error.message || error.value || 'Runner error'), 20_000);
28
+ const entry = { message };
29
+ if (error.stack)
30
+ entry.stack = (0, format_1.capText)((0, format_1.stripAnsi)(error.stack), 20_000);
31
+ this.errors.push(entry);
32
+ }
33
+ printsToStdio() {
34
+ return false;
35
+ }
36
+ async onEnd(result) {
37
+ try {
38
+ await this.finish(result);
39
+ }
40
+ catch (error) {
41
+ const message = error instanceof Error ? error.stack || error.message : String(error);
42
+ process.stderr.write(`\nReborn reporter failed.\n${message}\n`);
43
+ throw error;
44
+ }
45
+ }
46
+ outputBase() {
47
+ if (this.config?.configFile)
48
+ return node_path_1.default.dirname(this.config.configFile);
49
+ return process.cwd();
50
+ }
51
+ folderFor(config) {
52
+ if (this.parsed.outputExplicit || !config.shard)
53
+ return this.parsed.outputFolder;
54
+ return `${this.parsed.outputFolder}-shard-${config.shard.current}`;
55
+ }
56
+ async finish(result) {
57
+ if (!this.config || !this.suite)
58
+ return;
59
+ const outputDir = (0, paths_1.resolveOutputFolder)(this.outputBase(), this.folderFor(this.config));
60
+ const status = result.status;
61
+ const { report, files } = (0, serialize_1.buildReport)({
62
+ tests: this.suite.allTests(),
63
+ rootDir: this.outputBase(),
64
+ playwrightVersion: this.config.version,
65
+ projectName: node_path_1.default.basename(this.outputBase()),
66
+ projects: this.config.projects.map((project) => project.name),
67
+ workers: this.config.workers,
68
+ shard: this.config.shard ? `${this.config.shard.current}/${this.config.shard.total}` : null,
69
+ status,
70
+ startTime: result.startTime,
71
+ duration: result.duration,
72
+ generatedAt: new Date().toISOString(),
73
+ errors: this.errors,
74
+ screenshots: this.parsed.screenshots,
75
+ stepDetail: this.parsed.steps,
76
+ company: this.parsed.company,
77
+ accent: this.parsed.accent,
78
+ showLogs: this.parsed.showLogs,
79
+ showFiles: this.parsed.showFiles,
80
+ overview: this.parsed.overview,
81
+ });
82
+ const indexPath = await (0, render_1.writeReport)(outputDir, report, files, node_path_1.default.join(__dirname, 'ui'));
83
+ process.stdout.write(`\nReborn report: ${indexPath}\n`);
84
+ const failed = status !== 'passed';
85
+ if (this.parsed.open === 'always' || (this.parsed.open === 'on-failure' && failed)) {
86
+ (0, open_1.openReport)(indexPath);
87
+ }
88
+ }
89
+ }
90
+ exports.default = RebornReporter;
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ export type DisplayStatus = 'passed' | 'failed' | 'flaky' | 'skipped' | 'timedOut' | 'interrupted';
2
+ export type RunStatus = 'passed' | 'failed' | 'timedout' | 'interrupted';
3
+ export type AttachmentKind = 'image' | 'video' | 'trace' | 'file';
4
+ export type ShotRole = 'step' | 'last' | 'failure';
5
+ export type ShotMode = 'off' | 'failure' | 'last' | 'steps';
6
+ export type Accent = 'green' | 'red' | 'blue' | 'amber' | 'violet';
7
+ export type OverviewMode = 'chart' | 'timeline';
8
+ export type StepDetail = 'user' | 'all';
9
+ export type Counts = {
10
+ total: number;
11
+ passed: number;
12
+ failed: number;
13
+ flaky: number;
14
+ skipped: number;
15
+ timedOut: number;
16
+ interrupted: number;
17
+ };
18
+ export type Step = {
19
+ title: string;
20
+ subtitle?: string;
21
+ category: string;
22
+ duration: number;
23
+ error?: string;
24
+ location?: string;
25
+ steps: Step[];
26
+ };
27
+ export type ReportError = {
28
+ message: string;
29
+ stack?: string;
30
+ snippet?: string;
31
+ location?: string;
32
+ };
33
+ export type ReportAttachment = {
34
+ name: string;
35
+ contentType: string;
36
+ path: string;
37
+ kind: AttachmentKind;
38
+ role?: ShotRole;
39
+ stepTitle?: string;
40
+ omitted?: boolean;
41
+ };
42
+ export type Attempt = {
43
+ retry: number;
44
+ status: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
45
+ duration: number;
46
+ startTime: string;
47
+ workerIndex: number;
48
+ errors: ReportError[];
49
+ stdout?: string;
50
+ stderr?: string;
51
+ steps: Step[];
52
+ attachments: ReportAttachment[];
53
+ };
54
+ export type ReportTest = {
55
+ id: string;
56
+ title: string;
57
+ file: string;
58
+ line: number;
59
+ project: string;
60
+ group: string[];
61
+ tags: string[];
62
+ annotations: {
63
+ type: string;
64
+ description?: string;
65
+ }[];
66
+ expectedFailure: boolean;
67
+ status: DisplayStatus;
68
+ duration: number;
69
+ order: number;
70
+ attempts: Attempt[];
71
+ };
72
+ export type Report = {
73
+ version: 2;
74
+ screenshots: ShotMode;
75
+ steps: StepDetail;
76
+ generatedAt: string;
77
+ playwrightVersion: string;
78
+ status: RunStatus;
79
+ word: string;
80
+ company: string;
81
+ accent: Accent;
82
+ showLogs: boolean;
83
+ showFiles: boolean;
84
+ overview: OverviewMode;
85
+ summary: string;
86
+ startTime: string;
87
+ duration: number;
88
+ projectName: string;
89
+ projects: string[];
90
+ workers: number;
91
+ shard: string | null;
92
+ counts: Counts;
93
+ warnings: string[];
94
+ errors: ReportError[];
95
+ tests: ReportTest[];
96
+ };
97
+ export type PendingFile = {
98
+ name: string;
99
+ target: string;
100
+ sourcePath?: string;
101
+ body?: Buffer;
102
+ };
package/dist/model.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":""}
package/dist/open.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function openReport(filePath: string): void;
package/dist/open.js ADDED
@@ -0,0 +1,19 @@
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.openReport = openReport;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function openReport(filePath) {
10
+ const file = node_path_1.default.resolve(filePath);
11
+ const child = process.platform === 'darwin'
12
+ ? (0, node_child_process_1.spawn)('open', [file], { detached: true, stdio: 'ignore' })
13
+ : process.platform === 'win32'
14
+ ? (0, node_child_process_1.spawn)('cmd', ['/c', 'start', '', file], { detached: true, stdio: 'ignore', windowsHide: true })
15
+ : (0, node_child_process_1.spawn)('xdg-open', [file], { detached: true, stdio: 'ignore' });
16
+ child.on('error', () => { });
17
+ child.unref();
18
+ }
19
+ //# sourceMappingURL=open.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.js","sourceRoot":"","sources":["../src/open.ts"],"names":[],"mappings":";;;;;AAGA,gCASC;AAZD,2DAA2C;AAC3C,0DAA6B;AAE7B,SAAgB,UAAU,CAAC,QAAgB;IACzC,MAAM,IAAI,GAAG,mBAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ;QACzC,CAAC,CAAC,IAAA,0BAAK,EAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QAC5D,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,IAAA,0BAAK,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACjG,CAAC,CAAC,IAAA,0BAAK,EAAC,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACrE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { Accent, OverviewMode, ShotMode, StepDetail } from './model';
2
+ export type RebornOpen = 'always' | 'never' | 'on-failure';
3
+ export type RebornOptions = {
4
+ outputFolder?: string;
5
+ open?: RebornOpen;
6
+ screenshots?: ShotMode;
7
+ steps?: StepDetail;
8
+ company?: string;
9
+ accent?: Accent;
10
+ showLogs?: boolean;
11
+ showFiles?: boolean;
12
+ overview?: OverviewMode;
13
+ };
14
+ export type ParsedOptions = {
15
+ outputFolder: string;
16
+ outputExplicit: boolean;
17
+ open: RebornOpen;
18
+ screenshots: ShotMode;
19
+ steps: StepDetail;
20
+ company: string;
21
+ accent: Accent;
22
+ showLogs: boolean;
23
+ showFiles: boolean;
24
+ overview: OverviewMode;
25
+ };
26
+ export declare function parseOptions(options?: RebornOptions): ParsedOptions;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseOptions = parseOptions;
4
+ const OPENS = new Set(['always', 'never', 'on-failure']);
5
+ const SHOTS = new Set(['off', 'failure', 'last', 'steps']);
6
+ const STEP_DETAILS = new Set(['user', 'all']);
7
+ const ACCENTS = new Set(['green', 'red', 'blue', 'amber', 'violet']);
8
+ const OVERVIEWS = new Set(['chart', 'timeline']);
9
+ function parseOptions(options = {}) {
10
+ if (options === null || typeof options !== 'object' || Array.isArray(options)) {
11
+ throw new Error('Reborn options must be an object.');
12
+ }
13
+ const open = options.open ?? 'on-failure';
14
+ if (!OPENS.has(open)) {
15
+ throw new Error('Reborn open must be "always", "never", or "on-failure".');
16
+ }
17
+ const screenshots = options.screenshots ?? 'failure';
18
+ if (!SHOTS.has(screenshots)) {
19
+ throw new Error('Reborn screenshots must be "off", "failure", "last", or "steps".');
20
+ }
21
+ const steps = options.steps ?? 'user';
22
+ if (!STEP_DETAILS.has(steps)) {
23
+ throw new Error('Reborn steps must be "user" or "all".');
24
+ }
25
+ const company = options.company ?? 'Sandata';
26
+ if (typeof company !== 'string' || !company.trim()) {
27
+ throw new Error('Reborn company must be a non-empty name.');
28
+ }
29
+ const accent = options.accent ?? 'green';
30
+ if (!ACCENTS.has(accent)) {
31
+ throw new Error('Reborn accent must be "green", "red", "blue", "amber", or "violet".');
32
+ }
33
+ const showLogs = flag(options.showLogs, 'showLogs');
34
+ const showFiles = flag(options.showFiles, 'showFiles');
35
+ const overview = options.overview ?? 'chart';
36
+ if (!OVERVIEWS.has(overview)) {
37
+ throw new Error('Reborn overview must be "chart" or "timeline".');
38
+ }
39
+ const outputExplicit = options.outputFolder !== undefined;
40
+ const outputFolder = options.outputFolder ?? 'reborn-report';
41
+ if (typeof outputFolder !== 'string' || !outputFolder.trim()) {
42
+ throw new Error('Reborn outputFolder must be a non-empty path.');
43
+ }
44
+ return { outputFolder, outputExplicit, open, screenshots, steps, company: company.trim(), accent, showLogs, showFiles, overview };
45
+ }
46
+ function flag(value, name) {
47
+ if (value === undefined)
48
+ return true;
49
+ if (typeof value !== 'boolean') {
50
+ throw new Error(`Reborn ${name} must be true or false.`);
51
+ }
52
+ return value;
53
+ }
54
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}