@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.
- package/dist/global-setup-module.d.ts +2 -1
- package/dist/global-setup-module.js +432 -3
- package/dist/index.d.ts +204 -8
- package/dist/index.js +4724 -23
- package/dist/internal/capture/attachments.d.ts +9 -3
- package/dist/internal/capture/attachments.js +46 -20
- package/dist/internal/capture/capture-fixtures.d.ts +110 -7
- package/dist/internal/capture/capture-fixtures.js +2227 -639
- package/dist/internal/capture/inspect-on-failure.d.ts +50 -0
- package/dist/internal/capture/inspect-on-failure.js +66 -0
- package/dist/internal/capture/locator-healing.d.ts +33 -131
- package/dist/internal/capture/locator-healing.js +602 -711
- package/dist/internal/capture/pick-on-failure.d.ts +198 -0
- package/dist/internal/capture/pick-on-failure.js +1203 -0
- package/package.json +9 -4
- package/dist/internal/collect/error-text.d.ts +0 -18
- package/dist/internal/collect/error-text.js +0 -79
- package/dist/internal/collect/metadata-collector.d.ts +0 -32
- package/dist/internal/collect/metadata-collector.js +0 -246
- package/dist/internal/collect/skip-classify.d.ts +0 -27
- package/dist/internal/collect/skip-classify.js +0 -40
- package/dist/internal/collect/step-analyzer.d.ts +0 -103
- package/dist/internal/collect/step-analyzer.js +0 -221
- package/dist/internal/config/env.d.ts +0 -45
- package/dist/internal/config/env.js +0 -153
- package/dist/internal/files/compression.d.ts +0 -5
- package/dist/internal/files/compression.js +0 -69
- package/dist/internal/files/file-handler.d.ts +0 -38
- package/dist/internal/files/file-handler.js +0 -198
- package/dist/internal/streaming/crash-recovery.d.ts +0 -23
- package/dist/internal/streaming/crash-recovery.js +0 -106
- package/dist/internal/streaming/stream-buffer.d.ts +0 -17
- package/dist/internal/streaming/stream-buffer.js +0 -102
- package/dist/internal/streaming/stream-manager.d.ts +0 -88
- package/dist/internal/streaming/stream-manager.js +0 -395
- package/dist/internal/submit/run-submitter.d.ts +0 -67
- package/dist/internal/submit/run-submitter.js +0 -190
- package/dist/internal/submit/serializer.d.ts +0 -45
- package/dist/internal/submit/serializer.js +0 -107
- package/dist/internal/submit/uploader.d.ts +0 -89
- package/dist/internal/submit/uploader.js +0 -226
- package/dist/internal/support/ci.d.ts +0 -2
- package/dist/internal/support/ci.js +0 -32
- package/dist/internal/support/cli-filters.d.ts +0 -1
- package/dist/internal/support/cli-filters.js +0 -51
- package/dist/internal/support/errors.d.ts +0 -8
- package/dist/internal/support/errors.js +0 -15
- package/dist/internal/support/instance-id.d.ts +0 -4
- package/dist/internal/support/instance-id.js +0 -48
- package/dist/internal/support/limiter.d.ts +0 -2
- package/dist/internal/support/limiter.js +0 -27
- package/dist/internal/support/logger.d.ts +0 -26
- package/dist/internal/support/logger.js +0 -43
- package/dist/internal/support/reporter-version.d.ts +0 -2
- package/dist/internal/support/reporter-version.js +0 -54
- package/dist/internal/support/setup-file.d.ts +0 -13
- package/dist/internal/support/setup-file.js +0 -61
- package/dist/internal/support/source-snippet.d.ts +0 -12
- package/dist/internal/support/source-snippet.js +0 -97
- package/dist/internal/support/worker-index.d.ts +0 -7
- package/dist/internal/support/worker-index.js +0 -14
- package/dist/internal/transport/http-client.d.ts +0 -52
- package/dist/internal/transport/http-client.js +0 -201
- package/dist/public/config-wrapper.d.ts +0 -21
- package/dist/public/config-wrapper.js +0 -64
- package/dist/public/global-setup.d.ts +0 -13
- package/dist/public/global-setup.js +0 -146
- package/dist/public/options.d.ts +0 -78
- package/dist/public/options.js +0 -2
- package/dist/public/reporter.d.ts +0 -68
- package/dist/public/reporter.js +0 -376
- package/dist/types/collected.d.ts +0 -96
- package/dist/types/collected.js +0 -10
- package/dist/types/wire.d.ts +0 -174
- package/dist/types/wire.js +0 -14
- package/dist/types.d.ts +0 -11
- package/dist/types.js +0 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piwitests/reporter",
|
|
3
|
-
"version": "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": "
|
|
39
|
-
"reporter:dev": "
|
|
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.
|
|
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
|
-
}>;
|