@argos-ci/playwright 5.0.9 → 5.0.10

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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ElementHandle, Locator, Page, PageScreenshotOptions, LocatorScreenshotOptions } from '@playwright/test';
1
+ import { ElementHandle, Locator, Page, PageScreenshotOptions, LocatorScreenshotOptions, Frame } from '@playwright/test';
2
2
  import { ViewportOption, StabilizationPluginOptions } from '@argos-ci/browser';
3
3
  import { ScreenshotMetadata } from '@argos-ci/util';
4
4
 
@@ -19,7 +19,7 @@ type ArgosScreenshotOptions = {
19
19
  */
20
20
  argosCSS?: string;
21
21
  /**
22
- * Disable hover effects by moving the mouse to the top-left corner of the page.
22
+ * Disable hover effects by moving the mouse to the top-left corner of the document.
23
23
  * @default true
24
24
  */
25
25
  disableHover?: boolean;
@@ -59,6 +59,12 @@ type ArgosScreenshotOptions = {
59
59
  */
60
60
  afterScreenshot?: () => Promise<void> | void;
61
61
  } & LocatorOptions & ScreenshotOptions<LocatorScreenshotOptions> & ScreenshotOptions<PageScreenshotOptions>;
62
+ type Attachment = {
63
+ name: string;
64
+ contentType: string;
65
+ path: string;
66
+ };
67
+ type Handler = Page | Frame;
62
68
  /**
63
69
  * Stabilize the UI and takes a screenshot of the application under test.
64
70
  *
@@ -68,9 +74,9 @@ type ArgosScreenshotOptions = {
68
74
  */
69
75
  declare function argosScreenshot(
70
76
  /**
71
- * Playwright `page` object.
77
+ * Playwright `page` or `frame` object.
72
78
  */
73
- page: Page,
79
+ handler: Handler,
74
80
  /**
75
81
  * Name of the screenshot. Must be unique.
76
82
  */
@@ -78,7 +84,7 @@ name: string,
78
84
  /**
79
85
  * Options for the screenshot.
80
86
  */
81
- options?: ArgosScreenshotOptions): Promise<void>;
87
+ options?: ArgosScreenshotOptions): Promise<Attachment[]>;
82
88
 
83
89
  /**
84
90
  * Get the CSP script hash.
@@ -88,10 +94,13 @@ declare function getCSPScriptHash(): string;
88
94
  type MetadataConfig = {
89
95
  sdk: ScreenshotMetadata["sdk"];
90
96
  playwrightLibraries: string[];
97
+ url?: string;
98
+ test?: ScreenshotMetadata["test"];
99
+ viewport?: ScreenshotMetadata["viewport"];
91
100
  };
92
101
  /**
93
102
  * Set the metadata config.
94
103
  */
95
104
  declare function setMetadataConfig(metadata: MetadataConfig): void;
96
105
 
97
- export { type ArgosScreenshotOptions, setMetadataConfig as DO_NOT_USE_setMetadataConfig, argosScreenshot, getCSPScriptHash };
106
+ export { type ArgosScreenshotOptions, type Attachment, setMetadataConfig as DO_NOT_USE_setMetadataConfig, type MetadataConfig, argosScreenshot, getCSPScriptHash };
package/dist/index.js CHANGED
@@ -42,6 +42,9 @@ var DEFAULT_PLAYWRIGHT_LIBRARIES = [
42
42
  "playwright",
43
43
  "playwright-core"
44
44
  ];
45
+ function getMetadataOverrides() {
46
+ return metadataConfigStorage.getStore();
47
+ }
45
48
  async function getAutomationLibraryMetadata() {
46
49
  const metadataConfig = metadataConfigStorage.getStore();
47
50
  const libraries = metadataConfig?.playwrightLibraries ?? DEFAULT_PLAYWRIGHT_LIBRARIES;
@@ -81,8 +84,31 @@ async function getLibraryMetadata() {
81
84
  sdk
82
85
  };
83
86
  }
