@piwitests/reporter 0.11.0 → 0.13.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.
Files changed (77) hide show
  1. package/dist/global-setup-module.d.ts +2 -1
  2. package/dist/global-setup-module.js +432 -3
  3. package/dist/index.d.ts +204 -8
  4. package/dist/index.js +4724 -23
  5. package/dist/internal/capture/attachments.d.ts +9 -3
  6. package/dist/internal/capture/attachments.js +46 -20
  7. package/dist/internal/capture/capture-fixtures.d.ts +110 -7
  8. package/dist/internal/capture/capture-fixtures.js +2227 -639
  9. package/dist/internal/capture/inspect-on-failure.d.ts +50 -0
  10. package/dist/internal/capture/inspect-on-failure.js +66 -0
  11. package/dist/internal/capture/locator-healing.d.ts +33 -131
  12. package/dist/internal/capture/locator-healing.js +602 -711
  13. package/dist/internal/capture/pick-on-failure.d.ts +198 -0
  14. package/dist/internal/capture/pick-on-failure.js +1203 -0
  15. package/package.json +9 -4
  16. package/dist/internal/collect/error-text.d.ts +0 -18
  17. package/dist/internal/collect/error-text.js +0 -79
  18. package/dist/internal/collect/metadata-collector.d.ts +0 -32
  19. package/dist/internal/collect/metadata-collector.js +0 -246
  20. package/dist/internal/collect/skip-classify.d.ts +0 -27
  21. package/dist/internal/collect/skip-classify.js +0 -40
  22. package/dist/internal/collect/step-analyzer.d.ts +0 -103
  23. package/dist/internal/collect/step-analyzer.js +0 -221
  24. package/dist/internal/config/env.d.ts +0 -45
  25. package/dist/internal/config/env.js +0 -153
  26. package/dist/internal/files/compression.d.ts +0 -5
  27. package/dist/internal/files/compression.js +0 -69
  28. package/dist/internal/files/file-handler.d.ts +0 -38
  29. package/dist/internal/files/file-handler.js +0 -198
  30. package/dist/internal/streaming/crash-recovery.d.ts +0 -23
  31. package/dist/internal/streaming/crash-recovery.js +0 -106
  32. package/dist/internal/streaming/stream-buffer.d.ts +0 -17
  33. package/dist/internal/streaming/stream-buffer.js +0 -102
  34. package/dist/internal/streaming/stream-manager.d.ts +0 -88
  35. package/dist/internal/streaming/stream-manager.js +0 -395
  36. package/dist/internal/submit/run-submitter.d.ts +0 -67
  37. package/dist/internal/submit/run-submitter.js +0 -190
  38. package/dist/internal/submit/serializer.d.ts +0 -45
  39. package/dist/internal/submit/serializer.js +0 -107
  40. package/dist/internal/submit/uploader.d.ts +0 -89
  41. package/dist/internal/submit/uploader.js +0 -226
  42. package/dist/internal/support/ci.d.ts +0 -2
  43. package/dist/internal/support/ci.js +0 -32
  44. package/dist/internal/support/cli-filters.d.ts +0 -1
  45. package/dist/internal/support/cli-filters.js +0 -51
  46. package/dist/internal/support/errors.d.ts +0 -8
  47. package/dist/internal/support/errors.js +0 -15
  48. package/dist/internal/support/instance-id.d.ts +0 -4
  49. package/dist/internal/support/instance-id.js +0 -48
  50. package/dist/internal/support/limiter.d.ts +0 -2
  51. package/dist/internal/support/limiter.js +0 -27
  52. package/dist/internal/support/logger.d.ts +0 -26
  53. package/dist/internal/support/logger.js +0 -43
  54. package/dist/internal/support/reporter-version.d.ts +0 -2
  55. package/dist/internal/support/reporter-version.js +0 -54
  56. package/dist/internal/support/setup-file.d.ts +0 -13
  57. package/dist/internal/support/setup-file.js +0 -61
  58. package/dist/internal/support/source-snippet.d.ts +0 -12
  59. package/dist/internal/support/source-snippet.js +0 -97
  60. package/dist/internal/support/worker-index.d.ts +0 -7
  61. package/dist/internal/support/worker-index.js +0 -14
  62. package/dist/internal/transport/http-client.d.ts +0 -52
  63. package/dist/internal/transport/http-client.js +0 -201
  64. package/dist/public/config-wrapper.d.ts +0 -21
  65. package/dist/public/config-wrapper.js +0 -64
  66. package/dist/public/global-setup.d.ts +0 -13
  67. package/dist/public/global-setup.js +0 -146
  68. package/dist/public/options.d.ts +0 -78
  69. package/dist/public/options.js +0 -2
  70. package/dist/public/reporter.d.ts +0 -68
  71. package/dist/public/reporter.js +0 -376
  72. package/dist/types/collected.d.ts +0 -96
  73. package/dist/types/collected.js +0 -10
  74. package/dist/types/wire.d.ts +0 -174
  75. package/dist/types/wire.js +0 -14
  76. package/dist/types.d.ts +0 -11
  77. package/dist/types.js +0 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/reporter",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Playwright reporter for sending test results to Piwi Dashboard",
