@piwitests/reporter 0.5.0 → 0.7.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/README.md +37 -26
- package/dist/global-setup-module.js +2 -2
- package/dist/index.d.ts +13 -4
- package/dist/index.js +19 -5
- package/dist/internal/capture/attachments.d.ts +19 -0
- package/dist/internal/capture/attachments.js +22 -0
- package/dist/internal/capture/capture-fixtures.d.ts +41 -0
- package/dist/internal/capture/capture-fixtures.js +562 -0
- package/dist/{locator-healing.d.ts → internal/capture/locator-healing.d.ts} +47 -1
- package/dist/{locator-healing.js → internal/capture/locator-healing.js} +113 -29
- package/dist/internal/collect/error-text.d.ts +18 -0
- package/dist/internal/collect/error-text.js +79 -0
- package/dist/{metadata-collector.d.ts → internal/collect/metadata-collector.d.ts} +2 -2
- package/dist/{metadata-collector.js → internal/collect/metadata-collector.js} +12 -9
- package/dist/{skip-classify.d.ts → internal/collect/skip-classify.d.ts} +1 -1
- package/dist/{step-analyzer.d.ts → internal/collect/step-analyzer.d.ts} +6 -0
- package/dist/{step-analyzer.js → internal/collect/step-analyzer.js} +7 -2
- package/dist/internal/config/env.d.ts +45 -0
- package/dist/{config.js → internal/config/env.js} +50 -35
- package/dist/internal/files/compression.js +69 -0
- package/dist/{file-handler.d.ts → internal/files/file-handler.d.ts} +4 -4
- package/dist/{file-handler.js → internal/files/file-handler.js} +61 -29
- package/dist/{crash-recovery.d.ts → internal/streaming/crash-recovery.d.ts} +5 -5
- package/dist/{crash-recovery.js → internal/streaming/crash-recovery.js} +9 -8
- package/dist/{stream-buffer.d.ts → internal/streaming/stream-buffer.d.ts} +2 -2
- package/dist/{stream-buffer.js → internal/streaming/stream-buffer.js} +5 -5
- package/dist/{stream-manager.d.ts → internal/streaming/stream-manager.d.ts} +8 -8
- package/dist/{stream-manager.js → internal/streaming/stream-manager.js} +16 -13
- package/dist/{run-submitter.d.ts → internal/submit/run-submitter.d.ts} +7 -7
- package/dist/{run-submitter.js → internal/submit/run-submitter.js} +11 -9
- package/dist/{serializer.d.ts → internal/submit/serializer.d.ts} +1 -1
- package/dist/{uploader.d.ts → internal/submit/uploader.d.ts} +8 -8
- package/dist/{uploader.js → internal/submit/uploader.js} +45 -10
- package/dist/internal/support/ci.d.ts +2 -0
- package/dist/internal/support/ci.js +32 -0
- package/dist/internal/support/cli-filters.d.ts +1 -0
- package/dist/internal/support/cli-filters.js +51 -0
- package/dist/internal/support/errors.d.ts +8 -0
- package/dist/internal/support/errors.js +15 -0
- package/dist/internal/support/instance-id.d.ts +4 -0
- package/dist/internal/support/instance-id.js +48 -0
- package/dist/internal/support/limiter.d.ts +2 -0
- package/dist/internal/support/limiter.js +27 -0
- package/dist/internal/support/setup-file.d.ts +13 -0
- package/dist/internal/support/setup-file.js +61 -0
- package/dist/internal/support/source-snippet.d.ts +12 -0
- package/dist/internal/support/source-snippet.js +97 -0
- package/dist/internal/support/worker-index.d.ts +7 -0
- package/dist/internal/support/worker-index.js +14 -0
- package/dist/{http-client.d.ts → internal/transport/http-client.d.ts} +11 -1
- package/dist/{http-client.js → internal/transport/http-client.js} +57 -10
- package/dist/{config-wrapper.d.ts → public/config-wrapper.d.ts} +1 -1
- package/dist/{config-wrapper.js → public/config-wrapper.js} +4 -4
- package/dist/public/global-setup.d.ts +13 -0
- package/dist/public/global-setup.js +146 -0
- package/dist/{config.d.ts → public/options.d.ts} +7 -45
- package/dist/public/options.js +2 -0
- package/dist/{reporter.d.ts → public/reporter.d.ts} +1 -1
- package/dist/{reporter.js → public/reporter.js} +44 -54
- package/dist/types/collected.d.ts +96 -0
- package/dist/types/collected.js +10 -0
- package/dist/types/wire.d.ts +174 -0
- package/dist/types/wire.js +14 -0
- package/dist/types.d.ts +8 -253
- package/dist/types.js +23 -10
- package/package.json +1 -6
- package/dist/compression.js +0 -39
- package/dist/fixtures.d.ts +0 -25
- package/dist/fixtures.js +0 -336
- package/dist/helpers.d.ts +0 -44
- package/dist/helpers.js +0 -312
- /package/dist/{skip-classify.js → internal/collect/skip-classify.js} +0 -0
- /package/dist/{compression.d.ts → internal/files/compression.d.ts} +0 -0
- /package/dist/{serializer.js → internal/submit/serializer.js} +0 -0
- /package/dist/{logger.d.ts → internal/support/logger.d.ts} +0 -0
- /package/dist/{logger.js → internal/support/logger.js} +0 -0
package/dist/fixtures.js
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dashboardFixtures = void 0;
|
|
4
|
-
exports.extendDashboardFixtures = extendDashboardFixtures;
|
|
5
|
-
const zlib_1 = require("zlib");
|
|
6
|
-
const locator_healing_js_1 = require("./locator-healing.js");
|
|
7
|
-
/**
|
|
8
|
-
* Playwright fixtures that collect network requests, console entries,
|
|
9
|
-
* web vitals, ARIA snapshots, and locator interaction data during a test.
|
|
10
|
-
*
|
|
11
|
-
* Attaches collected data as `piwi-dashboard-*` test-info attachments
|
|
12
|
-
* which the Piwi Dashboard reporter parses on `onTestEnd`.
|
|
13
|
-
*/
|
|
14
|
-
exports.dashboardFixtures = {
|
|
15
|
-
page: async ({ page }, use, testInfo) => {
|
|
16
|
-
const networkRequests = [];
|
|
17
|
-
const consoleEntries = [];
|
|
18
|
-
const pendingHandlers = [];
|
|
19
|
-
// ── Locator interaction capture ──────────────────────────────────────
|
|
20
|
-
// Opt-out: skipped when PIWI_CAPTURE_LOCATORS=false (set automatically when
|
|
21
|
-
// the reporter's collectPerformanceMetrics / captureLocators is disabled),
|
|
22
|
-
// so the per-action DOM read + ARIA snapshot cost is never paid when unused.
|
|
23
|
-
const captureLocators = process.env.PIWI_CAPTURE_LOCATORS !== 'false';
|
|
24
|
-
const capturedLocators = [];
|
|
25
|
-
const capturePromises = [];
|
|
26
|
-
// Locator actions that threw — used at teardown to suggest a fresh locator
|
|
27
|
-
// from the current page when the element appears renamed/moved.
|
|
28
|
-
const failedLocators = [];
|
|
29
|
-
// Chain methods that take args and define a new locator scope (not just narrow).
|
|
30
|
-
// Origin method/args update to the chain call, e.g. .locator('.item') → locator('.item').
|
|
31
|
-
// Positional/filter chains that narrow but don't change locator identity.
|
|
32
|
-
// Origin stays from the page-level call, e.g. .first(), .nth(2), .filter(...).
|
|
33
|
-
function wrapLocator(locator, originMethod, originArgs) {
|
|
34
|
-
return new Proxy(locator, {
|
|
35
|
-
get(target, prop) {
|
|
36
|
-
const original = target[prop];
|
|
37
|
-
if (typeof original !== 'function')
|
|
38
|
-
return original;
|
|
39
|
-
if (locator_healing_js_1.CHAIN_METHODS.includes(prop)) {
|
|
40
|
-
return (...args) => {
|
|
41
|
-
const next = original.apply(target, args);
|
|
42
|
-
if (locator_healing_js_1.LOCATOR_CREATING_CHAINS.has(prop)) {
|
|
43
|
-
return wrapLocator(next, String(prop), args);
|
|
44
|
-
}
|
|
45
|
-
return wrapLocator(next, originMethod, originArgs);
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
if (!locator_healing_js_1.ACTION_METHODS.includes(prop))
|
|
49
|
-
return original;
|
|
50
|
-
return async (...callArgs) => {
|
|
51
|
-
const seq = capturedLocators.length;
|
|
52
|
-
// Capture the test call-site now (sync) so the snapshot's location
|
|
53
|
-
// matches the error stack's first user frame — independent of
|
|
54
|
-
// pw:api step ordering, worker interleaving, or concurrent actions.
|
|
55
|
-
const callerLocation = (0, locator_healing_js_1.captureCallerLocation)();
|
|
56
|
-
// Push a placeholder immediately — DOM capture runs async below
|
|
57
|
-
capturedLocators.push({
|
|
58
|
-
location: callerLocation,
|
|
59
|
-
stepIndex: seq,
|
|
60
|
-
used: {
|
|
61
|
-
method: originMethod,
|
|
62
|
-
args: originArgs,
|
|
63
|
-
raw: `${originMethod}(${JSON.stringify(originArgs)})`,
|
|
64
|
-
},
|
|
65
|
-
element: null,
|
|
66
|
-
alternatives: [],
|
|
67
|
-
});
|
|
68
|
-
// The placeholder is already pushed. If the action throws, record
|
|
69
|
-
// the failed locator (so teardown can suggest a fresh one for the
|
|
70
|
-
// element's current identity) and re-throw so the test still fails.
|
|
71
|
-
let result;
|
|
72
|
-
try {
|
|
73
|
-
result = await original.apply(target, callArgs);
|
|
74
|
-
}
|
|
75
|
-
catch (err) {
|
|
76
|
-
failedLocators.push({ method: originMethod, args: originArgs });
|
|
77
|
-
throw err;
|
|
78
|
-
}
|
|
79
|
-
// Fire-and-forget: capture element data without blocking the test.
|
|
80
|
-
// evaluate() can hang when page navigates (element detaches), so
|
|
81
|
-
// race it against a 500ms deadline and never throw.
|
|
82
|
-
const resolveAttrs = (async () => {
|
|
83
|
-
try {
|
|
84
|
-
const attrs = (await Promise.race([
|
|
85
|
-
target.evaluate((el, keep) => {
|
|
86
|
-
const attrMap = {};
|
|
87
|
-
for (const key of keep) {
|
|
88
|
-
const v = el.getAttribute(key) ?? el[key];
|
|
89
|
-
attrMap[key] = typeof v === 'string' ? v.slice(0, 200) : v ? String(v).slice(0, 200) : null;
|
|
90
|
-
}
|
|
91
|
-
const r = el.getBoundingClientRect();
|
|
92
|
-
return {
|
|
93
|
-
tagName: el.tagName?.toLowerCase?.() ?? 'unknown',
|
|
94
|
-
attributes: attrMap,
|
|
95
|
-
textContent: (el.textContent || '').trim().slice(0, 80),
|
|
96
|
-
center: {
|
|
97
|
-
x: Math.round(r.x + r.width / 2),
|
|
98
|
-
y: Math.round(r.y + r.height / 2),
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
}, [...locator_healing_js_1.CAPTURED_ATTRIBUTES]),
|
|
102
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('locator capture timeout')), 500)),
|
|
103
|
-
]));
|
|
104
|
-
// The browser-computed accessible name only feeds role-based and
|
|
105
|
-
// form-field alternatives, so only pay for the extra ARIA
|
|
106
|
-
// snapshot when the element actually has a role or is a field.
|
|
107
|
-
const role = (0, locator_healing_js_1.resolveAriaRole)({
|
|
108
|
-
tagName: attrs.tagName,
|
|
109
|
-
attributes: attrs.attributes,
|
|
110
|
-
textContent: attrs.textContent,
|
|
111
|
-
accessibleName: null,
|
|
112
|
-
center: attrs.center,
|
|
113
|
-
});
|
|
114
|
-
const isFormField = ['input', 'select', 'textarea'].includes(attrs.tagName);
|
|
115
|
-
// Bound with a timeout: without it, ariaSnapshot waits up to the
|
|
116
|
-
// test timeout when the page is mid-navigation, which hangs the
|
|
117
|
-
// fixture teardown that drains these capture promises.
|
|
118
|
-
const aria = role || isFormField
|
|
119
|
-
? (await target.ariaSnapshot({ ref: true, timeout: 500 }).catch(() => null))
|
|
120
|
-
: null;
|
|
121
|
-
const accessibleName = (0, locator_healing_js_1.extractAccessibleName)(aria) ||
|
|
122
|
-
(0, locator_healing_js_1.approximateAccessibleName)({
|
|
123
|
-
tagName: attrs.tagName,
|
|
124
|
-
attributes: attrs.attributes,
|
|
125
|
-
textContent: attrs.textContent,
|
|
126
|
-
accessibleName: null,
|
|
127
|
-
center: attrs.center,
|
|
128
|
-
});
|
|
129
|
-
capturedLocators[seq] = {
|
|
130
|
-
location: callerLocation,
|
|
131
|
-
stepIndex: seq,
|
|
132
|
-
used: {
|
|
133
|
-
method: originMethod,
|
|
134
|
-
args: originArgs,
|
|
135
|
-
raw: `${originMethod}(${JSON.stringify(originArgs)})`,
|
|
136
|
-
},
|
|
137
|
-
element: { ...attrs, accessibleName },
|
|
138
|
-
alternatives: (0, locator_healing_js_1.generateAlternatives)({
|
|
139
|
-
tagName: attrs.tagName,
|
|
140
|
-
attributes: attrs.attributes,
|
|
141
|
-
textContent: attrs.textContent,
|
|
142
|
-
accessibleName,
|
|
143
|
-
center: attrs.center,
|
|
144
|
-
}),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
// element detached or timeout — keep the placeholder
|
|
149
|
-
}
|
|
150
|
-
})();
|
|
151
|
-
capturePromises.push(resolveAttrs);
|
|
152
|
-
return result;
|
|
153
|
-
};
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
if (captureLocators) {
|
|
158
|
-
for (const method of locator_healing_js_1.LOCATOR_METHODS) {
|
|
159
|
-
const original = page[method].bind(page);
|
|
160
|
-
page[method] = (...args) => wrapLocator(original(...args), method, args);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// ── Existing event listeners ──────────────────────────────────────────
|
|
164
|
-
page.on('console', (msg) => {
|
|
165
|
-
const type = msg.type();
|
|
166
|
-
if (['warning', 'error', 'assert'].includes(type)) {
|
|
167
|
-
consoleEntries.push({
|
|
168
|
-
type,
|
|
169
|
-
text: msg.text(),
|
|
170
|
-
timestamp: Date.now(),
|
|
171
|
-
location: msg.location()
|
|
172
|
-
? `${msg.location().url}:${msg.location().lineNumber}:${msg.location().columnNumber}`
|
|
173
|
-
: null,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
page.on('requestfinished', (request) => {
|
|
178
|
-
const p = (async () => {
|
|
179
|
-
try {
|
|
180
|
-
const url = request.url();
|
|
181
|
-
if (url.startsWith('data:') || url.startsWith('blob:'))
|
|
182
|
-
return;
|
|
183
|
-
const timing = request.timing();
|
|
184
|
-
const response = await request.response();
|
|
185
|
-
const resourceType = request.resourceType();
|
|
186
|
-
// Only keep API/document requests; skip static assets (scripts, styles, fonts, images, media)
|
|
187
|
-
if (!['fetch', 'xhr', 'document', 'other'].includes(resourceType))
|
|
188
|
-
return;
|
|
189
|
-
const entry = {
|
|
190
|
-
method: request.method(),
|
|
191
|
-
url,
|
|
192
|
-
status: response ? response.status() : 0,
|
|
193
|
-
duration: timing.responseEnd > 0 ? Math.round(timing.responseEnd - timing.requestStart) : 0,
|
|
194
|
-
startTime: timing.startTime,
|
|
195
|
-
resourceType,
|
|
196
|
-
};
|
|
197
|
-
if (response) {
|
|
198
|
-
const headers = response.headers();
|
|
199
|
-
// Response content type (without charset/boundary params) — relevant
|
|
200
|
-
// per-request metadata for distinguishing API/JSON vs document/HTML calls.
|
|
201
|
-
const contentType = headers['content-type'];
|
|
202
|
-
if (contentType)
|
|
203
|
-
entry.contentType = contentType.split(';')[0].trim();
|
|
204
|
-
const logHeader = headers['x-piwi-logs'];
|
|
205
|
-
if (logHeader) {
|
|
206
|
-
try {
|
|
207
|
-
entry.serverLogs = JSON.parse((0, zlib_1.gunzipSync)(Buffer.from(logHeader, 'base64')).toString('utf-8'));
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
/* ignore malformed header */
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
networkRequests.push(entry);
|
|
215
|
-
}
|
|
216
|
-
catch {
|
|
217
|
-
/* ignore */
|
|
218
|
-
}
|
|
219
|
-
})();
|
|
220
|
-
pendingHandlers.push(p);
|
|
221
|
-
});
|
|
222
|
-
await use(page);
|
|
223
|
-
// Wait for all in-flight requestfinished handlers before snapshotting
|
|
224
|
-
// networkRequests — the last request (often the one that failed the test)
|
|
225
|
-
// races with fixture teardown and its serverLogs would otherwise be lost.
|
|
226
|
-
await Promise.allSettled(pendingHandlers);
|
|
227
|
-
// ── Attach locator snapshots ──────────────────────────────────────────
|
|
228
|
-
// Cap the drain so a stuck capture (e.g. a navigation in flight) can never
|
|
229
|
-
// hang teardown past the test timeout; per-action evaluate/ariaSnapshot are
|
|
230
|
-
// already bounded, this is a backstop.
|
|
231
|
-
await Promise.race([Promise.allSettled(capturePromises), new Promise((resolve) => setTimeout(resolve, 2000))]);
|
|
232
|
-
if (capturedLocators.length > 0) {
|
|
233
|
-
await testInfo.attach('piwi-dashboard-locators', {
|
|
234
|
-
contentType: 'application/json',
|
|
235
|
-
body: Buffer.from(JSON.stringify(capturedLocators)),
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
if (testInfo.status !== 'passed' && testInfo.status !== 'skipped') {
|
|
239
|
-
try {
|
|
240
|
-
const snapshot = await page.locator(':root').ariaSnapshot();
|
|
241
|
-
if (snapshot) {
|
|
242
|
-
await testInfo.attach('piwi-dashboard-aria-snapshot', {
|
|
243
|
-
contentType: 'text/plain',
|
|
244
|
-
body: snapshot,
|
|
245
|
-
});
|
|
246
|
-
// Suggest a fresh locator for the failed action from the current page.
|
|
247
|
-
// When the element was renamed/moved, the pre-captured alternatives
|
|
248
|
-
// describe the old element, so this points at where it went now — as a
|
|
249
|
-
// Playwright annotation (shown in the report + trace) and an attachment
|
|
250
|
-
// (shown in the trace viewer). It does NOT change the locator.
|
|
251
|
-
const failed = failedLocators[failedLocators.length - 1];
|
|
252
|
-
const suggestion = failed ? (0, locator_healing_js_1.suggestLocatorsFromAria)(failed, snapshot) : null;
|
|
253
|
-
if (suggestion) {
|
|
254
|
-
testInfo.annotations.push({
|
|
255
|
-
type: 'piwi-locator-suggestion',
|
|
256
|
-
description: `${suggestion.failing} matched nothing on the failing page — the element may have been renamed or moved. Suggested: ${suggestion.suggestions.join(' | ')}`,
|
|
257
|
-
});
|
|
258
|
-
await testInfo.attach('piwi-dashboard-locator-suggestion', {
|
|
259
|
-
contentType: 'application/json',
|
|
260
|
-
body: Buffer.from(JSON.stringify(suggestion)),
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
/* ignore */
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (consoleEntries.length > 0) {
|
|
270
|
-
await testInfo.attach('piwi-dashboard-console', {
|
|
271
|
-
contentType: 'application/json',
|
|
272
|
-
body: Buffer.from(JSON.stringify(consoleEntries)),
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
if (networkRequests.length > 0) {
|
|
276
|
-
await testInfo.attach('piwi-dashboard-network', {
|
|
277
|
-
contentType: 'application/json',
|
|
278
|
-
body: Buffer.from(JSON.stringify(networkRequests)),
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
try {
|
|
282
|
-
const webVitals = await page.evaluate(() => {
|
|
283
|
-
const navEntries = performance.getEntriesByType('navigation');
|
|
284
|
-
const paintEntries = performance.getEntriesByType('paint');
|
|
285
|
-
const nav = navEntries[0];
|
|
286
|
-
const navigation = nav
|
|
287
|
-
? {
|
|
288
|
-
url: nav.name,
|
|
289
|
-
ttfb: Math.round(nav.responseStart - nav.fetchStart),
|
|
290
|
-
domInteractive: Math.round(nav.domInteractive - nav.fetchStart),
|
|
291
|
-
domContentLoaded: Math.round(nav.domContentLoadedEventEnd - nav.fetchStart),
|
|
292
|
-
loadComplete: Math.round(nav.loadEventEnd - nav.fetchStart),
|
|
293
|
-
transferSize: nav.transferSize || 0,
|
|
294
|
-
encodedBodySize: nav.encodedBodySize || 0,
|
|
295
|
-
decodedBodySize: nav.decodedBodySize || 0,
|
|
296
|
-
}
|
|
297
|
-
: null;
|
|
298
|
-
const paint = {};
|
|
299
|
-
for (const entry of paintEntries) {
|
|
300
|
-
const key = entry.name.replace(/-([a-z])/g, (_, l) => l.toUpperCase());
|
|
301
|
-
paint[key] = Math.round(entry.startTime);
|
|
302
|
-
}
|
|
303
|
-
if (!navigation && Object.keys(paint).length === 0)
|
|
304
|
-
return null;
|
|
305
|
-
return { navigation, paint };
|
|
306
|
-
});
|
|
307
|
-
if (webVitals) {
|
|
308
|
-
await testInfo.attach('piwi-dashboard-web-vitals', {
|
|
309
|
-
contentType: 'application/json',
|
|
310
|
-
body: Buffer.from(JSON.stringify(webVitals)),
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
catch {
|
|
315
|
-
/* ignore */
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
};
|
|
319
|
-
/**
|
|
320
|
-
* Extend a Playwright `test` object with Piwi Dashboard fixtures.
|
|
321
|
-
*
|
|
322
|
-
* Use this instead of importing `@playwright/test` directly from this package
|
|
323
|
-
* to avoid the "Requiring @playwright/test second time" error caused by
|
|
324
|
-
* duplicate module resolution.
|
|
325
|
-
*
|
|
326
|
-
* @example
|
|
327
|
-
* ```ts
|
|
328
|
-
* import { test as base } from '@playwright/test';
|
|
329
|
-
* import { extendDashboardFixtures } from '@piwitests/reporter/fixtures';
|
|
330
|
-
*
|
|
331
|
-
* export const test = extendDashboardFixtures(base);
|
|
332
|
-
* ```
|
|
333
|
-
*/
|
|
334
|
-
function extendDashboardFixtures(test) {
|
|
335
|
-
return test.extend(exports.dashboardFixtures);
|
|
336
|
-
}
|
package/dist/helpers.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { PiwiDashboardOptions } from './config.js';
|
|
2
|
-
/** Produce a deterministic short SHA-1 hex hash for a project name (used in temp file names) */
|
|
3
|
-
export declare function hashForProject(projectName: string): string;
|
|
4
|
-
/**
|
|
5
|
-
* Extract a worker index from a Playwright `TestResult`, falling back to
|
|
6
|
-
* `parallelIndex` when `workerIndex` is absent. Returns `null` when neither is
|
|
7
|
-
* set. Localizes the `as any` reach into Playwright's result object so the
|
|
8
|
-
* cast lives in exactly one place.
|
|
9
|
-
*/
|
|
10
|
-
export declare function workerIndexOf(result: any): number | null;
|
|
11
|
-
export declare function detectCliFileFilters(argv?: string[]): string[];
|
|
12
|
-
/** Return the temp-file path used to exchange setup info between globalSetup and the reporter instance */
|
|
13
|
-
export declare function getSetupFilePath(projectName: string): string;
|
|
14
|
-
/** Derive a unique instance identifier by hashing hostname + projectName (or runLabel + projectName when sharding) */
|
|
15
|
-
export declare function computeInstanceId(projectName: string, runLabel?: string | null): string;
|
|
16
|
-
/** Detect a stable CI run label from well-known environment variables. Returns null outside CI. */
|
|
17
|
-
export declare function detectCiRunLabel(): string | null;
|
|
18
|
-
/** Information saved by the global setup for the reporter instance to consume */
|
|
19
|
-
export interface SetupInfo {
|
|
20
|
-
/** Server-assigned run ID */
|
|
21
|
-
runId: number;
|
|
22
|
-
/** One-time token used to authenticate the /begin call */
|
|
23
|
-
setupToken: string;
|
|
24
|
-
/** Project name this run belongs to */
|
|
25
|
-
projectName: string;
|
|
26
|
-
}
|
|
27
|
-
/** Read and delete the setup info file for the given project. Returns `null` when no file exists. */
|
|
28
|
-
export declare function readSetupInfo(projectName: string): SetupInfo | null;
|
|
29
|
-
/** Read a snippet of source code surrounding the given line, returning a formatted string with line numbers. Returns `null` on error. */
|
|
30
|
-
export declare function readSourceSnippet(file: string, line: number, context: number): string | null;
|
|
31
|
-
/** Create a concurrency limiter that ensures at most `maxConcurrent` async operations run simultaneously */
|
|
32
|
-
export declare function createLimiter(maxConcurrent: number): <T>(fn: () => Promise<T>) => Promise<T>;
|
|
33
|
-
/**
|
|
34
|
-
* Create a Playwright `globalSetup` function that registers a test run on the
|
|
35
|
-
* Piwi Dashboard server at the start of the run. An optional `userSetup` is
|
|
36
|
-
* called afterward so existing setup logic is preserved.
|
|
37
|
-
*
|
|
38
|
-
* The server-run ID and a one-time token are written to a temp file so the
|
|
39
|
-
* reporter instance can pick them up during the streaming handshake.
|
|
40
|
-
*
|
|
41
|
-
* @param options Piwi Dashboard options (uses `serverUrl`, `projectName`, …).
|
|
42
|
-
* @param userSetup An existing global setup to chain after the Piwi registration.
|
|
43
|
-
*/
|
|
44
|
-
export declare function createGlobalSetup(options?: PiwiDashboardOptions, userSetup?: (config: any) => any): (config: any) => Promise<any>;
|