@ciach/playwright-private-reporter 0.1.1

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.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -0
  3. package/dist/cjs/bin/generate-report.d.ts +3 -0
  4. package/dist/cjs/bin/generate-report.d.ts.map +1 -0
  5. package/dist/cjs/bin/generate-report.js +6 -0
  6. package/dist/cjs/bin/generate-report.js.map +1 -0
  7. package/dist/cjs/cli/generateReport.d.ts +11 -0
  8. package/dist/cjs/cli/generateReport.d.ts.map +1 -0
  9. package/dist/cjs/cli/generateReport.js +56 -0
  10. package/dist/cjs/cli/generateReport.js.map +1 -0
  11. package/dist/cjs/config/withPrivateReporter.d.ts +4 -0
  12. package/dist/cjs/config/withPrivateReporter.d.ts.map +1 -0
  13. package/dist/cjs/config/withPrivateReporter.js +41 -0
  14. package/dist/cjs/config/withPrivateReporter.js.map +1 -0
  15. package/dist/cjs/history/diffRuns.d.ts +3 -0
  16. package/dist/cjs/history/diffRuns.d.ts.map +1 -0
  17. package/dist/cjs/history/diffRuns.js +16 -0
  18. package/dist/cjs/history/diffRuns.js.map +1 -0
  19. package/dist/cjs/history/loadPrevious.d.ts +4 -0
  20. package/dist/cjs/history/loadPrevious.d.ts.map +1 -0
  21. package/dist/cjs/history/loadPrevious.js +24 -0
  22. package/dist/cjs/history/loadPrevious.js.map +1 -0
  23. package/dist/cjs/index.d.ts +9 -0
  24. package/dist/cjs/index.d.ts.map +1 -0
  25. package/dist/cjs/index.js +21 -0
  26. package/dist/cjs/index.js.map +1 -0
  27. package/dist/cjs/package.json +1 -0
  28. package/dist/cjs/reporter/PrivateReporter.d.ts +16 -0
  29. package/dist/cjs/reporter/PrivateReporter.d.ts.map +1 -0
  30. package/dist/cjs/reporter/PrivateReporter.js +109 -0
  31. package/dist/cjs/reporter/PrivateReporter.js.map +1 -0
  32. package/dist/cjs/reporter/attachments.d.ts +9 -0
  33. package/dist/cjs/reporter/attachments.d.ts.map +1 -0
  34. package/dist/cjs/reporter/attachments.js +15 -0
  35. package/dist/cjs/reporter/attachments.js.map +1 -0
  36. package/dist/cjs/reporter/fingerprint.d.ts +12 -0
  37. package/dist/cjs/reporter/fingerprint.d.ts.map +1 -0
  38. package/dist/cjs/reporter/fingerprint.js +37 -0
  39. package/dist/cjs/reporter/fingerprint.js.map +1 -0
  40. package/dist/cjs/reporter/summary.d.ts +25 -0
  41. package/dist/cjs/reporter/summary.d.ts.map +1 -0
  42. package/dist/cjs/reporter/summary.js +105 -0
  43. package/dist/cjs/reporter/summary.js.map +1 -0
  44. package/dist/cjs/templates/summary.html.d.ts +3 -0
  45. package/dist/cjs/templates/summary.html.d.ts.map +1 -0
  46. package/dist/cjs/templates/summary.html.js +64 -0
  47. package/dist/cjs/templates/summary.html.js.map +1 -0
  48. package/dist/cjs/types/schema.d.ts +96 -0
  49. package/dist/cjs/types/schema.d.ts.map +1 -0
  50. package/dist/cjs/types/schema.js +3 -0
  51. package/dist/cjs/types/schema.js.map +1 -0
  52. package/dist/esm/bin/generate-report.d.ts +3 -0
  53. package/dist/esm/bin/generate-report.d.ts.map +1 -0
  54. package/dist/esm/bin/generate-report.js +4 -0
  55. package/dist/esm/bin/generate-report.js.map +1 -0
  56. package/dist/esm/cli/generateReport.d.ts +11 -0
  57. package/dist/esm/cli/generateReport.d.ts.map +1 -0
  58. package/dist/esm/cli/generateReport.js +54 -0
  59. package/dist/esm/cli/generateReport.js.map +1 -0
  60. package/dist/esm/config/withPrivateReporter.d.ts +4 -0
  61. package/dist/esm/config/withPrivateReporter.d.ts.map +1 -0
  62. package/dist/esm/config/withPrivateReporter.js +38 -0
  63. package/dist/esm/config/withPrivateReporter.js.map +1 -0
  64. package/dist/esm/history/diffRuns.d.ts +3 -0
  65. package/dist/esm/history/diffRuns.d.ts.map +1 -0
  66. package/dist/esm/history/diffRuns.js +13 -0
  67. package/dist/esm/history/diffRuns.js.map +1 -0
  68. package/dist/esm/history/loadPrevious.d.ts +4 -0
  69. package/dist/esm/history/loadPrevious.d.ts.map +1 -0
  70. package/dist/esm/history/loadPrevious.js +20 -0
  71. package/dist/esm/history/loadPrevious.js.map +1 -0
  72. package/dist/esm/index.d.ts +9 -0
  73. package/dist/esm/index.d.ts.map +1 -0
  74. package/dist/esm/index.js +8 -0
  75. package/dist/esm/index.js.map +1 -0
  76. package/dist/esm/package.json +1 -0
  77. package/dist/esm/reporter/PrivateReporter.d.ts +16 -0
  78. package/dist/esm/reporter/PrivateReporter.d.ts.map +1 -0
  79. package/dist/esm/reporter/PrivateReporter.js +105 -0
  80. package/dist/esm/reporter/PrivateReporter.js.map +1 -0
  81. package/dist/esm/reporter/attachments.d.ts +9 -0
  82. package/dist/esm/reporter/attachments.d.ts.map +1 -0
  83. package/dist/esm/reporter/attachments.js +11 -0
  84. package/dist/esm/reporter/attachments.js.map +1 -0
  85. package/dist/esm/reporter/fingerprint.d.ts +12 -0
  86. package/dist/esm/reporter/fingerprint.d.ts.map +1 -0
  87. package/dist/esm/reporter/fingerprint.js +32 -0
  88. package/dist/esm/reporter/fingerprint.js.map +1 -0
  89. package/dist/esm/reporter/summary.d.ts +25 -0
  90. package/dist/esm/reporter/summary.d.ts.map +1 -0
  91. package/dist/esm/reporter/summary.js +99 -0
  92. package/dist/esm/reporter/summary.js.map +1 -0
  93. package/dist/esm/templates/summary.html.d.ts +3 -0
  94. package/dist/esm/templates/summary.html.d.ts.map +1 -0
  95. package/dist/esm/templates/summary.html.js +61 -0
  96. package/dist/esm/templates/summary.html.js.map +1 -0
  97. package/dist/esm/types/schema.d.ts +96 -0
  98. package/dist/esm/types/schema.d.ts.map +1 -0
  99. package/dist/esm/types/schema.js +2 -0
  100. package/dist/esm/types/schema.js.map +1 -0
  101. package/examples/Jenkinsfile +82 -0
  102. package/examples/playwright.config.ts +19 -0
  103. package/package.json +78 -0
  104. package/schemas/failures.schema.json +82 -0
  105. package/schemas/run.schema.json +98 -0
