playwright-logbook 0.2.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 +21 -0
- package/README.md +134 -0
- package/dist/artifacts.d.ts +7 -0
- package/dist/artifacts.js +35 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/caseids.d.ts +12 -0
- package/dist/caseids.js +24 -0
- package/dist/caseids.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.js +6 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/debug.d.ts +7 -0
- package/dist/cli/commands/debug.js +14 -0
- package/dist/cli/commands/debug.js.map +1 -0
- package/dist/cli/commands/flaky.d.ts +8 -0
- package/dist/cli/commands/flaky.js +27 -0
- package/dist/cli/commands/flaky.js.map +1 -0
- package/dist/cli/commands/history.d.ts +8 -0
- package/dist/cli/commands/history.js +24 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/merge.d.ts +12 -0
- package/dist/cli/commands/merge.js +102 -0
- package/dist/cli/commands/merge.js.map +1 -0
- package/dist/cli/commands/report.d.ts +9 -0
- package/dist/cli/commands/report.js +13 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/summary.d.ts +7 -0
- package/dist/cli/commands/summary.js +14 -0
- package/dist/cli/commands/summary.js.map +1 -0
- package/dist/cli/format.d.ts +18 -0
- package/dist/cli/format.js +41 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/program.d.ts +12 -0
- package/dist/cli/program.js +74 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/clientlib.d.ts +48 -0
- package/dist/clientlib.js +136 -0
- package/dist/clientlib.js.map +1 -0
- package/dist/collect.d.ts +101 -0
- package/dist/collect.js +50 -0
- package/dist/collect.js.map +1 -0
- package/dist/debugpacket.d.ts +31 -0
- package/dist/debugpacket.js +105 -0
- package/dist/debugpacket.js.map +1 -0
- package/dist/env.d.ts +41 -0
- package/dist/env.js +49 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -0
- package/dist/history.d.ts +32 -0
- package/dist/history.js +72 -0
- package/dist/history.js.map +1 -0
- package/dist/index.cjs +1562 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/merge.d.ts +9 -0
- package/dist/merge.js +64 -0
- package/dist/merge.js.map +1 -0
- package/dist/model.d.ts +98 -0
- package/dist/model.js +129 -0
- package/dist/model.js.map +1 -0
- package/dist/options.d.ts +23 -0
- package/dist/options.js +5 -0
- package/dist/options.js.map +1 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.js +17 -0
- package/dist/paths.js.map +1 -0
- package/dist/publish.d.ts +14 -0
- package/dist/publish.js +2 -0
- package/dist/publish.js.map +1 -0
- package/dist/render.d.ts +6 -0
- package/dist/render.js +50 -0
- package/dist/render.js.map +1 -0
- package/dist/reporter.d.ts +53 -0
- package/dist/reporter.js +161 -0
- package/dist/reporter.js.map +1 -0
- package/dist/sanitize.d.ts +11 -0
- package/dist/sanitize.js +33 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/schema.d.ts +581 -0
- package/dist/schema.js +31 -0
- package/dist/schema.js.map +1 -0
- package/dist/signals.d.ts +9 -0
- package/dist/signals.js +37 -0
- package/dist/signals.js.map +1 -0
- package/dist/store.d.ts +41 -0
- package/dist/store.js +142 -0
- package/dist/store.js.map +1 -0
- package/dist/template/css.d.ts +1 -0
- package/dist/template/css.js +17 -0
- package/dist/template/css.js.map +1 -0
- package/dist/template/icons.d.ts +1 -0
- package/dist/template/icons.js +9 -0
- package/dist/template/icons.js.map +1 -0
- package/dist/template/js/core.d.ts +1 -0
- package/dist/template/js/core.js +90 -0
- package/dist/template/js/core.js.map +1 -0
- package/dist/template/js/failures.d.ts +1 -0
- package/dist/template/js/failures.js +33 -0
- package/dist/template/js/failures.js.map +1 -0
- package/dist/template/js/help.d.ts +1 -0
- package/dist/template/js/help.js +4 -0
- package/dist/template/js/help.js.map +1 -0
- package/dist/template/js/panel.d.ts +1 -0
- package/dist/template/js/panel.js +84 -0
- package/dist/template/js/panel.js.map +1 -0
- package/dist/template/js/project.d.ts +1 -0
- package/dist/template/js/project.js +40 -0
- package/dist/template/js/project.js.map +1 -0
- package/dist/template/js/run.d.ts +1 -0
- package/dist/template/js/run.js +38 -0
- package/dist/template/js/run.js.map +1 -0
- package/dist/template/js/tests.d.ts +1 -0
- package/dist/template/js/tests.js +23 -0
- package/dist/template/js/tests.js.map +1 -0
- package/dist/template/js/trends.d.ts +1 -0
- package/dist/template/js/trends.js +57 -0
- package/dist/template/js/trends.js.map +1 -0
- package/dist/template/tokens.d.ts +45 -0
- package/dist/template/tokens.js +12 -0
- package/dist/template/tokens.js.map +1 -0
- package/dist/template.d.ts +2 -0
- package/dist/template.js +19 -0
- package/dist/template.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +63 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { LogbookReporter as default, LogbookReporter } from './reporter.js';
|
|
2
|
+
export { shardFileSchema, runRecordSchema, runSummaryRecordSchema, testRecordSchema, stepRecordSchema, SCHEMA_VERSION } from './schema.js';
|
|
3
|
+
export type { ShardFile, RunRecord, RunSummaryRecord, TestRecord, ErrorRecord, StepRecord } from './schema.js';
|
|
4
|
+
export { mergeShards } from './merge.js';
|
|
5
|
+
export { FileHistoryStore, FileShardSink } from './store.js';
|
|
6
|
+
export type { HistoryStore, ShardSink } from './store.js';
|
|
7
|
+
export { computeFlaky, compareRuns, previousRun } from './history.js';
|
|
8
|
+
export { renderReport } from './render.js';
|
|
9
|
+
export { buildReportModel, renderTextSummary, renderMarkdownSummary, buildJsonSummary } from './model.js';
|
|
10
|
+
export type { ReportModel, ReportTest } from './model.js';
|
|
11
|
+
export { VERSION } from './version.js';
|
|
12
|
+
export type { Publisher, PublishContext, PublishResult } from './publish.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { LogbookReporter as default, LogbookReporter } from './reporter.js';
|
|
2
|
+
export { shardFileSchema, runRecordSchema, runSummaryRecordSchema, testRecordSchema, stepRecordSchema, SCHEMA_VERSION } from './schema.js';
|
|
3
|
+
export { mergeShards } from './merge.js';
|
|
4
|
+
export { FileHistoryStore, FileShardSink } from './store.js';
|
|
5
|
+
export { computeFlaky, compareRuns, previousRun } from './history.js';
|
|
6
|
+
export { renderReport } from './render.js';
|
|
7
|
+
export { buildReportModel, renderTextSummary, renderMarkdownSummary, buildJsonSummary } from './model.js';
|
|
8
|
+
export { VERSION } from './version.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3I,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE1G,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunRecord, ShardFile } from './schema.js';
|
|
2
|
+
/** Merge shard files into a deterministic run record. */
|
|
3
|
+
export declare function mergeShards(shards: ShardFile[], opts?: {
|
|
4
|
+
force?: boolean;
|
|
5
|
+
outputDir?: string;
|
|
6
|
+
}): {
|
|
7
|
+
run: RunRecord;
|
|
8
|
+
warnings: string[];
|
|
9
|
+
};
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { LogbookError } from './errors.js';
|
|
2
|
+
const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
3
|
+
const numberOf = (shard) => shard.shard?.current ?? 1;
|
|
4
|
+
const totalOf = (shard) => shard.shard?.total ?? 1;
|
|
5
|
+
const testOrder = (a, b) => compare(a.file, b.file) || a.line - b.line || compare(a.project, b.project) || compare(a.testId, b.testId);
|
|
6
|
+
function selectedShards(input, force, warnings) {
|
|
7
|
+
const byNumber = new Map();
|
|
8
|
+
for (const shard of [...input].sort((a, b) => numberOf(a) - numberOf(b) || compare(a.endedAt, b.endedAt) || compare(a.runId, b.runId))) {
|
|
9
|
+
const previous = byNumber.get(numberOf(shard));
|
|
10
|
+
if (previous && !force)
|
|
11
|
+
throw new LogbookError('DUPLICATE_SHARD', `duplicate shard ${numberOf(shard)}`);
|
|
12
|
+
if (previous)
|
|
13
|
+
warnings.push(`duplicate shard ${numberOf(shard)}; kept latest`);
|
|
14
|
+
byNumber.set(numberOf(shard), shard);
|
|
15
|
+
}
|
|
16
|
+
return [...byNumber.values()].sort((a, b) => numberOf(a) - numberOf(b));
|
|
17
|
+
}
|
|
18
|
+
function mergedTests(shards, warnings) {
|
|
19
|
+
const seen = new Set();
|
|
20
|
+
const tests = [];
|
|
21
|
+
for (const shard of shards)
|
|
22
|
+
for (const test of shard.tests) {
|
|
23
|
+
const key = `${test.testId}#${test.repeatEachIndex}`;
|
|
24
|
+
if (seen.has(key)) {
|
|
25
|
+
warnings.push(`duplicate test ${key}; kept first`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
seen.add(key);
|
|
29
|
+
tests.push(test);
|
|
30
|
+
}
|
|
31
|
+
return tests.sort(testOrder);
|
|
32
|
+
}
|
|
33
|
+
/** Merge shard files into a deterministic run record. */
|
|
34
|
+
export function mergeShards(shards, opts = {}) {
|
|
35
|
+
if (shards.length === 0)
|
|
36
|
+
throw new LogbookError('NO_DATA', 'no shards to merge');
|
|
37
|
+
const force = opts.force ?? false;
|
|
38
|
+
const warnings = [];
|
|
39
|
+
const total = totalOf(shards[0]);
|
|
40
|
+
if (shards.some((shard) => totalOf(shard) !== total))
|
|
41
|
+
throw new LogbookError('SHARD_MISMATCH', 'shard totals differ');
|
|
42
|
+
const newest = [...shards].sort((a, b) => compare(b.endedAt, a.endedAt) || compare(a.runId, b.runId))[0];
|
|
43
|
+
const runId = newest.runId;
|
|
44
|
+
if (shards.some((shard) => shard.runId !== runId)) {
|
|
45
|
+
if (!force)
|
|
46
|
+
throw new LogbookError('SHARD_MISMATCH', 'shard run IDs differ');
|
|
47
|
+
warnings.push(`shard run IDs differ; used ${runId}`);
|
|
48
|
+
}
|
|
49
|
+
const selected = selectedShards(shards, force, warnings);
|
|
50
|
+
const tests = mergedTests(selected, warnings);
|
|
51
|
+
const first = selected[0];
|
|
52
|
+
const startedAt = [...selected].map((shard) => shard.startedAt).sort(compare)[0];
|
|
53
|
+
const endedAt = [...selected].map((shard) => shard.endedAt).sort(compare).at(-1);
|
|
54
|
+
const summary = { total: tests.length, passed: tests.filter((test) => test.outcome === 'expected').length, failed: tests.filter((test) => test.outcome === 'unexpected').length, flaky: tests.filter((test) => test.outcome === 'flaky').length, skipped: tests.filter((test) => test.outcome === 'skipped').length };
|
|
55
|
+
const status = selected.some((shard) => shard.status === 'interrupted') ? 'interrupted' : selected.some((shard) => shard.status === 'timedout') ? 'timedout' : selected.some((shard) => shard.status === 'failed') || summary.failed > 0 ? 'failed' : 'passed';
|
|
56
|
+
const errors = new Map();
|
|
57
|
+
for (const shard of selected)
|
|
58
|
+
for (const error of shard.globalErrors)
|
|
59
|
+
if (!errors.has(error.message))
|
|
60
|
+
errors.set(error.message, error);
|
|
61
|
+
const receivedShards = selected.map(numberOf);
|
|
62
|
+
return { run: { schemaVersion: 1, kind: 'run', runId, title: first.title, startedAt, endedAt, durationMs: new Date(endedAt).getTime() - new Date(startedAt).getTime(), status, complete: receivedShards.length === total, expectedShards: total, receivedShards, env: first.env, project: first.project, paths: { outputDir: opts.outputDir ?? '.logbook' }, summary, tests, globalErrors: [...errors.values()] }, warnings };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Comparison, FlakyEntry } from './history.js';
|
|
2
|
+
import type { RunRecord, RunSummaryRecord, TestRecord } from './schema.js';
|
|
3
|
+
import type { ArtifactAvailability } from './artifacts.js';
|
|
4
|
+
export type ReportTest = TestRecord & {
|
|
5
|
+
timing: {
|
|
6
|
+
startedAt: string;
|
|
7
|
+
workerIndex: number;
|
|
8
|
+
} | null;
|
|
9
|
+
};
|
|
10
|
+
export interface ReportModel {
|
|
11
|
+
schemaVersion: 1;
|
|
12
|
+
generator: {
|
|
13
|
+
name: 'playwright-logbook';
|
|
14
|
+
version: string;
|
|
15
|
+
};
|
|
16
|
+
summaryMarkdown: string;
|
|
17
|
+
generatedAt: string | null;
|
|
18
|
+
attachmentAvailability?: ArtifactAvailability;
|
|
19
|
+
run: Omit<RunRecord, 'tests'> & {
|
|
20
|
+
tests: ReportTest[];
|
|
21
|
+
};
|
|
22
|
+
history: RunSummaryRecord[];
|
|
23
|
+
previous: RunSummaryRecord | null;
|
|
24
|
+
comparison: Comparison | null;
|
|
25
|
+
flaky: FlakyEntry[];
|
|
26
|
+
slowest: {
|
|
27
|
+
testId: string;
|
|
28
|
+
title: string;
|
|
29
|
+
file: string;
|
|
30
|
+
project: string;
|
|
31
|
+
durationMs: number;
|
|
32
|
+
}[];
|
|
33
|
+
files: {
|
|
34
|
+
file: string;
|
|
35
|
+
total: number;
|
|
36
|
+
failed: number;
|
|
37
|
+
flaky: number;
|
|
38
|
+
skipped: number;
|
|
39
|
+
durationMs: number;
|
|
40
|
+
}[];
|
|
41
|
+
tags: {
|
|
42
|
+
tag: string;
|
|
43
|
+
count: number;
|
|
44
|
+
}[];
|
|
45
|
+
delta: {
|
|
46
|
+
previousRunId: string;
|
|
47
|
+
passRatePp: number | null;
|
|
48
|
+
failed: number;
|
|
49
|
+
flaky: number;
|
|
50
|
+
durationPct: number | null;
|
|
51
|
+
} | null;
|
|
52
|
+
errorGroups: {
|
|
53
|
+
signature: string;
|
|
54
|
+
count: number;
|
|
55
|
+
projects: string[];
|
|
56
|
+
testIds: string[];
|
|
57
|
+
newCount: number;
|
|
58
|
+
}[];
|
|
59
|
+
recent: Record<string, string>;
|
|
60
|
+
projects: {
|
|
61
|
+
name: string;
|
|
62
|
+
total: number;
|
|
63
|
+
passed: number;
|
|
64
|
+
failed: number;
|
|
65
|
+
flaky: number;
|
|
66
|
+
skipped: number;
|
|
67
|
+
durationMs: number;
|
|
68
|
+
}[];
|
|
69
|
+
}
|
|
70
|
+
export declare function errorSignature(message: string | null): string;
|
|
71
|
+
/** Build compact, sorted report data from a stored run and its history. */
|
|
72
|
+
export declare function buildReportModel(input: {
|
|
73
|
+
run: RunRecord;
|
|
74
|
+
summaries: RunSummaryRecord[];
|
|
75
|
+
previous?: RunSummaryRecord | null;
|
|
76
|
+
flaky?: FlakyEntry[];
|
|
77
|
+
comparison?: Comparison | null;
|
|
78
|
+
generatedAt?: string | null;
|
|
79
|
+
historyLimit?: number;
|
|
80
|
+
recentRuns?: RunRecord[];
|
|
81
|
+
attachmentAvailability?: ArtifactAvailability;
|
|
82
|
+
}): ReportModel;
|
|
83
|
+
/** Format a duration for human-readable summaries. */
|
|
84
|
+
export declare function formatDuration(milliseconds: number): string;
|
|
85
|
+
/** Render a plain-text run summary. */
|
|
86
|
+
export declare function renderTextSummary(model: ReportModel): string;
|
|
87
|
+
/** Render a portable Markdown summary for CI systems. */
|
|
88
|
+
export declare function renderMarkdownSummary(model: ReportModel): string;
|
|
89
|
+
/** Build the concise JSON summary returned by the CLI. */
|
|
90
|
+
export declare function buildJsonSummary(model: ReportModel): {
|
|
91
|
+
runId: string;
|
|
92
|
+
status: RunRecord['status'];
|
|
93
|
+
complete: boolean;
|
|
94
|
+
summary: RunRecord['summary'];
|
|
95
|
+
durationMs: number;
|
|
96
|
+
comparison: Record<keyof Comparison, number>;
|
|
97
|
+
flaky: number;
|
|
98
|
+
};
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { testResultKind } from './history.js';
|
|
2
|
+
import { VERSION } from './version.js';
|
|
3
|
+
const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
4
|
+
export function errorSignature(message) {
|
|
5
|
+
if (!message?.trim())
|
|
6
|
+
return 'No error message';
|
|
7
|
+
const line = message.split(/\r?\n/).find((part) => part.trim()) ?? '';
|
|
8
|
+
return line.replace(/"[^"]*"|'[^']*'|`[^`]*`/g, '"…"')
|
|
9
|
+
.replace(/\b(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|0x[0-9a-f]+|[0-9a-f]{8,})\b/gi, 'ID')
|
|
10
|
+
.replace(/\d+/g, 'N').replace(/\bNms\b/g, 'N ms').replace(/\s+/g, ' ').trim().slice(0, 140);
|
|
11
|
+
}
|
|
12
|
+
function currentSummary(run) {
|
|
13
|
+
return { schemaVersion: 1, runId: run.runId, title: run.title, startedAt: run.startedAt, durationMs: run.durationMs, status: run.status, complete: run.complete, summary: run.summary, branch: run.env.git.branch, commit: run.env.git.commit, ciProvider: run.env.ci?.provider ?? null, buildUrl: run.env.ci?.buildUrl ?? null };
|
|
14
|
+
}
|
|
15
|
+
function slim(test) {
|
|
16
|
+
return test.outcome === 'expected' && test.attemptCount === 1 && test.attempts[0]?.errors.length === 0 && test.attempts[0].attachments.length === 0 && !test.attempts[0].steps?.length && !test.attempts[0].stdout && !test.attempts[0].stderr
|
|
17
|
+
? { ...test, attempts: [] } : test;
|
|
18
|
+
}
|
|
19
|
+
/** Build compact, sorted report data from a stored run and its history. */
|
|
20
|
+
export function buildReportModel(input) {
|
|
21
|
+
const { run } = input;
|
|
22
|
+
const all = new Map(input.summaries.map((entry) => [entry.runId, entry]));
|
|
23
|
+
all.set(run.runId, currentSummary(run));
|
|
24
|
+
const newest = [...all.values()].sort((a, b) => compare(b.startedAt, a.startedAt) || compare(a.runId, b.runId));
|
|
25
|
+
const historyLimit = Math.max(1, input.historyLimit ?? 30);
|
|
26
|
+
const selected = newest.slice(0, historyLimit);
|
|
27
|
+
if (!selected.some((item) => item.runId === run.runId))
|
|
28
|
+
selected.splice(-1, 1, currentSummary(run));
|
|
29
|
+
const history = selected.sort((a, b) => compare(a.startedAt, b.startedAt) || compare(a.runId, b.runId));
|
|
30
|
+
const files = new Map();
|
|
31
|
+
const tags = new Map();
|
|
32
|
+
const projects = new Map();
|
|
33
|
+
const groups = new Map();
|
|
34
|
+
const newIds = new Set(input.comparison?.newFailures.map((item) => item.testId) ?? []);
|
|
35
|
+
for (const test of run.tests) {
|
|
36
|
+
const item = files.get(test.file) ?? { file: test.file, total: 0, failed: 0, flaky: 0, skipped: 0, durationMs: 0 };
|
|
37
|
+
item.total += 1;
|
|
38
|
+
item.durationMs += test.durationMs;
|
|
39
|
+
if (test.outcome === 'unexpected')
|
|
40
|
+
item.failed += 1;
|
|
41
|
+
if (test.outcome === 'flaky')
|
|
42
|
+
item.flaky += 1;
|
|
43
|
+
if (test.outcome === 'skipped')
|
|
44
|
+
item.skipped += 1;
|
|
45
|
+
files.set(test.file, item);
|
|
46
|
+
for (const tag of test.tags)
|
|
47
|
+
tags.set(tag, (tags.get(tag) ?? 0) + 1);
|
|
48
|
+
const project = projects.get(test.project) ?? { name: test.project, total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0, durationMs: 0 };
|
|
49
|
+
project.total += 1;
|
|
50
|
+
project.durationMs += test.durationMs;
|
|
51
|
+
if (test.outcome === 'expected')
|
|
52
|
+
project.passed += 1;
|
|
53
|
+
if (test.outcome === 'unexpected')
|
|
54
|
+
project.failed += 1;
|
|
55
|
+
if (test.outcome === 'flaky')
|
|
56
|
+
project.flaky += 1;
|
|
57
|
+
if (test.outcome === 'skipped')
|
|
58
|
+
project.skipped += 1;
|
|
59
|
+
projects.set(test.project, project);
|
|
60
|
+
if (test.outcome === 'unexpected' || test.outcome === 'flaky') {
|
|
61
|
+
const signature = errorSignature(test.firstError?.message ?? null);
|
|
62
|
+
const group = groups.get(signature) ?? { signature, count: 0, projects: new Set(), testIds: [], newCount: 0 };
|
|
63
|
+
group.count += 1;
|
|
64
|
+
group.projects.add(test.project);
|
|
65
|
+
group.testIds.push(test.testId);
|
|
66
|
+
if (newIds.has(test.testId))
|
|
67
|
+
group.newCount += 1;
|
|
68
|
+
groups.set(signature, group);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const previous = input.previous ?? null;
|
|
72
|
+
const passRate = (summary) => summary.total ? summary.passed / summary.total : null;
|
|
73
|
+
const currentRate = passRate(run.summary), previousRate = previous && passRate(previous.summary);
|
|
74
|
+
const delta = previous ? { previousRunId: previous.runId, passRatePp: currentRate === null || previousRate === null ? null : Math.round((currentRate - previousRate) * 1000) / 10, failed: run.summary.failed - previous.summary.failed, flaky: run.summary.flaky - previous.summary.flaky, durationPct: previous.durationMs ? Math.round((run.durationMs - previous.durationMs) / previous.durationMs * 100) : null } : null;
|
|
75
|
+
const older = (input.recentRuns ?? []).filter((item) => item.runId !== run.runId).slice(-10);
|
|
76
|
+
const olderTests = older.map((item) => new Map(item.tests.map((test) => [test.testId, test])));
|
|
77
|
+
const recent = {};
|
|
78
|
+
for (const test of [...run.tests].sort((a, b) => compare(a.testId, b.testId)))
|
|
79
|
+
recent[test.testId] = olderTests.map((tests) => {
|
|
80
|
+
const prior = tests.get(test.testId);
|
|
81
|
+
return prior ? ({ pass: 'p', fail: 'f', flaky: 'k', skip: 's' })[testResultKind(prior)] : '-';
|
|
82
|
+
}).join('');
|
|
83
|
+
const model = { schemaVersion: 1, generator: { name: 'playwright-logbook', version: VERSION }, summaryMarkdown: '', generatedAt: input.generatedAt ?? null, ...(input.attachmentAvailability ? { attachmentAvailability: input.attachmentAvailability } : {}), run: { ...run, tests: run.tests.map((test) => ({ ...slim(test), timing: test.attempts[0] ? { startedAt: test.attempts[0].startedAt, workerIndex: test.attempts[0].workerIndex } : null })) }, history, previous, comparison: input.comparison ?? null, flaky: (input.flaky ?? []).slice(0, 50), slowest: [...run.tests].sort((a, b) => b.durationMs - a.durationMs || compare(a.testId, b.testId)).slice(0, 10).map(({ testId, title, file, project, durationMs }) => ({ testId, title, file, project, durationMs })), files: [...files.values()].sort((a, b) => compare(a.file, b.file)), tags: [...tags].map(([tag, count]) => ({ tag, count })).sort((a, b) => b.count - a.count || compare(a.tag, b.tag)), delta, errorGroups: [...groups.values()].map((group) => ({ ...group, projects: [...group.projects].sort(compare), testIds: group.testIds.sort(compare) })).sort((a, b) => b.count - a.count || compare(a.signature, b.signature)), recent, projects: [...projects.values()].sort((a, b) => compare(a.name, b.name)) };
|
|
84
|
+
model.summaryMarkdown = renderMarkdownSummary(model);
|
|
85
|
+
return model;
|
|
86
|
+
}
|
|
87
|
+
/** Format a duration for human-readable summaries. */
|
|
88
|
+
export function formatDuration(milliseconds) {
|
|
89
|
+
if (milliseconds < 1000)
|
|
90
|
+
return `${Math.round(milliseconds)}ms`;
|
|
91
|
+
if (milliseconds < 60000)
|
|
92
|
+
return `${(milliseconds / 1000).toFixed(1)}s`;
|
|
93
|
+
return `${Math.floor(milliseconds / 60000)}m ${Math.floor((milliseconds % 60000) / 1000)}s`;
|
|
94
|
+
}
|
|
95
|
+
const status = (model) => `${model.run.status.toUpperCase()}${model.run.complete ? '' : ` (INCOMPLETE: received shards ${model.run.receivedShards.join(',')} of ${model.run.expectedShards ?? '?'})`}`;
|
|
96
|
+
/** Render a plain-text run summary. */
|
|
97
|
+
export function renderTextSummary(model) {
|
|
98
|
+
const { run } = model;
|
|
99
|
+
return `${run.runId}: ${status(model)} — ${run.summary.passed} passed, ${run.summary.failed} failed, ${run.summary.flaky} flaky, ${run.summary.skipped} skipped (${formatDuration(run.durationMs)})\n`;
|
|
100
|
+
}
|
|
101
|
+
/** Render a portable Markdown summary for CI systems. */
|
|
102
|
+
export function renderMarkdownSummary(model) {
|
|
103
|
+
const { run } = model;
|
|
104
|
+
const lines = [`### Playwright run ${run.runId} — ${status(model)}`, '| Total | Passed | Failed | Flaky | Skipped | Duration |', '|---|---|---|---|---|---|', `| ${run.summary.total} | ${run.summary.passed} | ${run.summary.failed} | ${run.summary.flaky} | ${run.summary.skipped} | ${formatDuration(run.durationMs)} |`];
|
|
105
|
+
if (model.comparison?.newFailures.length) {
|
|
106
|
+
lines.push('', `**New failures (${model.comparison.newFailures.length})**`);
|
|
107
|
+
for (const item of model.comparison.newFailures)
|
|
108
|
+
lines.push(`- \`${item.file}\` › ${item.title} [${item.project}]`);
|
|
109
|
+
}
|
|
110
|
+
const flaky = run.tests.filter((test) => test.outcome === 'flaky');
|
|
111
|
+
if (flaky.length) {
|
|
112
|
+
lines.push('', `**Flaky in this run (${flaky.length})**`);
|
|
113
|
+
for (const item of flaky)
|
|
114
|
+
lines.push(`- \`${item.file}\` › ${item.title} [${item.project}]`);
|
|
115
|
+
}
|
|
116
|
+
let footer = `**Run**: ${run.env.ci?.buildUrl ?? run.runId}`;
|
|
117
|
+
if (run.env.git.branch)
|
|
118
|
+
footer += ` · branch ${run.env.git.branch}`;
|
|
119
|
+
if (run.env.git.commit)
|
|
120
|
+
footer += ` · commit ${run.env.git.commit.slice(0, 7)}`;
|
|
121
|
+
lines.push('', footer);
|
|
122
|
+
return `${lines.join('\n')}\n`;
|
|
123
|
+
}
|
|
124
|
+
/** Build the concise JSON summary returned by the CLI. */
|
|
125
|
+
export function buildJsonSummary(model) {
|
|
126
|
+
const counts = model.comparison;
|
|
127
|
+
return { runId: model.run.runId, status: model.run.status, complete: model.run.complete, summary: model.run.summary, durationMs: model.run.durationMs, comparison: { newFailures: counts?.newFailures.length ?? 0, fixed: counts?.fixed.length ?? 0, stillFailing: counts?.stillFailing.length ?? 0, newTests: counts?.newTests.length ?? 0, removedTests: counts?.removedTests.length ?? 0 }, flaky: model.flaky.length };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface LogbookOptions {
|
|
2
|
+
outputDir?: string;
|
|
3
|
+
runId?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
redact?: (string | RegExp)[];
|
|
6
|
+
autoMerge?: boolean;
|
|
7
|
+
autoReport?: boolean;
|
|
8
|
+
historyLimit?: number;
|
|
9
|
+
maxTextLength?: number;
|
|
10
|
+
caseIdPatterns?: string[];
|
|
11
|
+
quiet?: boolean;
|
|
12
|
+
captureDetails?: boolean | {
|
|
13
|
+
steps?: boolean;
|
|
14
|
+
output?: boolean;
|
|
15
|
+
images?: boolean;
|
|
16
|
+
};
|
|
17
|
+
maxSteps?: number;
|
|
18
|
+
maxOutputLength?: number;
|
|
19
|
+
maxImageBytes?: number;
|
|
20
|
+
maxEmbeddedBytes?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Resolve reporter options with deterministic defaults. */
|
|
23
|
+
export declare function resolveOptions(options?: LogbookOptions): Required<Pick<LogbookOptions, 'outputDir' | 'autoMerge' | 'autoReport' | 'quiet' | 'historyLimit' | 'maxTextLength'>> & LogbookOptions;
|
package/dist/options.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Resolve reporter options with deterministic defaults. */
|
|
2
|
+
export function resolveOptions(options = {}) {
|
|
3
|
+
return { outputDir: '.logbook', autoMerge: true, autoReport: true, historyLimit: 30, maxTextLength: 4000, quiet: false, captureDetails: false, maxSteps: 100, maxOutputLength: 2000, maxImageBytes: 250000, maxEmbeddedBytes: 5000000, ...options };
|
|
4
|
+
}
|
|
5
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAkBA,4DAA4D;AAC5D,MAAM,UAAU,cAAc,CAAC,UAA0B,EAAE;IACzD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;AACtP,CAAC"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Convert a platform path to a POSIX path. */
|
|
2
|
+
export declare function posixPath(value: string): string;
|
|
3
|
+
/** Return a path relative to the project root using POSIX separators. */
|
|
4
|
+
export declare function toRel(projectRoot: string, value: string): string;
|
|
5
|
+
/** Return a relative attachment path, or null when it is outside the project root. */
|
|
6
|
+
export declare function toRelOrNull(projectRoot: string, value: string): string | null;
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
/** Convert a platform path to a POSIX path. */
|
|
3
|
+
export function posixPath(value) { return value.split(path.sep).join('/'); }
|
|
4
|
+
function pathModule(...values) {
|
|
5
|
+
return values.some((value) => /^[A-Za-z]:[\\/]/.test(value) || value.includes('\\')) ? path.win32 : path.posix;
|
|
6
|
+
}
|
|
7
|
+
/** Return a path relative to the project root using POSIX separators. */
|
|
8
|
+
export function toRel(projectRoot, value) {
|
|
9
|
+
return pathModule(projectRoot, value).relative(projectRoot, value).replaceAll('\\', '/');
|
|
10
|
+
}
|
|
11
|
+
/** Return a relative attachment path, or null when it is outside the project root. */
|
|
12
|
+
export function toRelOrNull(projectRoot, value) {
|
|
13
|
+
const api = pathModule(projectRoot, value);
|
|
14
|
+
const relative = api.relative(projectRoot, value);
|
|
15
|
+
return relative === '' || (!relative.startsWith('..') && !api.isAbsolute(relative)) ? relative.replaceAll('\\', '/') : null;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,+CAA+C;AAC/C,MAAM,UAAU,SAAS,CAAC,KAAa,IAAY,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAE5F,SAAS,UAAU,CAAC,GAAG,MAAgB;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;AACjH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,KAAK,CAAC,WAAmB,EAAE,KAAa;IACtD,OAAO,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC3F,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,KAAa;IAC5D,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAClD,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9H,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RunRecord } from './schema.js';
|
|
2
|
+
export interface PublishContext {
|
|
3
|
+
run: RunRecord;
|
|
4
|
+
dryRun: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface PublishResult {
|
|
7
|
+
published: number;
|
|
8
|
+
skipped: number;
|
|
9
|
+
errors: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface Publisher {
|
|
12
|
+
name: string;
|
|
13
|
+
publish(ctx: PublishContext): Promise<PublishResult>;
|
|
14
|
+
}
|
package/dist/publish.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../src/publish.ts"],"names":[],"mappings":""}
|
package/dist/render.d.ts
ADDED
package/dist/render.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { donutSegments, formatDuration, safeHref } from './clientlib.js';
|
|
3
|
+
import { buildClientScript, buildStyles } from './template.js';
|
|
4
|
+
const escapeHtml = (value) => value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
|
|
5
|
+
const safeJson = (value) => JSON.stringify(value).replaceAll('<', '\\u003c').replaceAll('>', '\\u003e').replaceAll('&', '\\u0026').replaceAll('\u2028', '\\u2028').replaceAll('\u2029', '\\u2029');
|
|
6
|
+
const tabs = ['tests', 'failures', 'trends', 'flaky', 'run', 'project'];
|
|
7
|
+
function attachmentLinks(model, reportDir) {
|
|
8
|
+
const links = {};
|
|
9
|
+
for (const test of model.run.tests)
|
|
10
|
+
for (const attempt of test.attempts)
|
|
11
|
+
for (const attachment of attempt.attachments) {
|
|
12
|
+
const file = attachment.path;
|
|
13
|
+
if (file && model.attachmentAvailability?.[file] !== 'missing' && !path.posix.isAbsolute(file) && !file.split('/').includes('..') && !file.includes('\\') && !file.includes(':'))
|
|
14
|
+
links[file] = path.posix.relative(reportDir, file);
|
|
15
|
+
}
|
|
16
|
+
return links;
|
|
17
|
+
}
|
|
18
|
+
function donut(model) {
|
|
19
|
+
const summary = model.run.summary;
|
|
20
|
+
const rate = summary.total ? `${Math.round(summary.passed / summary.total * 100)}%` : '—';
|
|
21
|
+
const arcs = donutSegments(summary, 54).map((item) => `<circle class="donut-segment ${item.kind}" cx="72" cy="72" r="54" fill="none" stroke-width="15" stroke-dasharray="${item.dasharray}" stroke-dashoffset="${item.offset}" transform="rotate(-90 72 72)"/>`).join('');
|
|
22
|
+
return `<svg class="donut" viewBox="0 0 144 144" role="img" aria-label="${escapeHtml(rate)} passed"><circle class="donut-track" cx="72" cy="72" r="54" fill="none" stroke-width="15"/>${arcs}<text class="donut-center" x="72" y="72" text-anchor="middle">${rate}</text><text class="donut-label" x="72" y="88" text-anchor="middle">passed</text></svg>`;
|
|
23
|
+
}
|
|
24
|
+
function comparison(model) {
|
|
25
|
+
const delta = model.delta;
|
|
26
|
+
if (!delta)
|
|
27
|
+
return '<section class="comparison comparison-empty" aria-label="Run comparison"><div><h2>Run comparison</h2><p>No earlier run to compare yet. Changes will appear here after the next run.</p></div></section>';
|
|
28
|
+
const signed = (value, suffix = '') => `${value > 0 ? '+' : ''}${value}${suffix}`;
|
|
29
|
+
const metric = (label, value) => `<div class="comparison-metric"><span>${label}</span><strong>${escapeHtml(value)}</strong></div>`;
|
|
30
|
+
const newFailures = model.comparison?.newFailures.length ?? 0;
|
|
31
|
+
return `<section class="comparison" aria-label="Changes since previous run"><div class="comparison-head"><div><h2>Changes since previous run</h2><p>Compared with <code>${escapeHtml(delta.previousRunId)}</code> · signed values are net changes</p></div>${newFailures ? '<button id="lb-new-failures" class="button" type="button">View new failures</button>' : ''}</div><div class="comparison-grid">${metric('Pass rate change', delta.passRatePp === null ? '—' : signed(delta.passRatePp, ' pp'))}${metric('Failed change', signed(delta.failed))}${metric('Flaky change', signed(delta.flaky))}${metric('Duration change', delta.durationPct === null ? '—' : signed(delta.durationPct, '%'))}${metric('New failures', String(newFailures))}</div></section>`;
|
|
32
|
+
}
|
|
33
|
+
/** Render a self-contained, offline HTML report. */
|
|
34
|
+
export function renderReport(model, options = {}) {
|
|
35
|
+
const run = model.run;
|
|
36
|
+
const title = options.title ?? run.title ?? 'Playwright Logbook';
|
|
37
|
+
const reportDir = options.reportDir ?? path.posix.join(run.paths.outputDir, 'report');
|
|
38
|
+
const cards = ['total', 'passed', 'failed', 'flaky', 'skipped'].map((kind) => `<button type="button" class="card" data-card-kind="${kind}">${kind[0].toUpperCase()}${kind.slice(1)}<strong>${run.summary[kind]}</strong></button>`).join('');
|
|
39
|
+
const tabButtons = tabs.map((tab) => `<button type="button" role="tab" data-tab="${tab}" aria-controls="tab-${tab}" aria-selected="false">${tab[0].toUpperCase()}${tab.slice(1)}</button>`).join('');
|
|
40
|
+
const panels = tabs.map((tab) => `<section class="panel" id="tab-${tab}" role="tabpanel" hidden></section>`).join('');
|
|
41
|
+
const missing = Array.from({ length: run.expectedShards ?? 1 }, (_, index) => index + 1).filter((number) => !run.receivedShards.includes(number));
|
|
42
|
+
const banner = run.complete ? '' : `<p class="banner">INCOMPLETE run · missing shards ${missing.join(', ')}</p>`;
|
|
43
|
+
const projectName = run.project.name || 'Unnamed project';
|
|
44
|
+
const meta = [run.env.git.branch && `Branch: ${run.env.git.branch}`, run.env.git.commit && `Commit: ${run.env.git.commit.slice(0, 7)}`, run.env.git.prNumber && `PR #${run.env.git.prNumber}`, `Shards: ${run.receivedShards.length}/${run.expectedShards ?? 1}`, `Playwright: ${run.env.playwrightVersion}`, `Workers: ${run.env.workers}`].filter(Boolean).map((item) => `<span>${escapeHtml(String(item))}</span>`).join('');
|
|
45
|
+
const buildUrl = run.env.ci?.buildUrl;
|
|
46
|
+
const buildLink = buildUrl && /^https?:\/\//i.test(buildUrl) && safeHref(buildUrl) ? `<a href="${escapeHtml(buildUrl)}" rel="noopener noreferrer">CI build</a>` : '';
|
|
47
|
+
const footer = `${model.generatedAt ? `Generated ${escapeHtml(model.generatedAt)} · ` : ''}playwright-logbook v${escapeHtml(model.generator?.version ?? '0.1.0')} · Press ? for shortcuts`;
|
|
48
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(title)}</title><style>${buildStyles()}</style></head><body><a class="skip-link" href="#tab-tests">Skip to results</a><div class="topbar"><div class="topbar-inner"><span class="wordmark">▤ logbook</span><span class="topbar-title">${escapeHtml(projectName)}</span><span class="status-chip ${run.status === 'passed' ? 'passed' : 'failed'}">${escapeHtml(run.status.toUpperCase())}</span><div class="topbar-actions"><button id="lb-theme" type="button">Dark mode</button><button id="lb-copy-summary" class="optional" type="button">Copy summary</button><button id="lb-download" class="optional" type="button">Download JSON</button><button id="lb-print" class="optional" type="button">Print</button><button id="lb-help" type="button" aria-label="Help">?</button></div></div></div><main class="container"><header id="lb-header" class="hero">${donut(model)}<div class="hero-main"><p class="eyebrow">Project <strong>${escapeHtml(projectName)}</strong></p><h1>${escapeHtml(title)}</h1><dl class="run-facts"><div><dt>Run ID</dt><dd title="${escapeHtml(run.runId)}">${escapeHtml(run.runId)}</dd></div><div><dt>Started</dt><dd><time datetime="${escapeHtml(run.startedAt)}" title="${escapeHtml(run.startedAt)}">${escapeHtml(run.startedAt)}</time></dd></div><div><dt>Duration</dt><dd>${formatDuration(run.durationMs)}</dd></div></dl><details class="env-disclosure"><summary>Environment details</summary><div class="meta">${meta}${buildLink}</div></details></div></header>${banner}<div id="lb-cards" class="cards">${cards}</div>${comparison(model)}<p id="lb-error" class="error-banner" hidden></p><nav class="tabs" role="tablist" aria-label="Report sections">${tabButtons}</nav>${panels}<footer>${footer}</footer></main><div id="lb-backdrop" class="panel-backdrop" hidden></div><aside id="lb-panel" class="detail-panel" aria-label="Test details" hidden><div class="panel-head"><h2 id="lb-panel-heading" tabindex="-1"></h2><button id="lb-panel-close" type="button" aria-label="Close details">Close</button></div><div id="lb-panel-content"></div></aside><div id="lb-toast" class="toast" role="status" aria-live="polite" hidden></div><dialog id="lb-help-dialog" class="help-dialog"></dialog><dialog id="lb-lightbox" class="lightbox"></dialog><noscript>Enable JavaScript to explore tests and report details.</noscript><script type="application/json" id="lb-data">${safeJson(model)}</script><script type="application/json" id="lb-links">${safeJson(attachmentLinks(model, reportDir))}</script><script>${buildClientScript()}</script></body></html>`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import type { PwConfig, PwError, PwResult, PwTest } from './collect.js';
|
|
3
|
+
import type { DetectEnvOptions } from './env.js';
|
|
4
|
+
import type { LogbookOptions } from './options.js';
|
|
5
|
+
import type { ShardSink } from './store.js';
|
|
6
|
+
import type { HistoryStore } from './store.js';
|
|
7
|
+
interface ReporterSuite {
|
|
8
|
+
allTests(): PwTest[];
|
|
9
|
+
}
|
|
10
|
+
interface FullResult {
|
|
11
|
+
status: 'passed' | 'failed' | 'timedout' | 'interrupted';
|
|
12
|
+
startTime?: Date;
|
|
13
|
+
duration?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ReporterDeps extends Pick<DetectEnvOptions, 'env' | 'exec' | 'clock' | 'random'> {
|
|
16
|
+
cwd?: () => string;
|
|
17
|
+
stderr?: (line: string) => void;
|
|
18
|
+
fs?: Pick<typeof fs, 'mkdir' | 'writeFile' | 'rename'> & Partial<Pick<typeof fs, 'readFile' | 'stat' | 'realpath'>>;
|
|
19
|
+
sink?: ShardSink;
|
|
20
|
+
historyStore?: HistoryStore;
|
|
21
|
+
}
|
|
22
|
+
/** Playwright reporter that writes a durable shard without changing the test result. */
|
|
23
|
+
export declare class LogbookReporter {
|
|
24
|
+
private readonly options;
|
|
25
|
+
private readonly deps;
|
|
26
|
+
private config;
|
|
27
|
+
private suite;
|
|
28
|
+
private startedAt;
|
|
29
|
+
private detected;
|
|
30
|
+
private root;
|
|
31
|
+
private globalErrors;
|
|
32
|
+
private warned;
|
|
33
|
+
private readonly stepsByResult;
|
|
34
|
+
constructor(options?: LogbookOptions, deps?: ReporterDeps);
|
|
35
|
+
printsToStdio(): boolean;
|
|
36
|
+
private write;
|
|
37
|
+
private warning;
|
|
38
|
+
onBegin(config: PwConfig, suite: ReporterSuite): void;
|
|
39
|
+
onError(error: PwError): void;
|
|
40
|
+
onStepEnd(_test: PwTest, result: PwResult, step: {
|
|
41
|
+
title: string;
|
|
42
|
+
category: string;
|
|
43
|
+
duration: number;
|
|
44
|
+
error?: unknown;
|
|
45
|
+
parent?: {
|
|
46
|
+
parent?: unknown;
|
|
47
|
+
} | null;
|
|
48
|
+
}): void;
|
|
49
|
+
private captureImages;
|
|
50
|
+
private saveReport;
|
|
51
|
+
onEnd(result: FullResult): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
export {};
|