@argos-ci/playwright 6.5.0 → 6.6.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.
- package/dist/index.cjs +4 -4
- package/dist/index.d.mts +185 -0
- package/dist/index.mjs +564 -0
- package/dist/reporter.d.mts +1984 -0
- package/dist/reporter.mjs +323 -0
- package/package.json +14 -14
- package/dist/index.d.ts +0 -162
- package/dist/index.js +0 -604
- package/dist/reporter.d.ts +0 -70
- package/dist/reporter.js +0 -388
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { getGitRepositoryPath, getMetadataPath, getScreenshotName, readVersionFromPackage, validateThreshold, writeMetadata } from "@argos-ci/util";
|
|
4
|
+
import { dirname, relative, resolve } from "node:path";
|
|
5
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
+
import { getGlobalScript, resolveViewport } from "@argos-ci/browser";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
//#region src/metadata.ts
|
|
9
|
+
const require$1 = createRequire(import.meta.url);
|
|
10
|
+
/**
|
|
11
|
+
* Try to resolve a package.
|
|
12
|
+
*/
|
|
13
|
+
function tryResolve(pkg) {
|
|
14
|
+
try {
|
|
15
|
+
return require$1.resolve(pkg);
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Private metadata config storage.
|
|
22
|
+
* Used to inject the metadata from other SDKs like @argos-ci/storybook.
|
|
23
|
+
*/
|
|
24
|
+
const metadataConfigStorage = new AsyncLocalStorage();
|
|
25
|
+
/**
|
|
26
|
+
* Set the metadata config.
|
|
27
|
+
*/
|
|
28
|
+
function setMetadataConfig(metadata) {
|
|
29
|
+
metadataConfigStorage.enterWith(metadata);
|
|
30
|
+
}
|
|
31
|
+
const DEFAULT_PLAYWRIGHT_LIBRARIES = [
|
|
32
|
+
"@playwright/test",
|
|
33
|
+
"playwright",
|
|
34
|
+
"playwright-core"
|
|
35
|
+
];
|
|
36
|
+
/**
|
|
37
|
+
* Get the metadata overrides set by the SDK.
|
|
38
|
+
*/
|
|
39
|
+
function getMetadataOverrides() {
|
|
40
|
+
return metadataConfigStorage.getStore();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get the name and version of the automation library.
|
|
44
|
+
*/
|
|
45
|
+
async function getAutomationLibraryMetadata() {
|
|
46
|
+
const libraries = metadataConfigStorage.getStore()?.playwrightLibraries ?? DEFAULT_PLAYWRIGHT_LIBRARIES;
|
|
47
|
+
for (const name of libraries) {
|
|
48
|
+
const pkgPath = tryResolve(`${name}/package.json`);
|
|
49
|
+
if (pkgPath) return {
|
|
50
|
+
version: await readVersionFromPackage(pkgPath),
|
|
51
|
+
name
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Unable to find any of the following packages: ${libraries.join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the version of the Argos Playwright SDK.
|
|
58
|
+
*/
|
|
59
|
+
async function getArgosPlaywrightVersion() {
|
|
60
|
+
return readVersionFromPackage(require$1.resolve("@argos-ci/playwright/package.json"));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the name and version of the SDK.
|
|
64
|
+
*/
|
|
65
|
+
async function getSdkMetadata() {
|
|
66
|
+
const metadataConfig = metadataConfigStorage.getStore();
|
|
67
|
+
if (metadataConfig) return metadataConfig.sdk;
|
|
68
|
+
return {
|
|
69
|
+
name: "@argos-ci/playwright",
|
|
70
|
+
version: await getArgosPlaywrightVersion()
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the metadata of the automation library and the SDK.
|
|
75
|
+
*/
|
|
76
|
+
async function getLibraryMetadata() {
|
|
77
|
+
const [automationLibrary, sdk] = await Promise.all([getAutomationLibraryMetadata(), getSdkMetadata()]);
|
|
78
|
+
return {
|
|
79
|
+
automationLibrary,
|
|
80
|
+
sdk
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the test file path relative to the repository path.
|
|
85
|
+
* If the repository path is not set, it returns the absolute path.
|
|
86
|
+
*/
|
|
87
|
+
function resolveTestFilePath(filepath, repositoryPath) {
|
|
88
|
+
if (!repositoryPath) return filepath;
|
|
89
|
+
return relative(repositoryPath, filepath);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get the metadata of the test.
|
|
93
|
+
*/
|
|
94
|
+
async function getTestMetadata(testInfo) {
|
|
95
|
+
const repositoryPath = await getGitRepositoryPath();
|
|
96
|
+
const metadataConfig = metadataConfigStorage.getStore();
|
|
97
|
+
if (metadataConfig?.test) return {
|
|
98
|
+
...metadataConfig.test,
|
|
99
|
+
location: metadataConfig.test?.location ? {
|
|
100
|
+
file: resolveTestFilePath(metadataConfig.test.location.file, repositoryPath),
|
|
101
|
+
line: metadataConfig.test.location.line,
|
|
102
|
+
column: metadataConfig.test.location.column
|
|
103
|
+
} : void 0
|
|
104
|
+
};
|
|
105
|
+
if (!testInfo) return null;
|
|
106
|
+
return {
|
|
107
|
+
id: testInfo.testId,
|
|
108
|
+
title: testInfo.title,
|
|
109
|
+
titlePath: testInfo.titlePath,
|
|
110
|
+
tags: testInfo.tags.length > 0 ? testInfo.tags : void 0,
|
|
111
|
+
retry: testInfo.retry,
|
|
112
|
+
retries: testInfo.project.retries,
|
|
113
|
+
repeat: testInfo.repeatEachIndex,
|
|
114
|
+
location: {
|
|
115
|
+
file: resolveTestFilePath(testInfo.file, repositoryPath),
|
|
116
|
+
line: testInfo.line,
|
|
117
|
+
column: testInfo.column
|
|
118
|
+
},
|
|
119
|
+
annotations: testInfo.annotations
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/util.ts
|
|
124
|
+
const require = createRequire(import.meta.url);
|
|
125
|
+
/**
|
|
126
|
+
* Check if the project is using the Argos reporter.
|
|
127
|
+
*/
|
|
128
|
+
function checkIsUsingArgosReporter(testInfo) {
|
|
129
|
+
if (!testInfo) return false;
|
|
130
|
+
const reporterPath = require.resolve("@argos-ci/playwright/reporter");
|
|
131
|
+
return testInfo.config.reporter.some((reporter) => reporter[0].includes("@argos-ci/playwright/reporter") || reporter[0] === reporterPath);
|
|
132
|
+
}
|
|
133
|
+
const PNG_EXTENSION = `.png`;
|
|
134
|
+
const ARIA_EXTENSION = `.aria.yml`;
|
|
135
|
+
255 - PNG_EXTENSION.length - `.argos.json`.length;
|
|
136
|
+
/**
|
|
137
|
+
* Get test info from the Playwright test.
|
|
138
|
+
*/
|
|
139
|
+
async function getTestInfo() {
|
|
140
|
+
try {
|
|
141
|
+
const { test } = await import("@playwright/test");
|
|
142
|
+
return test.info();
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Check if the value is a Page.
|
|
149
|
+
*/
|
|
150
|
+
function checkIsPage(value) {
|
|
151
|
+
return Boolean(value && typeof value === "object" && "bringToFront" in value && typeof value.bringToFront === "function");
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Check if the value is an element handle.
|
|
155
|
+
*/
|
|
156
|
+
function checkIsElementHandle(value) {
|
|
157
|
+
return Boolean(value && typeof value === "object" && "asElement" in value && typeof value.asElement === "function");
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Check if the handler is a Frame.
|
|
161
|
+
*/
|
|
162
|
+
function checkIsFrame(handler) {
|
|
163
|
+
return "page" in handler && typeof handler.page === "function";
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get the Playwright `Page` from the handler.
|
|
167
|
+
* If the handler is a Frame, it returns the parent page.
|
|
168
|
+
* Otherwise, it returns the handler itself.
|
|
169
|
+
*/
|
|
170
|
+
function getPage(handler) {
|
|
171
|
+
if (checkIsFrame(handler)) return handler.page();
|
|
172
|
+
return handler;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get the viewport size.
|
|
176
|
+
*/
|
|
177
|
+
function getViewportSize(page) {
|
|
178
|
+
const viewportSize = page.viewportSize();
|
|
179
|
+
if (!viewportSize) throw new Error("Snapshots can't be taken without a viewport.");
|
|
180
|
+
return viewportSize;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Sets the viewport size and waits for the visual viewport to match the specified dimensions.
|
|
184
|
+
* @returns A promise that resolves when the viewport size has been successfully set and matched.
|
|
185
|
+
*/
|
|
186
|
+
async function setViewportSize(page, viewportSize) {
|
|
187
|
+
await page.setViewportSize(viewportSize);
|
|
188
|
+
await page.waitForFunction(({ width, height }) => window.innerWidth === width && window.innerHeight === height, {
|
|
189
|
+
width: viewportSize.width,
|
|
190
|
+
height: viewportSize.height
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get the snapshot names based on the test info.
|
|
195
|
+
*/
|
|
196
|
+
function getSnapshotNames(name, testInfo) {
|
|
197
|
+
if (testInfo) {
|
|
198
|
+
const projectName = `${testInfo.project.name}/${name}`;
|
|
199
|
+
if (testInfo.repeatEachIndex > 0) return {
|
|
200
|
+
name: `${projectName} repeat-${testInfo.repeatEachIndex}`,
|
|
201
|
+
baseName: projectName
|
|
202
|
+
};
|
|
203
|
+
return {
|
|
204
|
+
name: projectName,
|
|
205
|
+
baseName: null
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
name,
|
|
210
|
+
baseName: null
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Inject Argos script into the document.
|
|
215
|
+
*/
|
|
216
|
+
async function injectArgos(handler) {
|
|
217
|
+
if (!await handler.evaluate(() => typeof window.__ARGOS__ !== "undefined")) await handler.addScriptTag({ content: getGlobalScript() });
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Prepare Argos screenshot by injecting the SDK and creating the root directory.
|
|
221
|
+
*/
|
|
222
|
+
async function prepare(args) {
|
|
223
|
+
const { handler, useArgosReporter, root } = args;
|
|
224
|
+
await Promise.all([useArgosReporter ? null : mkdir(root, { recursive: true }), injectArgos(handler)]);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get metadata and path.
|
|
228
|
+
*/
|
|
229
|
+
async function getPathAndMetadata(args) {
|
|
230
|
+
const { handler, testInfo, names, extension, root, useArgosReporter } = args;
|
|
231
|
+
const overrides = getMetadataOverrides();
|
|
232
|
+
const path = useArgosReporter && testInfo ? testInfo.outputPath("argos", `${names.name}${extension}`) : resolve(root, `${names.name}${extension}`);
|
|
233
|
+
const dir = dirname(path);
|
|
234
|
+
const [colorScheme, mediaType, libMetadata, testMetadata] = await Promise.all([
|
|
235
|
+
handler.evaluate(() => window.__ARGOS__.getColorScheme()),
|
|
236
|
+
handler.evaluate(() => window.__ARGOS__.getMediaType()),
|
|
237
|
+
getLibraryMetadata(),
|
|
238
|
+
getTestMetadata(testInfo),
|
|
239
|
+
dir !== root ? mkdir(dir, { recursive: true }) : null
|
|
240
|
+
]);
|
|
241
|
+
const viewportSize = checkIsFrame(handler) ? null : getViewportSize(handler);
|
|
242
|
+
const browser = getPage(handler).context().browser();
|
|
243
|
+
if (!browser) throw new Error("Can't take screenshots without a browser.");
|
|
244
|
+
const browserName = browser.browserType().name();
|
|
245
|
+
const browserVersion = browser.version();
|
|
246
|
+
const metadata = {
|
|
247
|
+
url: overrides?.url ?? handler.url(),
|
|
248
|
+
colorScheme,
|
|
249
|
+
mediaType,
|
|
250
|
+
test: testMetadata,
|
|
251
|
+
story: overrides?.story,
|
|
252
|
+
browser: {
|
|
253
|
+
name: browserName,
|
|
254
|
+
version: browserVersion
|
|
255
|
+
},
|
|
256
|
+
...libMetadata
|
|
257
|
+
};
|
|
258
|
+
const viewport = viewportSize ?? getMetadataOverrides()?.viewport;
|
|
259
|
+
if (viewport) metadata.viewport = viewport;
|
|
260
|
+
metadata.transient = {};
|
|
261
|
+
if (names.baseName) metadata.transient.baseName = `${names.baseName}${extension}`;
|
|
262
|
+
return {
|
|
263
|
+
metadata,
|
|
264
|
+
path
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Convert a screenshot to a snapshot path
|
|
269
|
+
*/
|
|
270
|
+
function screenshotToSnapshotPath(value) {
|
|
271
|
+
return value.replace(/\.png$/, ARIA_EXTENSION);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Run before taking all screenshots.
|
|
275
|
+
*/
|
|
276
|
+
async function beforeAll(handler, context, options) {
|
|
277
|
+
await handler.evaluate((context) => window.__ARGOS__.beforeAll(context), context);
|
|
278
|
+
if (options?.disableHover) await getPage(handler).mouse.move(0, 0);
|
|
279
|
+
return async () => {
|
|
280
|
+
await handler.evaluate(() => window.__ARGOS__.afterAll());
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Run before taking each screenshot.
|
|
285
|
+
*/
|
|
286
|
+
async function beforeEach(handler, context) {
|
|
287
|
+
await handler.evaluate((context) => window.__ARGOS__.beforeEach(context), context);
|
|
288
|
+
return async () => {
|
|
289
|
+
await handler.evaluate(() => window.__ARGOS__.afterEach());
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Increase the timeout for the test x3.
|
|
294
|
+
* Returns a function to reset the timeout.
|
|
295
|
+
*/
|
|
296
|
+
async function increaseTimeout() {
|
|
297
|
+
const testInfo = await getTestInfo();
|
|
298
|
+
if (testInfo) {
|
|
299
|
+
const { timeout } = testInfo;
|
|
300
|
+
testInfo.setTimeout(timeout * 3);
|
|
301
|
+
return {
|
|
302
|
+
value: timeout,
|
|
303
|
+
reset: () => {
|
|
304
|
+
testInfo.setTimeout(timeout);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Wait for the UI to be ready before taking the screenshot.
|
|
312
|
+
*/
|
|
313
|
+
async function waitForReadiness(handler, context) {
|
|
314
|
+
const timeout = await increaseTimeout();
|
|
315
|
+
try {
|
|
316
|
+
await handler.waitForFunction((context) => {
|
|
317
|
+
return window.__ARGOS__.waitFor(context);
|
|
318
|
+
}, context, timeout ? { timeout: timeout.value } : void 0);
|
|
319
|
+
timeout?.reset();
|
|
320
|
+
} catch (error) {
|
|
321
|
+
const reasons = await handler.evaluate((context) => window.__ARGOS__.getWaitFailureExplanations(context), context);
|
|
322
|
+
throw new Error(`
|
|
323
|
+
Failed to stabilize screenshot, found the following issues:
|
|
324
|
+
${reasons.map((reason) => `- ${reason}`).join("\n")}
|
|
325
|
+
`.trim(), { cause: error });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Attach attachments to test info if necessary.
|
|
330
|
+
*/
|
|
331
|
+
async function attachAttachments(args) {
|
|
332
|
+
const { attachments, useArgosReporter, testInfo } = args;
|
|
333
|
+
if (useArgosReporter && testInfo) await Promise.all(attachments.map((attachment) => testInfo.attach(attachment.name, {
|
|
334
|
+
path: attachment.path,
|
|
335
|
+
contentType: attachment.contentType
|
|
336
|
+
})));
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/attachment.ts
|
|
340
|
+
function getAttachmentName(name, type) {
|
|
341
|
+
return `argos/${type}___${name}`;
|
|
342
|
+
}
|
|
343
|
+
//#endregion
|
|
344
|
+
//#region src/aria-snapshot.ts
|
|
345
|
+
const DEFAULT_SNAPSHOTS_ROOT = "./screenshots";
|
|
346
|
+
/**
|
|
347
|
+
* Stabilize the UI and takes a snapshot of the application under test.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* argosAriaSnapshot(page, "my-screenshot")
|
|
351
|
+
* @see https://playwright.dev/docs/aria-snapshots
|
|
352
|
+
*/
|
|
353
|
+
async function argosAriaSnapshot(handler, name, options = {}) {
|
|
354
|
+
const { element, has, hasText, hasNot, hasNotText, timeout, root = DEFAULT_SNAPSHOTS_ROOT } = options;
|
|
355
|
+
if (!handler) throw new Error("A Playwright `handler` object is required.");
|
|
356
|
+
if (!name) throw new Error("The `name` argument is required.");
|
|
357
|
+
const snapshotTarget = typeof element === "string" ? handler.locator(element, {
|
|
358
|
+
has,
|
|
359
|
+
hasText,
|
|
360
|
+
hasNot,
|
|
361
|
+
hasNotText
|
|
362
|
+
}) : element ?? handler.locator("body");
|
|
363
|
+
const testInfo = await getTestInfo();
|
|
364
|
+
const useArgosReporter = checkIsUsingArgosReporter(testInfo);
|
|
365
|
+
await prepare({
|
|
366
|
+
handler,
|
|
367
|
+
useArgosReporter,
|
|
368
|
+
root
|
|
369
|
+
});
|
|
370
|
+
const context = getStabilizationContext$1(options);
|
|
371
|
+
const afterAll = await beforeAll(handler, context);
|
|
372
|
+
const names = getSnapshotNames(name, testInfo);
|
|
373
|
+
const { path: snapshotPath, metadata } = await getPathAndMetadata({
|
|
374
|
+
handler,
|
|
375
|
+
extension: ARIA_EXTENSION,
|
|
376
|
+
names,
|
|
377
|
+
root,
|
|
378
|
+
testInfo,
|
|
379
|
+
useArgosReporter
|
|
380
|
+
});
|
|
381
|
+
await waitForReadiness(handler, context);
|
|
382
|
+
const afterEach = await beforeEach(handler, context);
|
|
383
|
+
await waitForReadiness(handler, context);
|
|
384
|
+
await Promise.all([snapshotTarget.ariaSnapshot({ timeout }).then((snapshot) => {
|
|
385
|
+
return writeFile(snapshotPath, snapshot, "utf-8");
|
|
386
|
+
}), writeMetadata(snapshotPath, metadata)]);
|
|
387
|
+
const attachments = [{
|
|
388
|
+
name: getAttachmentName(names.name, "aria"),
|
|
389
|
+
contentType: "application/yaml",
|
|
390
|
+
path: snapshotPath
|
|
391
|
+
}, {
|
|
392
|
+
name: getAttachmentName(names.name, "aria/metadata"),
|
|
393
|
+
contentType: "application/json",
|
|
394
|
+
path: getMetadataPath(snapshotPath)
|
|
395
|
+
}];
|
|
396
|
+
await attachAttachments({
|
|
397
|
+
attachments,
|
|
398
|
+
testInfo,
|
|
399
|
+
useArgosReporter
|
|
400
|
+
});
|
|
401
|
+
await afterEach();
|
|
402
|
+
await afterAll();
|
|
403
|
+
return attachments;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Get the stabilization context from the options.
|
|
407
|
+
*/
|
|
408
|
+
function getStabilizationContext$1(options) {
|
|
409
|
+
const { stabilize } = options;
|
|
410
|
+
return {
|
|
411
|
+
fullPage: false,
|
|
412
|
+
argosCSS: void 0,
|
|
413
|
+
viewports: void 0,
|
|
414
|
+
options: stabilize
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region src/screenshot.ts
|
|
419
|
+
const DEFAULT_SCREENSHOT_ROOT = "./screenshots";
|
|
420
|
+
/**
|
|
421
|
+
* Stabilize the UI and takes a screenshot of the application under test.
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* argosScreenshot(page, "my-screenshot")
|
|
425
|
+
* @see https://argos-ci.com/docs/playwright#api-overview
|
|
426
|
+
*/
|
|
427
|
+
async function argosScreenshot(handler, name, options = {}) {
|
|
428
|
+
const { element, has, hasText, hasNot, hasNotText, viewports, argosCSS: _argosCSS, root = DEFAULT_SCREENSHOT_ROOT, ariaSnapshot, disableHover = true, ...playwrightOptions } = options;
|
|
429
|
+
if (!handler) throw new Error("A Playwright `handler` object is required.");
|
|
430
|
+
if (!name) throw new Error("The `name` argument is required.");
|
|
431
|
+
const screenshotTarget = typeof element === "string" ? handler.locator(element, {
|
|
432
|
+
has,
|
|
433
|
+
hasText,
|
|
434
|
+
hasNot,
|
|
435
|
+
hasNotText
|
|
436
|
+
}) : element ?? (checkIsFrame(handler) ? handler.locator("body") : handler);
|
|
437
|
+
const testInfo = await getTestInfo();
|
|
438
|
+
const useArgosReporter = checkIsUsingArgosReporter(testInfo);
|
|
439
|
+
await prepare({
|
|
440
|
+
handler,
|
|
441
|
+
useArgosReporter,
|
|
442
|
+
root
|
|
443
|
+
});
|
|
444
|
+
const originalViewportSize = checkIsFrame(handler) ? null : getViewportSize(handler);
|
|
445
|
+
const fullPage = options.fullPage !== void 0 ? options.fullPage : screenshotTarget === handler;
|
|
446
|
+
const context = getStabilizationContext(options);
|
|
447
|
+
const afterAll = await beforeAll(handler, context, { disableHover });
|
|
448
|
+
const stabilizeAndScreenshot = async (name) => {
|
|
449
|
+
const names = getSnapshotNames(name, testInfo);
|
|
450
|
+
const { path: screenshotPath, metadata } = await getPathAndMetadata({
|
|
451
|
+
handler,
|
|
452
|
+
extension: PNG_EXTENSION,
|
|
453
|
+
root,
|
|
454
|
+
names,
|
|
455
|
+
testInfo,
|
|
456
|
+
useArgosReporter
|
|
457
|
+
});
|
|
458
|
+
if (options.tag) metadata.tags = Array.isArray(options.tag) ? options.tag : [options.tag];
|
|
459
|
+
if (options.threshold !== void 0) {
|
|
460
|
+
validateThreshold(options.threshold);
|
|
461
|
+
metadata.transient.threshold = options.threshold;
|
|
462
|
+
}
|
|
463
|
+
await options.beforeScreenshot?.({ runStabilization: (stabilizationOptions) => waitForReadiness(handler, getStabilizationContext({
|
|
464
|
+
...options,
|
|
465
|
+
stabilize: stabilizationOptions ?? options.stabilize
|
|
466
|
+
})) });
|
|
467
|
+
await waitForReadiness(handler, context);
|
|
468
|
+
const afterEach = await beforeEach(handler, context);
|
|
469
|
+
await waitForReadiness(handler, context);
|
|
470
|
+
const [snapshotPath] = await Promise.all([
|
|
471
|
+
(async () => {
|
|
472
|
+
if (!ariaSnapshot) return null;
|
|
473
|
+
const snapshotTarget = checkIsPage(screenshotTarget) ? screenshotTarget.locator("body") : screenshotTarget;
|
|
474
|
+
if (checkIsElementHandle(snapshotTarget)) throw new Error(`Element handle is not supported with "ariaSnapshot" option. Use a Locator instead.`);
|
|
475
|
+
const snapshotPath = screenshotToSnapshotPath(screenshotPath);
|
|
476
|
+
const snapshotMetadata = {
|
|
477
|
+
...metadata,
|
|
478
|
+
transient: {
|
|
479
|
+
parentName: `${names.name}${PNG_EXTENSION}`,
|
|
480
|
+
...metadata.transient.baseName ? { baseName: screenshotToSnapshotPath(metadata.transient.baseName) } : {}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
await Promise.all([snapshotTarget.ariaSnapshot().then((snapshot) => {
|
|
484
|
+
return writeFile(snapshotPath, snapshot, "utf-8");
|
|
485
|
+
}), writeMetadata(snapshotPath, snapshotMetadata)]);
|
|
486
|
+
return snapshotPath;
|
|
487
|
+
})(),
|
|
488
|
+
screenshotTarget.screenshot({
|
|
489
|
+
path: screenshotPath,
|
|
490
|
+
type: "png",
|
|
491
|
+
fullPage,
|
|
492
|
+
mask: [handler.locator("[data-visual-test=\"blackout\"]")],
|
|
493
|
+
animations: "disabled",
|
|
494
|
+
...playwrightOptions
|
|
495
|
+
}),
|
|
496
|
+
writeMetadata(screenshotPath, metadata)
|
|
497
|
+
]);
|
|
498
|
+
const attachments = [{
|
|
499
|
+
name: getAttachmentName(names.name, "screenshot"),
|
|
500
|
+
contentType: "image/png",
|
|
501
|
+
path: screenshotPath
|
|
502
|
+
}, {
|
|
503
|
+
name: getAttachmentName(names.name, "screenshot/metadata"),
|
|
504
|
+
contentType: "application/json",
|
|
505
|
+
path: getMetadataPath(screenshotPath)
|
|
506
|
+
}];
|
|
507
|
+
if (snapshotPath) attachments.push({
|
|
508
|
+
name: getAttachmentName(names.name, "aria"),
|
|
509
|
+
contentType: "application/yaml",
|
|
510
|
+
path: snapshotPath
|
|
511
|
+
}, {
|
|
512
|
+
name: getAttachmentName(names.name, "aria/metadata"),
|
|
513
|
+
contentType: "application/json",
|
|
514
|
+
path: getMetadataPath(snapshotPath)
|
|
515
|
+
});
|
|
516
|
+
await attachAttachments({
|
|
517
|
+
attachments,
|
|
518
|
+
testInfo,
|
|
519
|
+
useArgosReporter
|
|
520
|
+
});
|
|
521
|
+
await afterEach();
|
|
522
|
+
await options.afterScreenshot?.();
|
|
523
|
+
return attachments;
|
|
524
|
+
};
|
|
525
|
+
const allAttachments = [];
|
|
526
|
+
if (viewports) {
|
|
527
|
+
if (checkIsFrame(handler)) throw new Error(`viewports option is not supported with an iframe`);
|
|
528
|
+
for (const viewport of viewports) {
|
|
529
|
+
const viewportSize = resolveViewport(viewport);
|
|
530
|
+
await setViewportSize(handler, viewportSize);
|
|
531
|
+
const attachments = await stabilizeAndScreenshot(getScreenshotName(name, { viewportWidth: viewportSize.width }));
|
|
532
|
+
allAttachments.push(...attachments);
|
|
533
|
+
}
|
|
534
|
+
if (!originalViewportSize) throw new Error(`Invariant: viewport size must be saved`);
|
|
535
|
+
await setViewportSize(handler, originalViewportSize);
|
|
536
|
+
} else {
|
|
537
|
+
const attachments = await stabilizeAndScreenshot(name);
|
|
538
|
+
allAttachments.push(...attachments);
|
|
539
|
+
}
|
|
540
|
+
await afterAll();
|
|
541
|
+
return allAttachments;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Get the stabilization context from the options.
|
|
545
|
+
*/
|
|
546
|
+
function getStabilizationContext(options) {
|
|
547
|
+
const { fullPage, argosCSS, stabilize, viewports } = options;
|
|
548
|
+
return {
|
|
549
|
+
fullPage,
|
|
550
|
+
argosCSS,
|
|
551
|
+
viewports,
|
|
552
|
+
options: stabilize
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/csp.ts
|
|
557
|
+
/**
|
|
558
|
+
* Get the CSP script hash.
|
|
559
|
+
*/
|
|
560
|
+
function getCSPScriptHash() {
|
|
561
|
+
return `'sha256-${createHash("sha256").update(getGlobalScript()).digest("base64")}'`;
|
|
562
|
+
}
|
|
563
|
+
//#endregion
|
|
564
|
+
export { setMetadataConfig as DO_NOT_USE_setMetadataConfig, argosAriaSnapshot, argosScreenshot, getCSPScriptHash };
|