@@ -0,0 +1,16 @@
1
+ import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
2
+ import type { PrivateReporterOptions } from '../types/schema.js';
3
+ export declare class PrivateReporter implements Reporter {
4
+ private readonly options;
5
+ private readonly failures;
6
+ private readonly counts;
7
+ private readonly healthErrors;
8
+ constructor(options: PrivateReporterOptions);
9
+ printsToStdio(): boolean;
10
+ onTestEnd(test: TestCase, result: TestResult): void;
11
+ onEnd(): Promise<void>;
12
+ private recordStatus;
13
+ private guard;
14
+ private guardAsync;
15
+ }
16
+ //# sourceMappingURL=PrivateReporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrivateReporter.d.ts","sourceRoot":"","sources":["../../../src/reporter/PrivateReporter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAKhF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgB;gBAE1B,OAAO,EAAE,sBAAsB;IAI3C,aAAa,IAAI,OAAO;IAIxB,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAmC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBnC,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,KAAK;YAQC,UAAU;CAOzB"}
@@ -0,0 +1,105 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { normalizeAttachments } from './attachments.js';
4
+ import { createFailureFingerprint, extractFirstMeaningfulStack, normalizeErrorMessage } from './fingerprint.js';
5
+ import { buildFailuresSummary, buildRunSummary, createEmptyCounts, summarizeFailures } from './summary.js';
6
+ export class PrivateReporter {
7
+ options;
8
+ failures = [];
9
+ counts = createEmptyCounts();
10
+ healthErrors = [];
11
+ constructor(options) {
12
+ this.options = options;
13
+ }
14
+ printsToStdio() {
15
+ return false;
16
+ }
17
+ onTestEnd(test, result) {
18
+ this.guard(() => {
19
+ this.recordStatus(result.status);
20
+ if (!result.error) {
21
+ return;
22
+ }
23
+ const normalizedMessage = normalizeErrorMessage(result.error.message);
24
+ const firstMeaningfulStack = extractFirstMeaningfulStack(result.error.stack);
25
+ const attachments = normalizeAttachments(result.attachments);
26
+ this.failures.push({
27
+ fingerprint: createFailureFingerprint({
28
+ testFile: test.location.file,
29
+ titlePath: test.titlePath(),
30
+ projectName: test.parent.project?.()?.name,
31
+ normalizedMessage,
32
+ firstMeaningfulStack,
33
+ }),
34
+ title: test.title,
35
+ normalizedMessage,
36
+ firstMeaningfulStack,
37
+ testFile: test.location.file,
38
+ projectName: test.parent.project?.()?.name,
39
+ status: result.status === 'timedOut' ? 'timedOut' : 'failed',
40
+ id: test.id,
41
+ titlePath: test.titlePath(),
42
+ location: `${test.location.file}:${test.location.line}:${test.location.column}`,
43
+ retry: result.retry,
44
+ attachments,
45
+ });
46
+ });
47
+ }
48
+ async onEnd() {
49
+ await this.guardAsync(async () => {
50
+ const outputDir = join(this.options.outputDir ?? 'artifacts', 'internal-report');
51
+ await mkdir(outputDir, { recursive: true });
52
+ const groupedFailures = summarizeFailures(this.failures);
53
+ const runSummary = buildRunSummary({
54
+ options: this.options,
55
+ counts: this.counts,
56
+ groupedFailures,
57
+ });
58
+ const failuresSummary = buildFailuresSummary(this.options, groupedFailures);
59
+ await Promise.all([
60
+ writeFile(join(outputDir, 'run.json'), JSON.stringify(runSummary, null, 2), 'utf8'),
61
+ writeFile(join(outputDir, 'failures.json'), JSON.stringify(failuresSummary, null, 2), 'utf8'),
62
+ writeFile(join(outputDir, 'health.json'), JSON.stringify({ ok: this.healthErrors.length === 0, errors: this.healthErrors }, null, 2), 'utf8'),
63
+ ]);
64
+ });
65
+ }
66
+ recordStatus(status) {
67
+ switch (status) {
68
+ case 'passed':
69
+ this.counts.passed += 1;
70
+ break;
71
+ case 'failed':
72
+ this.counts.failed += 1;
73
+ break;
74
+ case 'skipped':
75
+ this.counts.skipped += 1;
76
+ break;
77
+ case 'timedOut':
78
+ this.counts.timedOut += 1;
79
+ break;
80
+ case 'interrupted':
81
+ this.counts.interrupted += 1;
82
+ break;
83
+ default:
84
+ this.counts.flaky += 1;
85
+ break;
86
+ }
87
+ }
88
+ guard(fn) {
89
+ try {
90
+ fn();
91
+ }
92
+ catch (error) {
93
+ this.healthErrors.push(error instanceof Error ? error.message : String(error));
94
+ }
95
+ }
96
+ async guardAsync(fn) {
97
+ try {
98
+ await fn();
99
+ }
100
+ catch (error) {
101
+ this.healthErrors.push(error instanceof Error ? error.message : String(error));
102
+ }
103
+ }
104
+ }
105
+ //# sourceMappingURL=PrivateReporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrivateReporter.js","sourceRoot":"","sources":["../../../src/reporter/PrivateReporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { AttachmentRecord } from '../types/schema.js';
2
+ export type PlaywrightLikeAttachment = {
3
+ name: string;
4
+ contentType?: string;
5
+ path?: string;
6
+ };
7
+ export declare function normalizeAttachments(attachments: PlaywrightLikeAttachment[] | undefined): AttachmentRecord[];
8
+ export declare function selectExampleAttachments(attachments: AttachmentRecord[], max?: number): AttachmentRecord[];
9
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../../src/reporter/attachments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,wBAAwB,EAAE,GAAG,SAAS,GAClD,gBAAgB,EAAE,CAMpB;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,gBAAgB,EAAE,EAAE,GAAG,SAAI,GAAG,gBAAgB,EAAE,CAErG"}
@@ -0,0 +1,11 @@
1
+ export function normalizeAttachments(attachments) {
2
+ return (attachments ?? []).map((attachment) => ({
3
+ name: attachment.name,
4
+ contentType: attachment.contentType,
5
+ path: attachment.path,
6
+ }));
7
+ }
8
+ export function selectExampleAttachments(attachments, max = 3) {
9
+ return attachments.filter((attachment) => Boolean(attachment.path)).slice(0, max);
10
+ }
11
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/reporter/attachments.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,oBAAoB,CAClC,WAAmD;IAEnD,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,IAAI,EAAE,UAAU,CAAC,IAAI;KACtB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,WAA+B,EAAE,GAAG,GAAG,CAAC;IAC/E,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACpF,CAAC"}
@@ -0,0 +1,12 @@
1
+ export type FingerprintParts = {
2
+ testFile: string;
3
+ titlePath: string[];
4
+ projectName?: string;
5
+ firstMeaningfulStack?: string;
6
+ normalizedMessage: string;
7
+ stepTitle?: string;
8
+ };
9
+ export declare function normalizeErrorMessage(message: string | undefined): string;
10
+ export declare function extractFirstMeaningfulStack(stack: string | undefined): string | undefined;
11
+ export declare function createFailureFingerprint(parts: FingerprintParts): string;
12
+ //# sourceMappingURL=fingerprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../../src/reporter/fingerprint.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAUzE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASzF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAWxE"}
@@ -0,0 +1,32 @@
1
+ import { createHash } from 'node:crypto';
2
+ export function normalizeErrorMessage(message) {
3
+ if (!message) {
4
+ return 'Unknown error';
5
+ }
6
+ return message
7
+ .replace(/\b\d+ms\b/g, '<duration>')
8
+ .replace(/https?:\/\/\S+/g, '<url>')
9
+ .replace(/\/workspace\/[\w\-/\.]+/g, '<workspace-path>')
10
+ .trim();
11
+ }
12
+ export function extractFirstMeaningfulStack(stack) {
13
+ if (!stack) {
14
+ return undefined;
15
+ }
16
+ return stack
17
+ .split('\n')
18
+ .map((line) => line.trim())
19
+ .find((line) => line.length > 0 && !line.startsWith('Error:'));
20
+ }
21
+ export function createFailureFingerprint(parts) {
22
+ const payload = [
23
+ parts.testFile,
24
+ parts.titlePath.join(' › '),
25
+ parts.projectName ?? '',
26
+ parts.firstMeaningfulStack ?? '',
27
+ parts.normalizedMessage,
28
+ parts.stepTitle ?? '',
29
+ ].join('||');
30
+ return createHash('sha256').update(payload).digest('hex').slice(0, 16);
31
+ }
32
+ //# sourceMappingURL=fingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../../src/reporter/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWzC,MAAM,UAAU,qBAAqB,CAAC,OAA2B;IAC/D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,OAAO,OAAO;SACX,OAAO,CAAC,YAAY,EAAE,YAAY,CAAC;SACnC,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC;SACnC,OAAO,CAAC,0BAA0B,EAAE,kBAAkB,CAAC;SACvD,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,KAAyB;IACnE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,KAAK;SACT,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAuB;IAC9D,MAAM,OAAO,GAAG;QACd,KAAK,CAAC,QAAQ;QACd,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAC3B,KAAK,CAAC,WAAW,IAAI,EAAE;QACvB,KAAK,CAAC,oBAAoB,IAAI,EAAE;QAChC,KAAK,CAAC,iBAAiB;QACvB,KAAK,CAAC,SAAS,IAAI,EAAE;KACtB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { AttachmentRecord, FailureGroup, FailuresSummary, HistoryDiff, PrivateReporterOptions, RunCounts, RunSummary } from '../types/schema.js';
2
+ export type FailureInput = {
3
+ fingerprint: string;
4
+ title: string;
5
+ normalizedMessage: string;
6
+ firstMeaningfulStack?: string;
7
+ testFile: string;
8
+ projectName?: string;
9
+ status: FailureGroup['status'];
10
+ id: string;
11
+ titlePath: string[];
12
+ location?: string;
13
+ retry: number;
14
+ attachments: AttachmentRecord[];
15
+ };
16
+ export declare function createEmptyCounts(): RunCounts;
17
+ export declare function summarizeFailures(inputs: FailureInput[]): FailureGroup[];
18
+ export declare function buildRunSummary(params: {
19
+ options: PrivateReporterOptions;
20
+ counts: RunCounts;
21
+ groupedFailures: FailureGroup[];
22
+ historyDiff?: HistoryDiff;
23
+ }): RunSummary;
24
+ export declare function buildFailuresSummary(options: PrivateReporterOptions, groups: FailureGroup[]): FailuresSummary;
25
+ //# sourceMappingURL=summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../../../src/reporter/summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,WAAW,EACX,sBAAsB,EACtB,SAAS,EACT,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC,CAAC;AAEF,wBAAgB,iBAAiB,IAAI,SAAS,CAS7C;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CA0CxE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,OAAO,EAAE,sBAAsB,CAAC;IAChC,MAAM,EAAE,SAAS,CAAC;IAClB,eAAe,EAAE,YAAY,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GAAG,UAAU,CAwCb;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,eAAe,CAO7G"}
@@ -0,0 +1,99 @@
1
+ export function createEmptyCounts() {
2
+ return {
3
+ passed: 0,
4
+ failed: 0,
5
+ skipped: 0,
6
+ flaky: 0,
7
+ timedOut: 0,
8
+ interrupted: 0,
9
+ };
10
+ }
11
+ export function summarizeFailures(inputs) {
12
+ const grouped = new Map();
13
+ for (const failure of inputs) {
14
+ const existing = grouped.get(failure.fingerprint);
15
+ if (existing) {
16
+ existing.countInRun += 1;
17
+ existing.tests.push({
18
+ id: failure.id,
19
+ titlePath: failure.titlePath,
20
+ location: failure.location,
21
+ retry: failure.retry,
22
+ status: failure.status,
23
+ attachments: failure.attachments,
24
+ });
25
+ continue;
26
+ }
27
+ grouped.set(failure.fingerprint, {
28
+ fingerprint: failure.fingerprint,
29
+ title: failure.title,
30
+ normalizedMessage: failure.normalizedMessage,
31
+ firstMeaningfulStack: failure.firstMeaningfulStack,
32
+ testFile: failure.testFile,
33
+ projectName: failure.projectName,
34
+ status: failure.status,
35
+ countInRun: 1,
36
+ tests: [
37
+ {
38
+ id: failure.id,
39
+ titlePath: failure.titlePath,
40
+ location: failure.location,
41
+ retry: failure.retry,
42
+ status: failure.status,
43
+ attachments: failure.attachments,
44
+ },
45
+ ],
46
+ exampleAttachments: failure.attachments.slice(0, 3),
47
+ });
48
+ }
49
+ return [...grouped.values()].sort((left, right) => right.countInRun - left.countInRun);
50
+ }
51
+ export function buildRunSummary(params) {
52
+ const buildUrlEnv = params.options.buildUrlEnv ?? 'BUILD_URL';
53
+ const buildIdEnv = params.options.buildIdEnv ?? 'BUILD_TAG';
54
+ const branchEnv = params.options.branchEnv ?? 'BRANCH_NAME';
55
+ const commitEnv = params.options.commitEnv ?? 'GIT_COMMIT';
56
+ const history = params.historyDiff;
57
+ const hasFailures = params.counts.failed + params.counts.timedOut + params.counts.interrupted > 0;
58
+ return {
59
+ schemaVersion: '1.0',
60
+ projectName: params.options.projectName,
61
+ generatedAt: new Date().toISOString(),
62
+ run: {
63
+ status: hasFailures ? 'failed' : 'passed',
64
+ buildId: process.env[buildIdEnv],
65
+ buildUrl: process.env[buildUrlEnv],
66
+ branch: process.env[branchEnv],
67
+ commit: process.env[commitEnv],
68
+ },
69
+ counts: params.counts,
70
+ projects: [],
71
+ history: {
72
+ enabled: Boolean(params.options.enableHistoryDiff),
73
+ previousBuildId: undefined,
74
+ newFailures: history?.newFailures.length ?? 0,
75
+ fixedFailures: history?.fixedFailures.length ?? 0,
76
+ stillFailing: history?.stillFailing.length ?? 0,
77
+ },
78
+ artifacts: {
79
+ blobReportDir: 'blob-report',
80
+ playwrightHtmlDir: 'playwright-report',
81
+ testResultsDir: 'test-results',
82
+ junitPath: 'junit/results.xml',
83
+ internalReportDir: 'internal-report',
84
+ },
85
+ failures: {
86
+ totalGroups: params.groupedFailures.length,
87
+ topFingerprint: params.groupedFailures[0]?.fingerprint,
88
+ },
89
+ };
90
+ }
91
+ export function buildFailuresSummary(options, groups) {
92
+ return {
93
+ schemaVersion: '1.0',
94
+ projectName: options.projectName,
95
+ generatedAt: new Date().toISOString(),
96
+ groups,
97
+ };
98
+ }
99
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../../../src/reporter/summary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { FailuresSummary, RunSummary } from '../types/schema.js';
2
+ export declare function renderSummaryHtml(run: RunSummary, failures: FailuresSummary): string;
3
+ //# sourceMappingURL=summary.html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.html.d.ts","sourceRoot":"","sources":["../../../src/templates/summary.html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAWtE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,eAAe,GAAG,MAAM,CAsDpF"}
@@ -0,0 +1,61 @@
1
+ function escapeHtml(value) {
2
+ return (value ?? '')
3
+ .replaceAll('&', '&amp;')
4
+ .replaceAll('<', '&lt;')
5
+ .replaceAll('>', '&gt;')
6
+ .replaceAll('"', '&quot;')
7
+ .replaceAll("'", '&#39;');
8
+ }
9
+ export function renderSummaryHtml(run, failures) {
10
+ const failureItems = failures.groups
11
+ .slice(0, 10)
12
+ .map((group) => `
13
+ <li>
14
+ <strong>${escapeHtml(group.title)}</strong>
15
+ <div>Fingerprint: <code>${escapeHtml(group.fingerprint)}</code></div>
16
+ <div>Occurrences: ${group.countInRun}</div>
17
+ <div>${escapeHtml(group.normalizedMessage)}</div>
18
+ </li>`)
19
+ .join('');
20
+ return `<!DOCTYPE html>
21
+ <html lang="en">
22
+ <head>
23
+ <meta charset="utf-8" />
24
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
25
+ <title>${escapeHtml(run.projectName)} test summary</title>
26
+ <style>
27
+ body { font-family: Arial, sans-serif; margin: 2rem; color: #1f2937; }
28
+ .grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; }
29
+ .card { border: 1px solid #d1d5db; border-radius: 8px; padding: 1rem; background: #fff; }
30
+ code { background: #f3f4f6; padding: 0.1rem 0.3rem; border-radius: 4px; }
31
+ ul { padding-left: 1.25rem; }
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <h1>${escapeHtml(run.projectName)} Playwright Summary</h1>
36
+ <p>Status: <strong>${escapeHtml(run.run.status)}</strong></p>
37
+ <div class="grid">
38
+ <div class="card"><div>Passed</div><strong>${run.counts.passed}</strong></div>
39
+ <div class="card"><div>Failed</div><strong>${run.counts.failed}</strong></div>
40
+ <div class="card"><div>Skipped</div><strong>${run.counts.skipped}</strong></div>
41
+ <div class="card"><div>Flaky</div><strong>${run.counts.flaky}</strong></div>
42
+ </div>
43
+ <h2>Build metadata</h2>
44
+ <ul>
45
+ <li>Build ID: ${escapeHtml(run.run.buildId)}</li>
46
+ <li>Branch: ${escapeHtml(run.run.branch)}</li>
47
+ <li>Commit: ${escapeHtml(run.run.commit)}</li>
48
+ <li>Build URL: ${run.run.buildUrl ? `<a href="${escapeHtml(run.run.buildUrl)}">${escapeHtml(run.run.buildUrl)}</a>` : 'n/a'}</li>
49
+ </ul>
50
+ <h2>Failure groups</h2>
51
+ <ul>${failureItems || '<li>No failing groups 🎉</li>'}</ul>
52
+ <h2>Artifacts</h2>
53
+ <ul>
54
+ <li><a href="../playwright-report/index.html">Merged Playwright HTML report</a></li>
55
+ <li><a href="../junit/results.xml">JUnit XML</a></li>
56
+ <li><a href="../test-results/">Test results attachments</a></li>
57
+ </ul>
58
+ </body>
59
+ </html>`;
60
+ }
61
+ //# sourceMappingURL=summary.html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.html.js","sourceRoot":"","sources":["../../../src/templates/summary.html.ts"],"names":[],"mappings":"AAEA,SAAS,UAAU,CAAC,KAAyB;IAC3C,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;SACjB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAe,EAAE,QAAyB;IAC1E,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM;SACjC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CAAC;;oBAEG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;oCACP,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC;8BACnC,KAAK,CAAC,UAAU;iBAC7B,UAAU,CAAC,KAAK,CAAC,iBAAiB,CAAC;cACtC,CACT;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;;;aAKI,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC;;;;;;;;;;UAU9B,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC;yBACZ,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;;mDAEA,GAAG,CAAC,MAAM,CAAC,MAAM;mDACjB,GAAG,CAAC,MAAM,CAAC,MAAM;oDAChB,GAAG,CAAC,MAAM,CAAC,OAAO;kDACpB,GAAG,CAAC,MAAM,CAAC,KAAK;;;;sBAI5C,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;oBAC7B,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;oBAC1B,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;uBACvB,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;;;UAGvH,YAAY,IAAI,+BAA+B;;;;;;;;QAQjD,CAAC;AACT,CAAC"}
@@ -0,0 +1,96 @@
1
+ export type PrivateReporterOptions = {
2
+ projectName: string;
3
+ outputDir?: string;
4
+ summaryTitle?: string;
5
+ enableHistoryDiff?: boolean;
6
+ ci?: boolean;
7
+ buildUrlEnv?: string;
8
+ buildIdEnv?: string;
9
+ branchEnv?: string;
10
+ commitEnv?: string;
11
+ };
12
+ export type AttachmentRecord = {
13
+ name: string;
14
+ contentType?: string;
15
+ path?: string;
16
+ };
17
+ export type FailureTestRecord = {
18
+ id: string;
19
+ titlePath: string[];
20
+ location?: string;
21
+ retry: number;
22
+ status: 'failed' | 'timedOut' | 'interrupted' | 'flaky';
23
+ attachments: AttachmentRecord[];
24
+ };
25
+ export type FailureGroup = {
26
+ fingerprint: string;
27
+ title: string;
28
+ normalizedMessage: string;
29
+ firstMeaningfulStack?: string;
30
+ testFile: string;
31
+ projectName?: string;
32
+ status: FailureTestRecord['status'];
33
+ countInRun: number;
34
+ tests: FailureTestRecord[];
35
+ exampleAttachments: AttachmentRecord[];
36
+ history?: {
37
+ firstSeen?: string;
38
+ lastSeen?: string;
39
+ state: 'new' | 'still-failing' | 'fixed' | 'unknown';
40
+ };
41
+ };
42
+ export type RunCounts = {
43
+ passed: number;
44
+ failed: number;
45
+ skipped: number;
46
+ flaky: number;
47
+ timedOut: number;
48
+ interrupted: number;
49
+ };
50
+ export type RunSummary = {
51
+ schemaVersion: '1.0';
52
+ projectName: string;
53
+ generatedAt: string;
54
+ run: {
55
+ status: 'passed' | 'failed' | 'partial';
56
+ buildId?: string;
57
+ buildUrl?: string;
58
+ branch?: string;
59
+ commit?: string;
60
+ };
61
+ counts: RunCounts;
62
+ projects: Array<{
63
+ name: string;
64
+ counts: RunCounts;
65
+ }>;
66
+ history: {
67
+ enabled: boolean;
68
+ previousBuildId?: string;
69
+ newFailures: number;
70
+ fixedFailures: number;
71
+ stillFailing: number;
72
+ };
73
+ artifacts: {
74
+ blobReportDir: string;
75
+ playwrightHtmlDir: string;
76
+ testResultsDir: string;
77
+ junitPath: string;
78
+ internalReportDir: string;
79
+ };
80
+ failures: {
81
+ totalGroups: number;
82
+ topFingerprint?: string;
83
+ };
84
+ };
85
+ export type FailuresSummary = {
86
+ schemaVersion: '1.0';
87
+ projectName: string;
88
+ generatedAt: string;
89
+ groups: FailureGroup[];
90
+ };
91
+ export type HistoryDiff = {
92
+ newFailures: FailureGroup[];
93
+ fixedFailures: FailureGroup[];
94
+ stillFailing: FailureGroup[];
95
+ };
96
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/types/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/types/schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,82 @@
1
+ pipeline {
2
+ agent any
3
+
4
+ options {
5
+ timestamps()
6
+ ansiColor('xterm')
7
+ }
8
+
9
+ environment {
10
+ CI = 'true'
11
+ PW_OUTPUT_DIR = 'artifacts'
12
+ }
13
+
14
+ stages {
15
+ stage('Checkout') {
16
+ steps {
17
+ checkout scm
18
+ }
19
+ }
20
+
21
+ stage('Install') {
22
+ steps {
23
+ sh 'npm ci'
24
+ sh 'npx playwright install --with-deps || true'
25
+ }
26
+ }
27
+
28
+ stage('Test') {
29
+ steps {
30
+ catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE') {
31
+ sh 'npx playwright test'
32
+ }
33
+ }
34
+ post {
35
+ always {
36
+ sh '''
37
+ mkdir -p all-blob-reports
38
+ find . -type f -path "*/blob-report/*.zip" -exec cp {} all-blob-reports/ \\; || true
39
+ '''
40
+ }
41
+ }
42
+ }
43
+
44
+ stage('Merge report') {
45
+ steps {
46
+ sh 'test -d all-blob-reports && npx playwright merge-reports --reporter html ./all-blob-reports'
47
+ }
48
+ }
49
+
50
+ stage('Generate summary') {
51
+ steps {
52
+ sh 'npx playwright-private-reporter-generate'
53
+ }
54
+ }
55
+ }
56
+
57
+ post {
58
+ always {
59
+ junit testResults: 'junit/**/*.xml', allowEmptyResults: true
60
+
61
+ publishHTML(target: [
62
+ allowMissing: true,
63
+ alwaysLinkToLastBuild: true,
64
+ keepAll: true,
65
+ reportDir: 'artifacts/internal-report',
66
+ reportFiles: 'summary.html',
67
+ reportName: 'Playwright Summary'
68
+ ])
69
+
70
+ publishHTML(target: [
71
+ allowMissing: true,
72
+ alwaysLinkToLastBuild: true,
73
+ keepAll: true,
74
+ reportDir: 'playwright-report',
75
+ reportFiles: 'index.html',
76
+ reportName: 'Playwright HTML Report'
77
+ ])
78
+
79
+ archiveArtifacts artifacts: 'playwright-report/**,artifacts/internal-report/**,artifacts/test-results/**,all-blob-reports/**,junit/**/*.xml', fingerprint: true, allowEmptyArchive: true
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from '@playwright/test';
2
+ import { withPrivateReporter } from '@ciach/playwright-private-reporter';
3
+
4
+ export default withPrivateReporter(
5
+ defineConfig({
6
+ testDir: './tests',
7
+ fullyParallel: true,
8
+ retries: process.env.CI ? 1 : 0,
9
+ use: {
10
+ baseURL: process.env.BASE_URL,
11
+ },
12
+ }),
13
+ {
14
+ projectName: 'payments-ui',
15
+ outputDir: 'artifacts',
16
+ summaryTitle: 'Payments UI Playwright Summary',
17
+ enableHistoryDiff: true,
18
+ },
19
+ );