@percy/dom 1.14.0 → 1.15.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/dist/bundle.js +102 -24
- package/package.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -18,10 +18,15 @@
|
|
|
18
18
|
elem.setAttribute('data-percy-element-id', uid());
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
return dom;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
// Translates JavaScript properties of inputs into DOM attributes.
|
|
24
|
-
function serializeInputElements(
|
|
25
|
+
function serializeInputElements(_ref) {
|
|
26
|
+
let {
|
|
27
|
+
dom,
|
|
28
|
+
clone
|
|
29
|
+
} = _ref;
|
|
25
30
|
for (let elem of dom.querySelectorAll('input, textarea, select')) {
|
|
26
31
|
let inputId = elem.getAttribute('data-percy-element-id');
|
|
27
32
|
let cloneEl = clone.querySelector(`[data-percy-element-id="${inputId}"]`);
|
|
@@ -61,8 +66,12 @@
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
// Recursively serializes iframe documents into srcdoc attributes.
|
|
64
|
-
function serializeFrames(
|
|
69
|
+
function serializeFrames(_ref) {
|
|
65
70
|
let {
|
|
71
|
+
dom,
|
|
72
|
+
clone,
|
|
73
|
+
warnings,
|
|
74
|
+
resources,
|
|
66
75
|
enableJavaScript
|
|
67
76
|
} = _ref;
|
|
68
77
|
for (let frame of dom.querySelectorAll('iframe')) {
|
|
@@ -90,8 +99,13 @@
|
|
|
90
99
|
enableJavaScript
|
|
91
100
|
});
|
|
92
101
|
|
|
93
|
-
//
|
|
94
|
-
|
|
102
|
+
// append serialized warnings and resources
|
|
103
|
+
/* istanbul ignore next: warnings not implemented yet */
|
|
104
|
+
for (let w of serialized.warnings) warnings.add(w);
|
|
105
|
+
for (let r of serialized.resources) resources.add(r);
|
|
106
|
+
|
|
107
|
+
// assign serialized html to srcdoc and remove src
|
|
108
|
+
cloneEl.setAttribute('srcdoc', serialized.html);
|
|
95
109
|
cloneEl.removeAttribute('src');
|
|
96
110
|
|
|
97
111
|
// delete inaccessible frames built with js when js is disabled because they
|
|
@@ -120,7 +134,11 @@
|
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
// Outputs in-memory CSSOM into their respective DOM nodes.
|
|
123
|
-
function serializeCSSOM(
|
|
137
|
+
function serializeCSSOM(_ref) {
|
|
138
|
+
let {
|
|
139
|
+
dom,
|
|
140
|
+
clone
|
|
141
|
+
} = _ref;
|
|
124
142
|
for (let styleSheet of dom.styleSheets) {
|
|
125
143
|
if (isCSSOM(styleSheet)) {
|
|
126
144
|
let styleId = styleSheet.ownerNode.getAttribute('data-percy-element-id');
|
|
@@ -137,8 +155,33 @@
|
|
|
137
155
|
}
|
|
138
156
|
}
|
|
139
157
|
|
|
158
|
+
// Creates a resource object from an element's unique ID and data URL
|
|
159
|
+
function resourceFromDataURL(uid, dataURL) {
|
|
160
|
+
// split dataURL into desired parts
|
|
161
|
+
let [data, content] = dataURL.split(',');
|
|
162
|
+
let [, mimetype] = data.split(':');
|
|
163
|
+
[mimetype] = mimetype.split(';');
|
|
164
|
+
|
|
165
|
+
// build a URL for the serialized asset
|
|
166
|
+
let [, ext] = mimetype.split('/');
|
|
167
|
+
let path = `/__serialized__/${uid}.${ext}`;
|
|
168
|
+
let url = new URL(path, document.URL).toString();
|
|
169
|
+
|
|
170
|
+
// return the url, base64 content, and mimetype
|
|
171
|
+
return {
|
|
172
|
+
url,
|
|
173
|
+
content,
|
|
174
|
+
mimetype
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
140
178
|
// Serialize in-memory canvas elements into images.
|
|
141
|
-
function serializeCanvas(
|
|
179
|
+
function serializeCanvas(_ref) {
|
|
180
|
+
let {
|
|
181
|
+
dom,
|
|
182
|
+
clone,
|
|
183
|
+
resources
|
|
184
|
+
} = _ref;
|
|
142
185
|
for (let canvas of dom.querySelectorAll('canvas')) {
|
|
143
186
|
// Note: the `.toDataURL` API requires WebGL canvas elements to use
|
|
144
187
|
// `preserveDrawingBuffer: true`. This is because `.toDataURL` uses the
|
|
@@ -148,9 +191,15 @@
|
|
|
148
191
|
// skip empty canvases
|
|
149
192
|
if (!dataUrl || dataUrl === 'data:,') continue;
|
|
150
193
|
|
|
194
|
+
// get the element's percy id and create a resource for it
|
|
195
|
+
let percyElementId = canvas.getAttribute('data-percy-element-id');
|
|
196
|
+
let resource = resourceFromDataURL(percyElementId, dataUrl);
|
|
197
|
+
resources.add(resource);
|
|
198
|
+
|
|
151
199
|
// create an image element in the cloned dom
|
|
152
200
|
let img = clone.createElement('img');
|
|
153
|
-
|
|
201
|
+
// use a data attribute to avoid making a real request
|
|
202
|
+
img.setAttribute('data-percy-serialized-attribute-src', resource.url);
|
|
154
203
|
|
|
155
204
|
// copy canvas element attributes to the image element such as style, class,
|
|
156
205
|
// or data attributes that may be targeted by CSS
|
|
@@ -167,7 +216,6 @@
|
|
|
167
216
|
img.style.maxWidth = img.style.maxWidth || '100%';
|
|
168
217
|
|
|
169
218
|
// insert the image into the cloned DOM and remove the cloned canvas element
|
|
170
|
-
let percyElementId = canvas.getAttribute('data-percy-element-id');
|
|
171
219
|
let cloneEl = clone.querySelector(`[data-percy-element-id=${percyElementId}]`);
|
|
172
220
|
cloneEl.parentElement.insertBefore(img, cloneEl);
|
|
173
221
|
cloneEl.remove();
|
|
@@ -175,9 +223,14 @@
|
|
|
175
223
|
}
|
|
176
224
|
|
|
177
225
|
// Captures the current frame of videos and sets the poster image
|
|
178
|
-
function serializeVideos(
|
|
226
|
+
function serializeVideos(_ref) {
|
|
227
|
+
let {
|
|
228
|
+
dom,
|
|
229
|
+
clone,
|
|
230
|
+
resources
|
|
231
|
+
} = _ref;
|
|
179
232
|
for (let video of dom.querySelectorAll('video')) {
|
|
180
|
-
//
|
|
233
|
+
// if the video already has a poster image, no work for us to do
|
|
181
234
|
if (video.getAttribute('poster')) continue;
|
|
182
235
|
let videoId = video.getAttribute('data-percy-element-id');
|
|
183
236
|
let cloneEl = clone.querySelector(`[data-percy-element-id="${videoId}"]`);
|
|
@@ -190,9 +243,15 @@
|
|
|
190
243
|
dataUrl = canvas.toDataURL();
|
|
191
244
|
} catch {}
|
|
192
245
|
|
|
193
|
-
//
|
|
246
|
+
// if the canvas produces a blank image, skip
|
|
194
247
|
if (!dataUrl || dataUrl === 'data:,') continue;
|
|
195
|
-
|
|
248
|
+
|
|
249
|
+
// create a resource from the serialized data url
|
|
250
|
+
let resource = resourceFromDataURL(videoId, dataUrl);
|
|
251
|
+
resources.add(resource);
|
|
252
|
+
|
|
253
|
+
// use a data attribute to avoid making a real request
|
|
254
|
+
cloneEl.setAttribute('data-percy-serialized-attribute-poster', resource.url);
|
|
196
255
|
}
|
|
197
256
|
}
|
|
198
257
|
|
|
@@ -214,34 +273,53 @@
|
|
|
214
273
|
return `<!DOCTYPE ${name}${deprecated}>`;
|
|
215
274
|
}
|
|
216
275
|
|
|
276
|
+
// Serializes and returns the cloned DOM as an HTML string
|
|
277
|
+
function serializeHTML(ctx) {
|
|
278
|
+
let html = ctx.clone.documentElement.outerHTML;
|
|
279
|
+
// replace serialized data attributes with real attributes
|
|
280
|
+
html = html.replace(/ data-percy-serialized-attribute-(\w+?)=/ig, ' $1=');
|
|
281
|
+
// include the doctype with the html string
|
|
282
|
+
return doctype(ctx.dom) + html;
|
|
283
|
+
}
|
|
284
|
+
|
|
217
285
|
// Serializes a document and returns the resulting DOM string.
|
|
218
286
|
function serializeDOM(options) {
|
|
219
287
|
let {
|
|
220
288
|
dom = document,
|
|
221
289
|
// allow snake_case or camelCase
|
|
222
290
|
enableJavaScript = options === null || options === void 0 ? void 0 : options.enable_javascript,
|
|
223
|
-
domTransformation = options === null || options === void 0 ? void 0 : options.dom_transformation
|
|
291
|
+
domTransformation = options === null || options === void 0 ? void 0 : options.dom_transformation,
|
|
292
|
+
stringifyResponse = options === null || options === void 0 ? void 0 : options.stringify_response
|
|
224
293
|
} = options || {};
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
294
|
+
|
|
295
|
+
// keep certain records throughout serialization
|
|
296
|
+
let ctx = {
|
|
297
|
+
resources: new Set(),
|
|
298
|
+
warnings: new Set(),
|
|
229
299
|
enableJavaScript
|
|
230
|
-
}
|
|
231
|
-
|
|
300
|
+
};
|
|
301
|
+
ctx.dom = prepareDOM(dom);
|
|
302
|
+
ctx.clone = ctx.dom.cloneNode(true);
|
|
303
|
+
serializeInputElements(ctx);
|
|
304
|
+
serializeFrames(ctx);
|
|
305
|
+
serializeVideos(ctx);
|
|
232
306
|
if (!enableJavaScript) {
|
|
233
|
-
serializeCSSOM(
|
|
234
|
-
serializeCanvas(
|
|
307
|
+
serializeCSSOM(ctx);
|
|
308
|
+
serializeCanvas(ctx);
|
|
235
309
|
}
|
|
236
|
-
let doc = clone.documentElement;
|
|
237
310
|
if (domTransformation) {
|
|
238
311
|
try {
|
|
239
|
-
domTransformation(
|
|
312
|
+
domTransformation(ctx.clone.documentElement);
|
|
240
313
|
} catch (err) {
|
|
241
314
|
console.error('Could not transform the dom:', err.message);
|
|
242
315
|
}
|
|
243
316
|
}
|
|
244
|
-
|
|
317
|
+
let result = {
|
|
318
|
+
html: serializeHTML(ctx),
|
|
319
|
+
warnings: Array.from(ctx.warnings),
|
|
320
|
+
resources: Array.from(ctx.resources)
|
|
321
|
+
};
|
|
322
|
+
return stringifyResponse ? JSON.stringify(result) : result;
|
|
245
323
|
}
|
|
246
324
|
|
|
247
325
|
exports["default"] = serializeDOM;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/dom",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"interactor.js": "^2.0.0-beta.10"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "383ce2888d2e4fe6972368f9bbe8580b23431a98"
|
|
38
38
|
}
|