84
- async function getTestMetadataFromTestInfo(testInfo) {
87
+ function resolveTestFilePath(filepath, repositoryPath) {
88
+ if (!repositoryPath) {
89
+ return filepath;
90
+ }
91
+ return relative(repositoryPath, filepath);
92
+ }
93
+ async function getTestMetadata(testInfo) {
85
94
  const repositoryPath = await getGitRepositoryPath();
95
+ const metadataConfig = metadataConfigStorage.getStore();
96
+ if (metadataConfig?.test) {
97
+ return {
98
+ ...metadataConfig.test,
99
+ location: metadataConfig.test?.location ? {
100
+ file: resolveTestFilePath(
101
+ metadataConfig.test.location.file,
102
+ repositoryPath
103
+ ),
104
+ line: metadataConfig.test.location.line,
105
+ column: metadataConfig.test.location.column
106
+ } : void 0
107
+ };
108
+ }
109
+ if (!testInfo) {
110
+ return null;
111
+ }
86
112
  const testMetadata = {
87
113
  id: testInfo.testId,
88
114
  title: testInfo.title,
@@ -91,7 +117,7 @@ async function getTestMetadataFromTestInfo(testInfo) {
91
117
  retries: testInfo.project.retries,
92
118
  repeat: testInfo.repeatEachIndex,
93
119
  location: {
94
- file: repositoryPath ? relative(repositoryPath, testInfo.file) : testInfo.file,
120
+ file: resolveTestFilePath(testInfo.file, repositoryPath),
95
121
  line: testInfo.line,
96
122
  column: testInfo.column
97
123
  }
@@ -111,12 +137,12 @@ function checkIsUsingArgosReporter(testInfo) {
111
137
 
112
138
  // src/screenshot.ts
113
139
  var DEFAULT_SCREENSHOT_ROOT = "./screenshots";
114
- async function injectArgos(page) {
115
- const injected = await page.evaluate(
140
+ async function injectArgos(handler) {
141
+ const injected = await handler.evaluate(
116
142
  () => typeof window.__ARGOS__ !== "undefined"
117
143
  );
118
144
  if (!injected) {
119
- await page.addScriptTag({ content: getGlobalScript() });
145
+ await handler.addScriptTag({ content: getGlobalScript() });
120
146
  }
121
147
  }
122
148
  async function getTestInfo() {
@@ -127,6 +153,15 @@ async function getTestInfo() {
127
153
  return null;
128
154
  }
129
155
  }
156
+ function checkIsFrame(handler) {
157
+ return "page" in handler && typeof handler.page === "function";
158
+ }
159
+ function getPage(handler) {
160
+ if (checkIsFrame(handler)) {
161
+ return handler.page();
162
+ }
163
+ return handler;
164
+ }
130
165
  function getViewportSize(page) {
131
166
  const viewportSize = page.viewportSize();
132
167
  if (!viewportSize) {
@@ -150,30 +185,30 @@ function getStabilizationContext(options) {
150
185
  options: stabilize
151
186
  };
152
187
  }
153
- async function beforeAll(page, options) {
188
+ async function beforeAll(handler, options) {
154
189
  const { disableHover = true } = options;
155
190
  const context = getStabilizationContext(options);
156
- await page.evaluate(
191
+ await handler.evaluate(
157
192
  (context2) => window.__ARGOS__.beforeAll(context2),
158
193
  context
159
194
  );
160
195
  if (disableHover) {
161
- await page.mouse.move(0, 0);
196
+ await getPage(handler).mouse.move(0, 0);
162
197
  }
163
198
  return async () => {
164
- await page.evaluate(
199
+ await handler.evaluate(
165
200
  () => window.__ARGOS__.afterAll()
166
201
  );
167
202
  };
168
203
  }
169
- async function beforeEach(page, options) {
204
+ async function beforeEach(handler, options) {
170
205
  const context = getStabilizationContext(options);
171
- await page.evaluate(
206
+ await handler.evaluate(
172
207
  (context2) => window.__ARGOS__.beforeEach(context2),
173
208
  context
174
209
  );
175
210
  return async () => {
176
- await page.evaluate(
211
+ await handler.evaluate(
177
212
  () => window.__ARGOS__.afterEach()
178
213
  );
179
214
  };
@@ -192,11 +227,11 @@ async function increaseTimeout() {
192
227
  }
193
228
  return null;
194
229
  }
195
- async function waitForReadiness(page, options) {
230
+ async function waitForReadiness(handler, options) {
196
231
  const context = getStabilizationContext(options);
197
232
  const timeout = await increaseTimeout();
198
233
  try {
199
- await page.waitForFunction(
234
+ await handler.waitForFunction(
200
235
  (context2) => {
201
236
  const argos = window.__ARGOS__;
202
237
  return argos.waitFor(context2);
@@ -206,7 +241,7 @@ async function waitForReadiness(page, options) {
206
241
  );
207
242
  timeout?.reset();
208
243
  } catch (error) {
209
- const reasons = await page.evaluate(
244
+ const reasons = await handler.evaluate(
210
245
  (context2) => window.__ARGOS__.getWaitFailureExplanations(
211
246
  context2
212
247
  ),
@@ -234,7 +269,7 @@ function getScreenshotNames(name, testInfo) {
234
269
  }
235
270
  return { name, baseName: null };
236
271
  }
237
- async function argosScreenshot(page, name, options = {}) {
272
+ async function argosScreenshot(handler, name, options = {}) {
238
273
  const {
239
274
  element,
240
275
  has,
@@ -244,13 +279,13 @@ async function argosScreenshot(page, name, options = {}) {
244
279
  root = DEFAULT_SCREENSHOT_ROOT,
245
280
  ...playwrightOptions
246
281
  } = options;
247
- if (!page) {
248
- throw new Error("A Playwright `page` object is required.");
282
+ if (!handler) {
283
+ throw new Error("A Playwright `handler` object is required.");
249
284
  }
250
285
  if (!name) {
251
286
  throw new Error("The `name` argument is required.");
252
287
  }
253
- const handle = typeof element === "string" ? page.locator(element, { has, hasText }) : element ?? page;
288
+ const screenshotTarget = typeof element === "string" ? handler.locator(element, { has, hasText }) : element ?? (checkIsFrame(handler) ? handler.locator("body") : handler);
254
289
  const testInfo = await getTestInfo();
255
290
  const useArgosReporter = Boolean(
256
291
  testInfo && checkIsUsingArgosReporter(testInfo)
@@ -259,33 +294,33 @@ async function argosScreenshot(page, name, options = {}) {
259
294
  // Create the screenshot folder if it doesn't exist
260
295
  useArgosReporter ? null : mkdir(root, { recursive: true }),
261
296
  // Inject Argos script into the page
262
- injectArgos(page)
297
+ injectArgos(handler)
263
298
  ]);
264
- const originalViewportSize = getViewportSize(page);
265
- const fullPage = options.fullPage !== void 0 ? options.fullPage : handle === page;
266
- const afterAll = await beforeAll(page, options);
299
+ const originalViewportSize = checkIsFrame(handler) ? null : getViewportSize(handler);
300
+ const fullPage = options.fullPage !== void 0 ? options.fullPage : screenshotTarget === handler;
301
+ const afterAll = await beforeAll(handler, options);
267
302
  const collectMetadata = async (testInfo2) => {
303
+ const overrides = getMetadataOverrides();
268
304
  const [colorScheme, mediaType, libMetadata, testMetadata] = await Promise.all([
269
- page.evaluate(
305
+ handler.evaluate(
270
306
  () => window.__ARGOS__.getColorScheme()
271
307
  ),
272
- page.evaluate(
308
+ handler.evaluate(
273
309
  () => window.__ARGOS__.getMediaType()
274
310
  ),
275
311
  getLibraryMetadata(),
276
- testInfo2 ? getTestMetadataFromTestInfo(testInfo2) : null
312
+ getTestMetadata(testInfo2)
277
313
  ]);
278
- const viewportSize = getViewportSize(page);
279
- const browser = page.context().browser();
314
+ const viewportSize = checkIsFrame(handler) ? null : getViewportSize(handler);
315
+ const browser = getPage(handler).context().browser();
280
316
  if (!browser) {
281
317
  throw new Error("Can't take screenshots without a browser.");
282
318
  }
283
319
  const browserName = browser.browserType().name();
284
320
  const browserVersion = browser.version();
285
- const url = page.url();
321
+ const url = overrides?.url ?? handler.url();
286
322
  const metadata = {
287
323
  url,
288
- viewport: viewportSize,
289
324
  colorScheme,
290
325
  mediaType,
291
326
  test: testMetadata,
@@ -295,6 +330,10 @@ async function argosScreenshot(page, name, options = {}) {
295
330
  },
296
331
  ...libMetadata
297
332
  };
333
+ const viewport = viewportSize ?? getMetadataOverrides()?.viewport;
334
+ if (viewport) {
335
+ metadata.viewport = viewport;
336
+ }
298
337
  return metadata;
299
338
  };
300
339
  const stabilizeAndScreenshot = async (name2) => {
@@ -314,53 +353,74 @@ async function argosScreenshot(page, name, options = {}) {
314
353
  await mkdir(dirname(screenshotPath), { recursive: true });
315
354
  }
316
355
  await options.beforeScreenshot?.({
317
- runStabilization: (stabilizationOptions) => waitForReadiness(page, {
356
+ runStabilization: (stabilizationOptions) => waitForReadiness(handler, {
318
357
  ...options,
319
358
  stabilize: stabilizationOptions ?? options.stabilize
320
359
  })
321
360
  });
322
- await waitForReadiness(page, options);
323
- const afterEach = await beforeEach(page, options);
324
- await waitForReadiness(page, options);
361
+ await waitForReadiness(handler, options);
362
+ const afterEach = await beforeEach(handler, options);
363
+ await waitForReadiness(handler, options);
325
364
  await Promise.all([
326
- handle.screenshot({
365
+ screenshotTarget.screenshot({
327
366
  path: screenshotPath,
328
367
  type: "png",
329
368
  fullPage,
330
- mask: [page.locator('[data-visual-test="blackout"]')],
369
+ mask: [handler.locator('[data-visual-test="blackout"]')],
331
370
  animations: "disabled",
332
371
  ...playwrightOptions
333
372
  }),
334
373
  writeMetadata(screenshotPath, metadata)
335
374
  ]);
375
+ const attachments = [
376
+ {
377
+ name: getAttachmentName(names.name, "screenshot"),
378
+ contentType: "image/png",
379
+ path: screenshotPath
380
+ },
381
+ {
382
+ name: getAttachmentName(names.name, "metadata"),
383
+ contentType: "application/json",
384
+ path: getMetadataPath(screenshotPath)
385
+ }
386
+ ];
336
387
  if (useArgosReporter && testInfo) {
337
- await Promise.all([
338
- testInfo.attach(getAttachmentName(names.name, "metadata"), {
339
- path: getMetadataPath(screenshotPath),
340
- contentType: "application/json"
341
- }),
342
- testInfo.attach(getAttachmentName(names.name, "screenshot"), {
343
- path: screenshotPath,
344
- contentType: "image/png"
345
- })
346
- ]);
388
+ await Promise.all(
389
+ attachments.map(
390
+ (attachment) => testInfo.attach(attachment.name, {
391
+ path: attachment.path,
392
+ contentType: attachment.contentType
393
+ })
394
+ )
395
+ );
347
396
  }
348
397
  await afterEach();
349
398
  await options.afterScreenshot?.();
399
+ return attachments;
350
400
  };
401
+ const allAttachments = [];
351
402
  if (viewports) {
403
+ if (checkIsFrame(handler)) {
404
+ throw new Error(`viewports option is not supported with an iframe`);
405
+ }
352
406
  for (const viewport of viewports) {
353
407
  const viewportSize = resolveViewport(viewport);
354
- await setViewportSize(page, viewportSize);
355
- await stabilizeAndScreenshot(
408
+ await setViewportSize(handler, viewportSize);
409
+ const attachments = await stabilizeAndScreenshot(
356
410
  getScreenshotName(name, { viewportWidth: viewportSize.width })
357
411
  );
412
+ allAttachments.push(...attachments);
413
+ }
414
+ if (!originalViewportSize) {
415
+ throw new Error(`Invariant: viewport size must be saved`);
358
416
  }
359
- await setViewportSize(page, originalViewportSize);
417
+ await setViewportSize(handler, originalViewportSize);
360
418
  } else {
361
- await stabilizeAndScreenshot(name);
419
+ const attachments = await stabilizeAndScreenshot(name);
420
+ allAttachments.push(...attachments);
362
421
  }
363
422
  await afterAll();
423
+ return allAttachments;
364
424
  }
365
425
 
366
426
  // src/csp.ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@argos-ci/playwright",
3
3
  "description": "Playwright SDK for visual testing with Argos.",
4
- "version": "5.0.9",
4
+ "version": "5.0.10",
5
5
  "author": "Smooth Code",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -47,9 +47,9 @@
47
47
  "node": ">=18.16.0"
48
48
  },
49
49
  "dependencies": {
50
- "@argos-ci/browser": "4.2.0",
51
- "@argos-ci/core": "3.2.1",
52
- "@argos-ci/util": "2.3.2",
50
+ "@argos-ci/browser": "4.2.1",
51
+ "@argos-ci/core": "3.2.2",
52
+ "@argos-ci/util": "2.3.3",
53
53
  "chalk": "^5.4.1",
54
54
  "debug": "^4.4.0"
55
55
  },
@@ -67,5 +67,5 @@
67
67
  "check-format": "prettier --check --ignore-unknown --ignore-path=../../.gitignore --ignore-path=../../.prettierignore .",
68
68
  "lint": "eslint ."
69
69
  },
70
- "gitHead": "899956c05cad3211eb2dd030a214a2e0af12e27d"
70
+ "gitHead": "ecab2281c36828d0c43728e3ed98e1eefe227c99"
71
71
  }