5
5
  "url": "https://github.com/PiwiTests/platform",
6
6
  "homepage": "https://piwitests.github.io",
@@ -35,8 +35,9 @@
35
35
  "provenance": true
36
36
  },
37
37
  "scripts": {
38
- "reporter:build": "tsc",
39
- "reporter:dev": "tsc --watch",
38
+ "reporter:build": "tsup",
39
+ "reporter:dev": "tsup --watch",
40
+ "reporter:typecheck": "tsc --noEmit",
40
41
  "reporter:format": "oxfmt --config oxfmt.config.mts --write \"src/\"",
41
42
  "reporter:format:check": "oxfmt --config oxfmt.config.mts --check \"src/\"",
42
43
  "reporter:lint": "oxlint --config oxlint.config.mts .",
@@ -52,9 +53,13 @@
52
53
  "dist/"
53
54
  ],
54
55
  "peerDependencies": {
55
- "@playwright/test": "^1.40.0"
56
+ "@playwright/test": "^1.61.1"
56
57
  },
57
58
  "dependencies": {
58
59
  "form-data": "^4.0.0"
60
+ },
61
+ "devDependencies": {
62
+ "@piwitests/core": "*",
63
+ "tsup": "^8.5.0"
59
64
  }
60
65
  }
@@ -1,18 +0,0 @@
1
- import type { TestResult } from '@playwright/test/reporter';
2
- /**
3
- * Build the stored error text for a test result.
4
- *
5
- * Playwright's `result.error` carries the primary failure but for a **timeout**
6
- * it is only the bare `Test timeout of 30000ms exceeded.` — the interrupted
7
- * action's actual error (with its call log — the single most diagnostic fact)
8
- * lives in `result.errors[1..]`. Concatenating all errors (deduped by message)
9
- * ensures the server sees the call log so it can extract the locator, fingerprint
10
- * correctly, and feed the model the right facts.
11
- *
12
- * A synthetic `at file:line:col` frame from `error.location` is appended when
13
- * no stack frame is present, enabling locator-healing to find the pre-captured
14
- * snapshot for that call site. The frame sits after the message, where
15
- * `extractMessageHead` trims it before fingerprinting — so clustering is
16
- * unaffected.
17
- */
18
- export declare function buildErrorText(result: TestResult): string | null;
@@ -1,79 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.buildErrorText = buildErrorText;
37
- const path = __importStar(require("node:path"));
38
- /**
39
- * Build the stored error text for a test result.
40
- *
41
- * Playwright's `result.error` carries the primary failure but for a **timeout**
42
- * it is only the bare `Test timeout of 30000ms exceeded.` — the interrupted
43
- * action's actual error (with its call log — the single most diagnostic fact)
44
- * lives in `result.errors[1..]`. Concatenating all errors (deduped by message)
45
- * ensures the server sees the call log so it can extract the locator, fingerprint
46
- * correctly, and feed the model the right facts.
47
- *
48
- * A synthetic `at file:line:col` frame from `error.location` is appended when
49
- * no stack frame is present, enabling locator-healing to find the pre-captured
50
- * snapshot for that call site. The frame sits after the message, where
51
- * `extractMessageHead` trims it before fingerprinting — so clustering is
52
- * unaffected.
53
- */
54
- function buildErrorText(result) {
55
- if (!result.errors || result.errors.length === 0)
56
- return null;
57
- const seen = new Set();
58
- const parts = [];
59
- for (const err of result.errors) {
60
- const msg = err.message ?? '';
61
- if (!msg)
62
- continue;
63
- const dedupeKey = msg.trim().slice(0, 200);
64
- if (seen.has(dedupeKey))
65
- continue;
66
- seen.add(dedupeKey);
67
- parts.push(msg);
68
- }
69
- let text = parts.join('\n---\n');
70
- if (!text)
71
- return null;
72
- const primary = result.error;
73
- const loc = primary?.location;
74
- if (loc?.file && !/\n\s+at\s/.test(text)) {
75
- const rel = path.relative(process.cwd(), loc.file).split(path.sep).join('/');
76
- text += `\n at ${rel}:${loc.line}:${loc.column}`;
77
- }
78
- return text;
79
- }
@@ -1,32 +0,0 @@
1
- import type { FullConfig, Suite, TestCase } from '@playwright/test/reporter';
2
- import { Logger } from '../support/logger.js';
3
- import type { SuiteConfigEntry } from '../../types.js';
4
- /**
5
- * Collects CI/CD environment metadata, SCM (git) information, and Playwright
6
- * config metadata to attach to each test run submission.
7
- *
8
- * Also owns all reach-through access to Playwright-internal suite fields
9
- * (`_parallelMode`, `_annotations`, `project()`) so the surface area that
10
- * breaks when Playwright renames those internals is guarded behind one class.
11
- */
12
- export declare class MetadataCollector {
13
- private readonly logger;
14
- constructor(logger?: Logger);
15
- /** Collect all available metadata from the environment, config, and suite */
16
- collect(config: FullConfig, suite: Suite, options: any): Record<string, unknown>;
17
- /** Walk the test's parent hierarchy to extract the resolved browser/project configuration */
18
- getBrowserConfig(test: TestCase): Record<string, any> | null;
19
- /**
20
- * Walk the test's parent `describe` suites to extract the suite path (titles)
21
- * and per-level config (parallel mode + annotations). Reaches into Playwright
22
- * suite internals (`_parallelMode`, `_annotations`) — kept here next to
23
- * `getBrowserConfig` so all such access is guarded behind one class.
24
- */
25
- getSuiteInfo(test: TestCase): {
26
- suitePath: string[];
27
- suiteConfig: SuiteConfigEntry[];
28
- };
29
- private collectScmInfo;
30
- private collectCiInfo;
31
- private extractPlaywrightConfigMetadata;
32
- }
@@ -1,246 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MetadataCollector = void 0;
4
- const node_child_process_1 = require("node:child_process");
5
- const errors_js_1 = require("../support/errors.js");
6
- const logger_js_1 = require("../support/logger.js");
7
- /**
8
- * Collects CI/CD environment metadata, SCM (git) information, and Playwright
9
- * config metadata to attach to each test run submission.
10
- *
11
- * Also owns all reach-through access to Playwright-internal suite fields
12
- * (`_parallelMode`, `_annotations`, `project()`) so the surface area that
13
- * breaks when Playwright renames those internals is guarded behind one class.
14
- */
15
- class MetadataCollector {
16
- constructor(logger = new logger_js_1.Logger()) {
17
- this.logger = logger;
18
- }
19
- /** Collect all available metadata from the environment, config, and suite */
20
- collect(config, suite, options) {
21
- const metadata = {};
22
- if (options.projectDescription)
23
- metadata.projectDescription = options.projectDescription;
24
- if (options.relatedIssue)
25
- metadata.relatedIssue = options.relatedIssue;
26
- if (options.ciInfo)
27
- metadata.ciInfo = options.ciInfo;
28
- if (options.tags && Array.isArray(options.tags))
29
- metadata.tags = options.tags;
30
- if (options.customData)
31
- metadata.customData = options.customData;
32
- if (options.collectScmInfo) {
33
- const scm = this.collectScmInfo(options);
34
- if (scm)
35
- metadata.scm = scm;
36
- }
37
- if (options.collectCiInfo) {
38
- const ci = this.collectCiInfo();
39
- if (ci)
40
- metadata.ci = ci;
41
- }
42
- const htmlMeta = this.extractPlaywrightConfigMetadata(config);
43
- if (htmlMeta && Object.keys(htmlMeta).length > 0)
44
- metadata.htmlReport = htmlMeta;
45
- if (config.metadata)
46
- metadata.playwrightConfig = config.metadata;
47
- if (suite?.allTests) {
48
- const all = suite.allTests();
49
- if (all.length > 0) {
50
- const first = all[0];
51
- if (first?.parent) {
52
- const proj = first.parent.project();
53
- if (proj?.metadata)
54
- metadata.playwrightProject = proj.metadata;
55
- }
56
- }
57
- }
58
- return metadata;
59
- }
60
- /** Walk the test's parent hierarchy to extract the resolved browser/project configuration */
61
- getBrowserConfig(test) {
62
- try {
63
- let suite = test.parent;
64
- let depth = 0;
65
- while (suite && depth < 20) {
66
- depth++;
67
- const project = suite.project?.();
68
- if (project) {
69
- const use = project.use ?? {};
70
- const config = { projectName: project.name };
71
- if (use.browserName)
72
- config.browserName = use.browserName;
73
- if (use.channel)
74
- config.channel = use.channel;
75
- if (use.viewport)
76
- config.viewport = { width: use.viewport.width, height: use.viewport.height };
77
- if (use.deviceScaleFactor != null)
78
- config.deviceScaleFactor = use.deviceScaleFactor;
79
- if (use.isMobile != null)
80
- config.isMobile = use.isMobile;
81
- if (use.hasTouch != null)
82
- config.hasTouch = use.hasTouch;
83
- if (use.locale)
84
- config.locale = use.locale;
85
- if (use.timezoneId)
86
- config.timezoneId = use.timezoneId;
87
- if (use.geolocation) {
88
- config.geolocation = {
89
- longitude: use.geolocation.longitude,
90
- latitude: use.geolocation.latitude,
91
- ...(use.geolocation.accuracy != null && { accuracy: use.geolocation.accuracy }),
92
- };
93
- }
94
- if (use.colorScheme)
95
- config.colorScheme = use.colorScheme;
96
- if (use.reducedMotion)
97
- config.reducedMotion = use.reducedMotion;
98
- if (use.forcedColors)
99
- config.forcedColors = use.forcedColors;
100
- if (use.offline)
101
- config.offline = use.offline;
102
- if (use.bypassCSP)
103
- config.bypassCSP = use.bypassCSP;
104
- if (use.javaScriptEnabled === false)
105
- config.javaScriptEnabled = false;
106
- if (use.serviceWorkers)
107
- config.serviceWorkers = use.serviceWorkers;
108
- if (use.userAgent)
109
- config.userAgent = use.userAgent;
110
- return config;
111
- }
112
- suite = suite.parent;
113
- }
114
- return null;
115
- }
116
- catch {
117
- return null;
118
- }
119
- }
120
- /**
121
- * Walk the test's parent `describe` suites to extract the suite path (titles)
122
- * and per-level config (parallel mode + annotations). Reaches into Playwright
123
- * suite internals (`_parallelMode`, `_annotations`) — kept here next to
124
- * `getBrowserConfig` so all such access is guarded behind one class.
125
- */
126
- getSuiteInfo(test) {
127
- const suitePath = [];
128
- const suiteConfig = [];
129
- const suites = [];
130
- let suite = test.parent;
131
- while (suite && suite.type === 'describe') {
132
- suites.unshift(suite);
133
- suite = suite.parent;
134
- }
135
- for (const s of suites) {
136
- if (!s.title)
137
- continue;
138
- suitePath.push(s.title);
139
- const rawMode = s._parallelMode;
140
- const mode = rawMode === 'parallel' ? 'parallel' : rawMode === 'serial' ? 'serial' : 'default';
141
- const annotations = s._annotations ?? [];
142
- suiteConfig.push({ mode, annotations });
143
- }
144
- return { suitePath, suiteConfig };
145
- }
146
- collectScmInfo(_options) {
147
- const scm = {};
148
- try {
149
- const execOpts = { encoding: 'utf8', timeout: 5000, maxBuffer: 1024 * 1024 };
150
- scm.commit = (0, node_child_process_1.execSync)('git rev-parse HEAD', execOpts).trim();
151
- scm.branch = (0, node_child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', execOpts).trim();
152
- scm.author = (0, node_child_process_1.execSync)('git log -1 --pretty=format:"%an"', execOpts).trim();
153
- scm.commitMessage = (0, node_child_process_1.execSync)('git log -1 --pretty=format:"%s"', execOpts).trim();
154
- try {
155
- scm.remoteUrl = (0, node_child_process_1.execSync)('git config --get remote.origin.url', execOpts).trim();
156
- }
157
- catch {
158
- /* optional */
159
- }
160
- }
161
- catch (error) {
162
- this.logger.debug(`Git info not available: ${(0, errors_js_1.errorMessage)(error)}`);
163
- }
164
- return Object.keys(scm).length > 0 ? scm : undefined;
165
- }
166
- collectCiInfo() {
167
- // Env vars are `string | undefined`; undefined values are dropped on JSON
168
- // serialization, so collecting them directly preserves the prior behavior.
169
- const ci = {};
170
- const env = process.env;
171
- if (env.JENKINS_URL) {
172
- ci.provider = 'Jenkins';
173
- ci.buildNumber = env.BUILD_NUMBER;
174
- ci.buildUrl = env.BUILD_URL;
175
- ci.jobName = env.JOB_NAME;
176
- }
177
- else if (env.GITHUB_ACTIONS) {
178
- ci.provider = 'GitHub Actions';
179
- ci.runId = env.GITHUB_RUN_ID;
180
- ci.runNumber = env.GITHUB_RUN_NUMBER;
181
- ci.workflow = env.GITHUB_WORKFLOW;
182
- ci.actor = env.GITHUB_ACTOR;
183
- ci.repository = env.GITHUB_REPOSITORY;
184
- ci.ref = env.GITHUB_REF;
185
- ci.sha = env.GITHUB_SHA;
186
- ci.serverUrl = env.GITHUB_SERVER_URL;
187
- if (ci.serverUrl && ci.repository && ci.runId) {
188
- ci.buildUrl = `${ci.serverUrl}/${ci.repository}/actions/runs/${ci.runId}`;
189
- }
190
- }
191
- else if (env.GITLAB_CI) {
192
- ci.provider = 'GitLab CI';
193
- ci.pipelineId = env.CI_PIPELINE_ID;
194
- ci.pipelineUrl = env.CI_PIPELINE_URL;
195
- ci.jobId = env.CI_JOB_ID;
196
- ci.jobUrl = env.CI_JOB_URL;
197
- ci.jobName = env.CI_JOB_NAME;
198
- }
199
- else if (env.CIRCLECI) {
200
- ci.provider = 'CircleCI';
201
- ci.buildNumber = env.CIRCLE_BUILD_NUM;
202
- ci.buildUrl = env.CIRCLE_BUILD_URL;
203
- ci.jobName = env.CIRCLE_JOB;
204
- ci.workflow = env.CIRCLE_WORKFLOW_ID;
205
- }
206
- else if (env.TRAVIS) {
207
- ci.provider = 'Travis CI';
208
- ci.buildNumber = env.TRAVIS_BUILD_NUMBER;
209
- ci.buildUrl = env.TRAVIS_BUILD_WEB_URL;
210
- ci.jobNumber = env.TRAVIS_JOB_NUMBER;
211
- }
212
- else if (env.TF_BUILD) {
213
- ci.provider = 'Azure Pipelines';
214
- ci.buildNumber = env.BUILD_BUILDNUMBER;
215
- ci.buildId = env.BUILD_BUILDID;
216
- if (env.SYSTEM_TEAMFOUNDATIONSERVERURI && env.SYSTEM_TEAMPROJECT && env.BUILD_BUILDID) {
217
- ci.buildUrl = `${env.SYSTEM_TEAMFOUNDATIONSERVERURI}${env.SYSTEM_TEAMPROJECT}/_build/results?buildId=${env.BUILD_BUILDID}`;
218
- }
219
- ci.jobName = env.AGENT_JOBNAME;
220
- }
221
- else if (env.CI) {
222
- ci.provider = 'Unknown CI';
223
- ci.detected = true;
224
- }
225
- return Object.keys(ci).length > 0 ? ci : undefined;
226
- }
227
- extractPlaywrightConfigMetadata(config) {
228
- const meta = {};
229
- if (config.projects?.length > 0) {
230
- meta.projects = config.projects.map((p) => ({
231
- name: p.name,
232
- testDir: p.testDir,
233
- use: {
234
- browserName: p.use?.browserName || p.name,
235
- viewport: p.use?.viewport,
236
- deviceScaleFactor: p.use?.deviceScaleFactor,
237
- },
238
- }));
239
- }
240
- meta.workers = config.workers;
241
- meta.timeout = config.globalTimeout;
242
- meta.fullyParallel = config.fullyParallel;
243
- return meta;
244
- }
245
- }
246
- exports.MetadataCollector = MetadataCollector;
@@ -1,27 +0,0 @@
1
- import type { TestAnnotation } from '../../types.js';
2
- /** Minimal structural shape for the annotation carriers (avoids importing Playwright types). */
3
- interface AnnotationCarrier {
4
- annotations?: ReadonlyArray<{
5
- type: string;
6
- description?: string;
7
- }>;
8
- }
9
- /**
10
- * Merge a test's declared annotations (`test.annotations`) with its result-level
11
- * annotations (`result.annotations`), deduped by `type + description`. Runtime
12
- * `test.skip('reason')` calls can surface on either side depending on the
13
- * Playwright version, so both are considered.
14
- */
15
- export declare function mergeAnnotations(test: AnnotationCarrier, result: AnnotationCarrier): TestAnnotation[];
16
- /**
17
- * Distinguish an intentional skip from a test that could not run.
18
- *
19
- * Playwright reports both as `result.status === 'skipped'`, but an intentional
20
- * `test.skip()` / `test.fixme()` (static, conditional, or runtime) always
21
- * carries a `skip`/`fixme` annotation, while a test skipped as a side effect of
22
- * an earlier failure in a `describe.serial` group carries none. The latter is
23
- * reclassified to `didnotrun` so the dashboard can tell "deliberately skipped"
24
- * from "never actually executed". Non-skipped statuses pass through unchanged.
25
- */
26
- export declare function classifyStatus(rawStatus: string, annotations: TestAnnotation[]): string;
27
- export {};
@@ -1,40 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mergeAnnotations = mergeAnnotations;
4
- exports.classifyStatus = classifyStatus;
5
- /**
6
- * Merge a test's declared annotations (`test.annotations`) with its result-level
7
- * annotations (`result.annotations`), deduped by `type + description`. Runtime
8
- * `test.skip('reason')` calls can surface on either side depending on the
9
- * Playwright version, so both are considered.
10
- */
11
- function mergeAnnotations(test, result) {
12
- const out = [];
13
- const seen = new Set();
14
- for (const list of [test.annotations, result.annotations]) {
15
- for (const a of list ?? []) {
16
- const key = `${a.type}\x00${a.description ?? ''}`;
17
- if (seen.has(key))
18
- continue;
19
- seen.add(key);
20
- out.push(a.description === undefined ? { type: a.type } : { type: a.type, description: a.description });
21
- }
22
- }
23
- return out;
24
- }
25
- /**
26
- * Distinguish an intentional skip from a test that could not run.
27
- *
28
- * Playwright reports both as `result.status === 'skipped'`, but an intentional
29
- * `test.skip()` / `test.fixme()` (static, conditional, or runtime) always
30
- * carries a `skip`/`fixme` annotation, while a test skipped as a side effect of
31
- * an earlier failure in a `describe.serial` group carries none. The latter is
32
- * reclassified to `didnotrun` so the dashboard can tell "deliberately skipped"
33
- * from "never actually executed". Non-skipped statuses pass through unchanged.
34
- */
35
- function classifyStatus(rawStatus, annotations) {
36
- if (rawStatus !== 'skipped')
37
- return rawStatus;
38
- const intentional = annotations.some((a) => a.type === 'skip' || a.type === 'fixme');
39
- return intentional ? 'skipped' : 'didnotrun';
40
- }
@@ -1,103 +0,0 @@
1
- /**
2
- * Categorise a Playwright step into `navigation`, `action`, `input`,
3
- * `assertion`, `wait`, `api`, `hook`, or `other`.
4
- *
5
- * Supports two title formats:
6
- * - Legacy api-path titles ("page.goto", "locator.click", "page.waitForTimeout")
7
- * - Modern human-readable titles introduced in newer Playwright versions
8
- * ("Navigate to \"{url}\"", "Click", "Wait for timeout", "Wait for load state").
9
- * Hook/fixture/expect steps are detected via Playwright's own `category`.
10
- */
11
- export declare function categorizeStep(title: string, pwCategory?: string): string;
12
- /** A single step flattened from the Playwright step tree with its derived category */
13
- export interface FlatStep {
14
- title: string;
15
- duration: number;
16
- category: string;
17
- /** Error message when the step failed (undefined when the step passed). */
18
- error?: {
19
- message: string;
20
- };
21
- /** True when the step carried an error — the signal the server needs for inline failure markers. */
22
- failed?: boolean;
23
- }
24
- /** Step-event category restricted to the values `extractTestStepEvents` emits. */
25
- export type StepEventCategory = 'hook' | 'fixture' | 'test.step' | 'expect' | 'wait';
26
- /** Recursively flatten a nested step tree into a flat list. Uses Playwright's built-in category when available. */
27
- export declare function flattenSteps(steps: any[]): FlatStep[];
28
- /** Aggregated step performance data for a single test case */
29
- export interface StepMetrics {
30
- /** Flattened step list with categories */
31
- steps: FlatStep[];
32
- /** Sum of top-level step durations */
33
- totalStepDuration: number;
34
- /** The single slowest step (by duration) */
35
- slowestStep: {
36
- title: string;
37
- duration: number;
38
- } | null;
39
- /** How many navigation steps were executed */
40
- navigationCount: number;
41
- /** Total wall-clock time spent in navigation steps */
42
- navigationTotalDuration: number;
43
- /** Total wall-clock time spent in wait steps (wasted time) */
44
- waitTotalDuration: number;
45
- /** Count of wait steps */
46
- waitCount: number;
47
- }
48
- /** Collect step metrics (flat steps, slowest step, navigation stats) from a Playwright step array */
49
- export declare function collectStepMetrics(steps: any[]): StepMetrics;
50
- /** Calculate the p-th percentile from a sorted array of numbers */
51
- export declare function percentile(sortedArr: number[], p: number): number;
52
- /** Summary performance statistics for a complete test run */
53
- export interface PerformanceSummary {
54
- /** Average test-case duration in ms */
55
- avgTestDuration?: number;
56
- /** Median (P50) test-case duration in ms */
57
- p50TestDuration?: number;
58
- /** P90 test-case duration in ms */
59
- p90TestDuration?: number;
60
- /** P95 test-case duration in ms */
61
- p95TestDuration?: number;
62
- /** Up to 5 slowest test cases */
63
- slowestTests?: Array<{
64
- title: string;
65
- duration: number;
66
- }>;
67
- /** Total time spent in navigation steps across all cases */
68
- totalNavigationDuration?: number;
69
- /** Average time per navigation step */
70
- avgNavigationDuration?: number;
71
- /** Total time spent in wait steps across all cases */
72
- totalWastedTimeMs?: number;
73
- }
74
- /** Compute run-level performance summary (averages, percentiles, slowest tests) from all test cases */
75
- export declare function computePerformanceSummary(testCases: any[]): PerformanceSummary;
76
- /**
77
- * Extract hook and fixture step events with absolute timings from a Playwright
78
- * step tree. These are used by the WorkersTimeline to render hook segments.
79
- *
80
- * Returns only top-level hook/fixture steps (beforeEach, afterEach, fixture
81
- * setup/teardown) — their sub-steps are included implicitly in their duration.
82
- */
83
- export declare function extractTestStepEvents(steps: any[], _testStartTime: Date): Array<{
84
- title: string;
85
- category: StepEventCategory;
86
- startedAt: number;
87
- duration: number;
88
- status: string;
89
- location?: string | null;
90
- }>;
91
- /**
92
- * Recursively extract wait-category steps from the Playwright step tree
93
- * with absolute timings. These are rendered as semi-transparent amber bars
94
- * on WorkersTimeline to visualize wasted time.
95
- */
96
- export declare function extractWaitEvents(steps: any[], insideWait?: boolean): Array<{
97
- title: string;
98
- category: StepEventCategory;
99
- startedAt: number;
100
- duration: number;
101
- status: string;
102
- location?: string | null;
103
- }>;