@percy/playwright 1.0.9 → 1.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.
Files changed (2) hide show
  1. package/index.js +76 -1
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -8,6 +8,45 @@ const CLIENT_INFO = `${sdkPkg.name}/${sdkPkg.version}`;
8
8
  const ENV_INFO = `${playwrightPkg.name}/${playwrightPkg.version}`;
9
9
  const log = utils.logger('playwright');
10
10
 
11
+ // Processes a single cross-origin frame to capture its snapshot and resources.
12
+ async function processFrame(page, frame, options, percyDOM) {
13
+ const frameUrl = frame.url();
14
+
15
+ /* istanbul ignore next: browser-executed iframe serialization */
16
+ // enableJavaScript: true prevents the standard iframe serialization logic from running.
17
+ // This is necessary because we're manually handling cross-origin iframe serialization here.
18
+ const iframeSnapshot = await frame.evaluate((opts) => {
19
+ /* eslint-disable-next-line no-undef */
20
+ return PercyDOM.serialize(opts);
21
+ }, { ...options, enableJavascript: true });
22
+
23
+ // Create a new resource for the iframe's HTML
24
+ const iframeResource = {
25
+ url: frameUrl,
26
+ content: iframeSnapshot.html,
27
+ mimetype: 'text/html'
28
+ };
29
+
30
+ // Get the iframe's element data from the main page context
31
+ /* istanbul ignore next: browser-executed evaluation function */
32
+ const iframeData = await page.evaluate((fUrl) => {
33
+ const iframes = Array.from(document.querySelectorAll('iframe'));
34
+ const matchingIframe = iframes.find(iframe => iframe.src.startsWith(fUrl));
35
+ if (matchingIframe) {
36
+ return {
37
+ percyElementId: matchingIframe.getAttribute('data-percy-element-id')
38
+ };
39
+ }
40
+ }, frameUrl);
41
+
42
+ return {
43
+ iframeData,
44
+ iframeResource,
45
+ iframeSnapshot,
46
+ frameUrl
47
+ };
48
+ }
49
+
11
50
  // Take a DOM snapshot and post it to the snapshot endpoint
12
51
  const percySnapshot = async function(page, name, options) {
13
52
  if (!page) throw new Error('A Playwright `page` object is required.');
@@ -16,7 +55,8 @@ const percySnapshot = async function(page, name, options) {
16
55
 
17
56
  try {
18
57
  // Inject the DOM serialization script
19
- await page.evaluate(await utils.fetchPercyDOM());
58
+ const percyDOM = await utils.fetchPercyDOM();
59
+ await page.evaluate(percyDOM);
20
60
 
21
61
  // Serialize and capture the DOM
22
62
  /* istanbul ignore next: no instrumenting injected code */
@@ -25,6 +65,41 @@ const percySnapshot = async function(page, name, options) {
25
65
  return PercyDOM.serialize(options);
26
66
  }, options);
27
67
 
68
+ // Process CORS IFrames
69
+ // Note: Blob URL handling (data-src images, blob background images) is now handled
70
+ // in the CLI via async DOM serialization. See: percy/cli packages/dom/src/serialize-blob-urls.js
71
+ // This section only handles cross-origin iframe serialization and resource merging.
72
+ const pageUrl = new URL(page.url());
73
+ const crossOriginFrames = page.frames()
74
+ .filter(frame => frame.url() !== 'about:blank' && new URL(frame.url()).origin !== pageUrl.origin);
75
+
76
+ // Inject Percy DOM into all cross-origin frames before processing them in parallel
77
+ await Promise.all(crossOriginFrames.map(frame => frame.evaluate(percyDOM)));
78
+
79
+ const processedFrames = await Promise.all(
80
+ crossOriginFrames.map(frame => processFrame(page, frame, options, percyDOM))
81
+ );
82
+
83
+ for (const { iframeData, iframeResource, iframeSnapshot, frameUrl } of processedFrames) {
84
+ // Add the iframe's own resources to the main snapshot
85
+ domSnapshot.resources.push(...iframeSnapshot.resources);
86
+ // Add the iframe HTML resource itself
87
+ domSnapshot.resources.push(iframeResource);
88
+
89
+ if (iframeData && iframeData.percyElementId) {
90
+ const regex = new RegExp(`(<iframe[^>]*data-percy-element-id=["']${iframeData.percyElementId}["'][^>]*>)`);
91
+ const match = domSnapshot.html.match(regex);
92
+
93
+ /* istanbul ignore next: iframe matching logic depends on DOM structure */
94
+ if (match) {
95
+ const iframeTag = match[1];
96
+ // Replace the original iframe tag with one that points to the new resource.
97
+ const newIframeTag = iframeTag.replace(/src="[^"]*"/i, `src="${frameUrl}"`);
98
+ domSnapshot.html = domSnapshot.html.replace(iframeTag, newIframeTag);
99
+ }
100
+ }
101
+ }
102
+
28
103
  domSnapshot.cookies = await page.context().cookies();
29
104
 
30
105
  // Post the DOM to the snapshot endpoint with snapshot options and other info
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@percy/playwright",
3
3
  "description": "Playwright client library for visual testing with Percy",
4
- "version": "1.0.9",
4
+ "version": "1.0.10",
5
5
  "license": "MIT",
6
6
  "author": "Perceptual Inc.",
7
7
  "repository": "https://github.com/percy/percy-playwright",