@percy/dom 1.28.9 → 1.29.0-beta.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/bundle.js +266 -210
- package/package.json +3 -3
package/dist/bundle.js
CHANGED
|
@@ -6,48 +6,124 @@
|
|
|
6
6
|
process.env = process.env || {};
|
|
7
7
|
process.env.__PERCY_BROWSERIFIED__ = true;
|
|
8
8
|
|
|
9
|
+
// Creates a resource object from an element's unique ID and data URL
|
|
10
|
+
function resourceFromDataURL(uid, dataURL) {
|
|
11
|
+
// split dataURL into desired parts
|
|
12
|
+
let [data, content] = dataURL.split(',');
|
|
13
|
+
let [, mimetype] = data.split(':');
|
|
14
|
+
[mimetype] = mimetype.split(';');
|
|
15
|
+
|
|
16
|
+
// build a URL for the serialized asset
|
|
17
|
+
let [, ext] = mimetype.split('/');
|
|
18
|
+
let path = `/__serialized__/${uid}.${ext}`;
|
|
19
|
+
let url = rewriteLocalhostURL(new URL(path, document.URL).toString());
|
|
20
|
+
|
|
21
|
+
// return the url, base64 content, and mimetype
|
|
22
|
+
return {
|
|
23
|
+
url,
|
|
24
|
+
content,
|
|
25
|
+
mimetype
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function resourceFromText(uid, mimetype, data) {
|
|
29
|
+
// build a URL for the serialized asset
|
|
30
|
+
let [, ext] = mimetype.split('/');
|
|
31
|
+
let path = `/__serialized__/${uid}.${ext}`;
|
|
32
|
+
let url = rewriteLocalhostURL(new URL(path, document.URL).toString());
|
|
33
|
+
// return the url, text content, and mimetype
|
|
34
|
+
return {
|
|
35
|
+
url,
|
|
36
|
+
content: data,
|
|
37
|
+
mimetype
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function styleSheetFromNode(node) {
|
|
41
|
+
/* istanbul ignore if: sanity check */
|
|
42
|
+
if (node.sheet) return node.sheet;
|
|
43
|
+
|
|
44
|
+
// Cloned style nodes don't have a sheet instance unless they are within
|
|
45
|
+
// a document; we get it by temporarily adding the rules to DOM
|
|
46
|
+
const tempStyle = node.cloneNode();
|
|
47
|
+
tempStyle.setAttribute('data-percy-style-helper', '');
|
|
48
|
+
tempStyle.innerHTML = node.innerHTML;
|
|
49
|
+
const clone = document.cloneNode();
|
|
50
|
+
clone.appendChild(tempStyle);
|
|
51
|
+
const sheet = tempStyle.sheet;
|
|
52
|
+
// Cleanup node
|
|
53
|
+
tempStyle.remove();
|
|
54
|
+
return sheet;
|
|
55
|
+
}
|
|
56
|
+
function rewriteLocalhostURL(url) {
|
|
57
|
+
return url.replace(/(http[s]{0,1}:\/\/)(localhost|127.0.0.1)[:\d+]*/, '$1render.percy.local');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Utility function to handle errors
|
|
61
|
+
function handleErrors(error, prefixMessage) {
|
|
62
|
+
let element = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
63
|
+
let additionalData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
64
|
+
let elementData = {};
|
|
65
|
+
if (element) {
|
|
66
|
+
elementData = {
|
|
67
|
+
nodeName: element.nodeName,
|
|
68
|
+
classNames: element.className,
|
|
69
|
+
id: element.id
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
additionalData = {
|
|
73
|
+
...additionalData,
|
|
74
|
+
...elementData
|
|
75
|
+
};
|
|
76
|
+
error.message += `\n${prefixMessage} \n${JSON.stringify(additionalData)}`;
|
|
77
|
+
error.message += '\n Please validate that your DOM is as per W3C standards using any online tool';
|
|
78
|
+
error.handled = true;
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
|
|
9
82
|
// Translates JavaScript properties of inputs into DOM attributes.
|
|
10
|
-
function serializeInputElements(
|
|
83
|
+
function serializeInputElements(ctx) {
|
|
11
84
|
let {
|
|
12
85
|
dom,
|
|
13
|
-
clone
|
|
14
|
-
|
|
15
|
-
} = _ref;
|
|
86
|
+
clone
|
|
87
|
+
} = ctx;
|
|
16
88
|
for (let elem of dom.querySelectorAll('input, textarea, select')) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
89
|
+
try {
|
|
90
|
+
let inputId = elem.getAttribute('data-percy-element-id');
|
|
91
|
+
let cloneEl = clone.querySelector(`[data-percy-element-id="${inputId}"]`);
|
|
92
|
+
switch (elem.type) {
|
|
93
|
+
case 'checkbox':
|
|
94
|
+
case 'radio':
|
|
95
|
+
/*
|
|
96
|
+
here we are removing the checked attr if present by default,
|
|
97
|
+
so that only the current selected radio-button will have the checked attr present in the dom
|
|
98
|
+
this happens because in html,
|
|
99
|
+
when the checked attribute is present in the multiple radio-buttons for which only one can be selected at a time,
|
|
100
|
+
the browser will only render the last checked radio-button by default,
|
|
101
|
+
when a user selects any particular radio-button, the checked attribute on other buttons is not removed,
|
|
102
|
+
hence sometimes it shows inconsistent state as html will still show the last radio as selected.
|
|
103
|
+
*/
|
|
104
|
+
cloneEl.removeAttribute('checked');
|
|
105
|
+
if (elem.checked) {
|
|
106
|
+
cloneEl.setAttribute('checked', '');
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
case 'select-one':
|
|
110
|
+
if (elem.selectedIndex !== -1) {
|
|
111
|
+
cloneEl.options[elem.selectedIndex].setAttribute('selected', 'true');
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
case 'select-multiple':
|
|
115
|
+
for (let option of elem.selectedOptions) {
|
|
116
|
+
cloneEl.options[option.index].setAttribute('selected', 'true');
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case 'textarea':
|
|
120
|
+
cloneEl.innerHTML = elem.value;
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
cloneEl.setAttribute('value', elem.value);
|
|
124
|
+
}
|
|
125
|
+
} catch (err) {
|
|
126
|
+
handleErrors(err, 'Error serializing input element: ', elem);
|
|
51
127
|
}
|
|
52
128
|
}
|
|
53
129
|
}
|
|
@@ -117,57 +193,6 @@
|
|
|
117
193
|
}
|
|
118
194
|
}
|
|
119
195
|
|
|
120
|
-
// Creates a resource object from an element's unique ID and data URL
|
|
121
|
-
function resourceFromDataURL(uid, dataURL) {
|
|
122
|
-
// split dataURL into desired parts
|
|
123
|
-
let [data, content] = dataURL.split(',');
|
|
124
|
-
let [, mimetype] = data.split(':');
|
|
125
|
-
[mimetype] = mimetype.split(';');
|
|
126
|
-
|
|
127
|
-
// build a URL for the serialized asset
|
|
128
|
-
let [, ext] = mimetype.split('/');
|
|
129
|
-
let path = `/__serialized__/${uid}.${ext}`;
|
|
130
|
-
let url = rewriteLocalhostURL(new URL(path, document.URL).toString());
|
|
131
|
-
|
|
132
|
-
// return the url, base64 content, and mimetype
|
|
133
|
-
return {
|
|
134
|
-
url,
|
|
135
|
-
content,
|
|
136
|
-
mimetype
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function resourceFromText(uid, mimetype, data) {
|
|
140
|
-
// build a URL for the serialized asset
|
|
141
|
-
let [, ext] = mimetype.split('/');
|
|
142
|
-
let path = `/__serialized__/${uid}.${ext}`;
|
|
143
|
-
let url = rewriteLocalhostURL(new URL(path, document.URL).toString());
|
|
144
|
-
// return the url, text content, and mimetype
|
|
145
|
-
return {
|
|
146
|
-
url,
|
|
147
|
-
content: data,
|
|
148
|
-
mimetype
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
function styleSheetFromNode(node) {
|
|
152
|
-
/* istanbul ignore if: sanity check */
|
|
153
|
-
if (node.sheet) return node.sheet;
|
|
154
|
-
|
|
155
|
-
// Cloned style nodes don't have a sheet instance unless they are within
|
|
156
|
-
// a document; we get it by temporarily adding the rules to DOM
|
|
157
|
-
const tempStyle = node.cloneNode();
|
|
158
|
-
tempStyle.setAttribute('data-percy-style-helper', '');
|
|
159
|
-
tempStyle.innerHTML = node.innerHTML;
|
|
160
|
-
const clone = document.cloneNode();
|
|
161
|
-
clone.appendChild(tempStyle);
|
|
162
|
-
const sheet = tempStyle.sheet;
|
|
163
|
-
// Cleanup node
|
|
164
|
-
tempStyle.remove();
|
|
165
|
-
return sheet;
|
|
166
|
-
}
|
|
167
|
-
function rewriteLocalhostURL(url) {
|
|
168
|
-
return url.replace(/(http[s]{0,1}:\/\/)(localhost|127.0.0.1)[:\d+]*/, '$1render.percy.local');
|
|
169
|
-
}
|
|
170
|
-
|
|
171
196
|
// Returns a mostly random uid.
|
|
172
197
|
function uid() {
|
|
173
198
|
return `_${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -211,14 +236,14 @@
|
|
|
211
236
|
let resource = resourceFromText(uid(), 'text/css', styles);
|
|
212
237
|
return resource;
|
|
213
238
|
}
|
|
214
|
-
function serializeCSSOM(
|
|
239
|
+
function serializeCSSOM(ctx) {
|
|
215
240
|
let {
|
|
216
241
|
dom,
|
|
217
242
|
clone,
|
|
218
243
|
resources,
|
|
219
244
|
cache,
|
|
220
245
|
warnings
|
|
221
|
-
} =
|
|
246
|
+
} = ctx;
|
|
222
247
|
// in-memory CSSOM into their respective DOM nodes.
|
|
223
248
|
let styleSheets = null;
|
|
224
249
|
// catch error in case styleSheets property is not available (overwritten to throw error)
|
|
@@ -231,30 +256,44 @@
|
|
|
231
256
|
for (let styleSheet of styleSheets) {
|
|
232
257
|
var _styleSheet$href;
|
|
233
258
|
if (isCSSOM(styleSheet)) {
|
|
234
|
-
let styleId
|
|
235
|
-
let cloneOwnerNode
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
259
|
+
let styleId;
|
|
260
|
+
let cloneOwnerNode;
|
|
261
|
+
try {
|
|
262
|
+
styleId = styleSheet.ownerNode.getAttribute('data-percy-element-id');
|
|
263
|
+
cloneOwnerNode = clone.querySelector(`[data-percy-element-id="${styleId}"]`);
|
|
264
|
+
if (styleSheetsMatch(styleSheet, styleSheetFromNode(cloneOwnerNode))) continue;
|
|
265
|
+
let style = document.createElement('style');
|
|
266
|
+
style.type = 'text/css';
|
|
267
|
+
style.setAttribute('data-percy-element-id', styleId);
|
|
268
|
+
style.setAttribute('data-percy-cssom-serialized', 'true');
|
|
269
|
+
style.innerHTML = Array.from(styleSheet.cssRules).map(cssRule => cssRule.cssText).join('\n');
|
|
270
|
+
cloneOwnerNode.parentNode.insertBefore(style, cloneOwnerNode.nextSibling);
|
|
271
|
+
cloneOwnerNode.remove();
|
|
272
|
+
} catch (err) {
|
|
273
|
+
handleErrors(err, 'Error serializing stylesheet: ', cloneOwnerNode, {
|
|
274
|
+
styleId: styleId
|
|
275
|
+
});
|
|
276
|
+
}
|
|
244
277
|
} else if ((_styleSheet$href = styleSheet.href) !== null && _styleSheet$href !== void 0 && _styleSheet$href.startsWith('blob:')) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
clone.
|
|
278
|
+
try {
|
|
279
|
+
const styleLink = document.createElement('link');
|
|
280
|
+
styleLink.setAttribute('rel', 'stylesheet');
|
|
281
|
+
let resource = createStyleResource(styleSheet);
|
|
282
|
+
resources.add(resource);
|
|
283
|
+
styleLink.setAttribute('data-percy-blob-stylesheets-serialized', 'true');
|
|
284
|
+
styleLink.setAttribute('data-percy-serialized-attribute-href', resource.url);
|
|
285
|
+
|
|
286
|
+
/* istanbul ignore next: tested, but coverage is stripped */
|
|
287
|
+
if (clone.constructor.name === 'HTMLDocument' || clone.constructor.name === 'DocumentFragment') {
|
|
288
|
+
// handle document and iframe
|
|
289
|
+
clone.body.prepend(styleLink);
|
|
290
|
+
} else if (clone.constructor.name === 'ShadowRoot') {
|
|
291
|
+
clone.prepend(styleLink);
|
|
292
|
+
}
|
|
293
|
+
} catch (err) {
|
|
294
|
+
handleErrors(err, 'Error serializing stylesheet from blob: ', null, {
|
|
295
|
+
stylesheetHref: styleSheet.href
|
|
296
|
+
});
|
|
258
297
|
}
|
|
259
298
|
}
|
|
260
299
|
}
|
|
@@ -289,90 +328,98 @@
|
|
|
289
328
|
}
|
|
290
329
|
|
|
291
330
|
// Serialize in-memory canvas elements into images.
|
|
292
|
-
function serializeCanvas(
|
|
331
|
+
function serializeCanvas(ctx) {
|
|
293
332
|
let {
|
|
294
333
|
dom,
|
|
295
334
|
clone,
|
|
296
335
|
resources
|
|
297
|
-
} =
|
|
336
|
+
} = ctx;
|
|
298
337
|
for (let canvas of dom.querySelectorAll('canvas')) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
338
|
+
try {
|
|
339
|
+
// Note: the `.toDataURL` API requires WebGL canvas elements to use
|
|
340
|
+
// `preserveDrawingBuffer: true`. This is because `.toDataURL` uses the
|
|
341
|
+
// drawing buffer, which is cleared after each render for WebGL by default.
|
|
342
|
+
let dataUrl = canvas.toDataURL();
|
|
343
|
+
|
|
344
|
+
// skip empty canvases
|
|
345
|
+
if (!dataUrl || dataUrl === 'data:,') continue;
|
|
346
|
+
|
|
347
|
+
// get the element's percy id and create a resource for it
|
|
348
|
+
let percyElementId = canvas.getAttribute('data-percy-element-id');
|
|
349
|
+
let resource = resourceFromDataURL(percyElementId, dataUrl);
|
|
350
|
+
resources.add(resource);
|
|
351
|
+
|
|
352
|
+
// create an image element in the cloned dom
|
|
353
|
+
let img = document.createElement('img');
|
|
354
|
+
// use a data attribute to avoid making a real request
|
|
355
|
+
img.setAttribute('data-percy-serialized-attribute-src', resource.url);
|
|
356
|
+
|
|
357
|
+
// copy canvas element attributes to the image element such as style, class,
|
|
358
|
+
// or data attributes that may be targeted by CSS
|
|
359
|
+
for (let {
|
|
360
|
+
name,
|
|
361
|
+
value
|
|
362
|
+
} of canvas.attributes) {
|
|
363
|
+
img.setAttribute(name, value);
|
|
364
|
+
}
|
|
325
365
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
366
|
+
// mark the image as serialized (can be targeted by CSS)
|
|
367
|
+
img.setAttribute('data-percy-canvas-serialized', '');
|
|
368
|
+
// set a default max width to account for canvases that might resize with JS
|
|
369
|
+
img.style.maxWidth = img.style.maxWidth || '100%';
|
|
330
370
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
371
|
+
// insert the image into the cloned DOM and remove the cloned canvas element
|
|
372
|
+
let cloneEl = clone.querySelector(`[data-percy-element-id=${percyElementId}]`);
|
|
373
|
+
// `parentElement` for elements directly under shadow root is `null` -> Incase of Nested Shadow DOM.
|
|
374
|
+
if (cloneEl.parentElement) {
|
|
375
|
+
cloneEl.parentElement.insertBefore(img, cloneEl);
|
|
376
|
+
} else {
|
|
377
|
+
clone.insertBefore(img, cloneEl);
|
|
378
|
+
}
|
|
379
|
+
cloneEl.remove();
|
|
380
|
+
} catch (err) {
|
|
381
|
+
handleErrors(err, 'Error serializing canvas element: ', canvas);
|
|
338
382
|
}
|
|
339
|
-
cloneEl.remove();
|
|
340
383
|
}
|
|
341
384
|
}
|
|
342
385
|
|
|
343
386
|
// Captures the current frame of videos and sets the poster image
|
|
344
|
-
function serializeVideos(
|
|
387
|
+
function serializeVideos(ctx) {
|
|
345
388
|
let {
|
|
346
389
|
dom,
|
|
347
390
|
clone,
|
|
348
391
|
resources,
|
|
349
392
|
warnings
|
|
350
|
-
} =
|
|
393
|
+
} = ctx;
|
|
351
394
|
for (let video of dom.querySelectorAll('video')) {
|
|
352
|
-
// if the video already has a poster image, no work for us to do
|
|
353
|
-
if (video.getAttribute('poster')) continue;
|
|
354
|
-
let videoId = video.getAttribute('data-percy-element-id');
|
|
355
|
-
let cloneEl = clone.querySelector(`[data-percy-element-id="${videoId}"]`);
|
|
356
|
-
let canvas = document.createElement('canvas');
|
|
357
|
-
let width = canvas.width = video.videoWidth;
|
|
358
|
-
let height = canvas.height = video.videoHeight;
|
|
359
|
-
let dataUrl;
|
|
360
|
-
canvas.getContext('2d').drawImage(video, 0, 0, width, height);
|
|
361
395
|
try {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
396
|
+
// if the video already has a poster image, no work for us to do
|
|
397
|
+
if (video.getAttribute('poster')) continue;
|
|
398
|
+
let videoId = video.getAttribute('data-percy-element-id');
|
|
399
|
+
let cloneEl = clone.querySelector(`[data-percy-element-id="${videoId}"]`);
|
|
400
|
+
let canvas = document.createElement('canvas');
|
|
401
|
+
let width = canvas.width = video.videoWidth;
|
|
402
|
+
let height = canvas.height = video.videoHeight;
|
|
403
|
+
let dataUrl;
|
|
404
|
+
canvas.getContext('2d').drawImage(video, 0, 0, width, height);
|
|
405
|
+
try {
|
|
406
|
+
dataUrl = canvas.toDataURL();
|
|
407
|
+
} catch (e) {
|
|
408
|
+
warnings.add(`data-percy-element-id="${videoId}" : ${e.toString()}`);
|
|
409
|
+
}
|
|
366
410
|
|
|
367
|
-
|
|
368
|
-
|
|
411
|
+
// if the canvas produces a blank image, skip
|
|
412
|
+
if (!dataUrl || dataUrl === 'data:,') continue;
|
|
369
413
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
414
|
+
// create a resource from the serialized data url
|
|
415
|
+
let resource = resourceFromDataURL(videoId, dataUrl);
|
|
416
|
+
resources.add(resource);
|
|
373
417
|
|
|
374
|
-
|
|
375
|
-
|
|
418
|
+
// use a data attribute to avoid making a real request
|
|
419
|
+
cloneEl.setAttribute('data-percy-serialized-attribute-poster', resource.url);
|
|
420
|
+
} catch (err) {
|
|
421
|
+
handleErrors(err, 'Error serializing video element: ', video);
|
|
422
|
+
}
|
|
376
423
|
}
|
|
377
424
|
}
|
|
378
425
|
|
|
@@ -434,54 +481,62 @@
|
|
|
434
481
|
*/
|
|
435
482
|
|
|
436
483
|
const ignoreTags = ['NOSCRIPT'];
|
|
437
|
-
function cloneNodeAndShadow(
|
|
484
|
+
function cloneNodeAndShadow(ctx) {
|
|
438
485
|
let {
|
|
439
486
|
dom,
|
|
440
487
|
disableShadowDOM,
|
|
441
488
|
resources
|
|
442
|
-
} =
|
|
489
|
+
} = ctx;
|
|
443
490
|
// clones shadow DOM and light DOM for a given node
|
|
444
491
|
let cloneNode = (node, parent) => {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
492
|
+
try {
|
|
493
|
+
let walkTree = (nextn, nextp) => {
|
|
494
|
+
while (nextn) {
|
|
495
|
+
if (!ignoreTags.includes(nextn.nodeName)) {
|
|
496
|
+
cloneNode(nextn, nextp);
|
|
497
|
+
}
|
|
498
|
+
nextn = nextn.nextSibling;
|
|
449
499
|
}
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
};
|
|
500
|
+
};
|
|
453
501
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
502
|
+
// mark the node before cloning
|
|
503
|
+
markElement(node, disableShadowDOM);
|
|
504
|
+
let clone = node.cloneNode();
|
|
457
505
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
506
|
+
// We apply any element transformations here to avoid another treeWalk
|
|
507
|
+
applyElementTransformations(clone);
|
|
508
|
+
serializeBase64(clone, resources);
|
|
509
|
+
parent.appendChild(clone);
|
|
462
510
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
511
|
+
// shallow clone should not contain children
|
|
512
|
+
if (clone.children) {
|
|
513
|
+
Array.from(clone.children).forEach(child => clone.removeChild(child));
|
|
514
|
+
}
|
|
467
515
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
516
|
+
// clone shadow DOM
|
|
517
|
+
if (node.shadowRoot && !disableShadowDOM) {
|
|
518
|
+
// create shadowRoot
|
|
519
|
+
if (clone.shadowRoot) {
|
|
520
|
+
// it may be set up in a custom element's constructor
|
|
521
|
+
clone.shadowRoot.innerHTML = '';
|
|
522
|
+
} else {
|
|
523
|
+
clone.attachShadow({
|
|
524
|
+
mode: 'open'
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
// clone dom elements
|
|
528
|
+
walkTree(node.shadowRoot.firstChild, clone.shadowRoot);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// clone light DOM
|
|
532
|
+
walkTree(node.firstChild, clone);
|
|
533
|
+
} catch (err) {
|
|
534
|
+
if (!err.handled) {
|
|
535
|
+
handleErrors(err, 'Error cloning node: ', node);
|
|
474
536
|
} else {
|
|
475
|
-
|
|
476
|
-
mode: 'open'
|
|
477
|
-
});
|
|
537
|
+
throw err;
|
|
478
538
|
}
|
|
479
|
-
// clone dom elements
|
|
480
|
-
walkTree(node.shadowRoot.firstChild, clone.shadowRoot);
|
|
481
539
|
}
|
|
482
|
-
|
|
483
|
-
// clone light DOM
|
|
484
|
-
walkTree(node.firstChild, clone);
|
|
485
540
|
};
|
|
486
541
|
let fragment = dom.createDocumentFragment();
|
|
487
542
|
cloneNode(dom.documentElement, fragment);
|
|
@@ -604,6 +659,7 @@
|
|
|
604
659
|
}
|
|
605
660
|
let result = {
|
|
606
661
|
html: serializeHTML(ctx),
|
|
662
|
+
cookies: dom.cookie,
|
|
607
663
|
warnings: Array.from(ctx.warnings),
|
|
608
664
|
resources: Array.from(ctx.resources),
|
|
609
665
|
hints: Array.from(ctx.hints)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/dom",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0-beta.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public",
|
|
12
|
-
"tag": "
|
|
12
|
+
"tag": "beta"
|
|
13
13
|
},
|
|
14
14
|
"main": "dist/bundle.js",
|
|
15
15
|
"browser": "dist/bundle.js",
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"interactor.js": "^2.0.0-beta.10"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "719cb9b3c5e50cfe23b2e27ebfa2410f30443cd8"
|
|
39
39
|
}
|