@percy/dom
Advanced tools
Comparing version 1.14.0 to 1.15.0
@@ -21,6 +21,11 @@ (function() { | ||
} | ||
return dom; | ||
} | ||
// Translates JavaScript properties of inputs into DOM attributes. | ||
function serializeInputElements(dom, clone) { | ||
function serializeInputElements(_ref) { | ||
let { | ||
dom, | ||
clone | ||
} = _ref; | ||
for (let elem of dom.querySelectorAll('input, textarea, select')) { | ||
@@ -65,4 +70,8 @@ let inputId = elem.getAttribute('data-percy-element-id'); | ||
// Recursively serializes iframe documents into srcdoc attributes. | ||
function serializeFrames(dom, clone, _ref) { | ||
function serializeFrames(_ref) { | ||
let { | ||
dom, | ||
clone, | ||
warnings, | ||
resources, | ||
enableJavaScript | ||
@@ -95,4 +104,9 @@ } = _ref; | ||
// assign to srcdoc and remove src | ||
cloneEl.setAttribute('srcdoc', serialized); | ||
// append serialized warnings and resources | ||
/* istanbul ignore next: warnings not implemented yet */ | ||
for (let w of serialized.warnings) warnings.add(w); | ||
for (let r of serialized.resources) resources.add(r); | ||
// assign serialized html to srcdoc and remove src | ||
cloneEl.setAttribute('srcdoc', serialized.html); | ||
cloneEl.removeAttribute('src'); | ||
@@ -126,3 +140,7 @@ | ||
// Outputs in-memory CSSOM into their respective DOM nodes. | ||
function serializeCSSOM(dom, clone) { | ||
function serializeCSSOM(_ref) { | ||
let { | ||
dom, | ||
clone | ||
} = _ref; | ||
for (let styleSheet of dom.styleSheets) { | ||
@@ -144,4 +162,29 @@ if (isCSSOM(styleSheet)) { | ||
// Creates a resource object from an element's unique ID and data URL | ||
function resourceFromDataURL(uid, dataURL) { | ||
// split dataURL into desired parts | ||
let [data, content] = dataURL.split(','); | ||
let [, mimetype] = data.split(':'); | ||
[mimetype] = mimetype.split(';'); | ||
// build a URL for the serialized asset | ||
let [, ext] = mimetype.split('/'); | ||
let path = `/__serialized__/${uid}.${ext}`; | ||
let url = new URL(path, document.URL).toString(); | ||
// return the url, base64 content, and mimetype | ||
return { | ||
url, | ||
content, | ||
mimetype | ||
}; | ||
} | ||
// Serialize in-memory canvas elements into images. | ||
function serializeCanvas(dom, clone) { | ||
function serializeCanvas(_ref) { | ||
let { | ||
dom, | ||
clone, | ||
resources | ||
} = _ref; | ||
for (let canvas of dom.querySelectorAll('canvas')) { | ||
@@ -156,5 +199,11 @@ // Note: the `.toDataURL` API requires WebGL canvas elements to use | ||
// get the element's percy id and create a resource for it | ||
let percyElementId = canvas.getAttribute('data-percy-element-id'); | ||
let resource = resourceFromDataURL(percyElementId, dataUrl); | ||
resources.add(resource); | ||
// create an image element in the cloned dom | ||
let img = clone.createElement('img'); | ||
img.src = dataUrl; | ||
// use a data attribute to avoid making a real request | ||
img.setAttribute('data-percy-serialized-attribute-src', resource.url); | ||
@@ -176,3 +225,2 @@ // copy canvas element attributes to the image element such as style, class, | ||
// insert the image into the cloned DOM and remove the cloned canvas element | ||
let percyElementId = canvas.getAttribute('data-percy-element-id'); | ||
let cloneEl = clone.querySelector(`[data-percy-element-id=${percyElementId}]`); | ||
@@ -185,5 +233,10 @@ cloneEl.parentElement.insertBefore(img, cloneEl); | ||
// Captures the current frame of videos and sets the poster image | ||
function serializeVideos(dom, clone) { | ||
function serializeVideos(_ref) { | ||
let { | ||
dom, | ||
clone, | ||
resources | ||
} = _ref; | ||
for (let video of dom.querySelectorAll('video')) { | ||
// If the video already has a poster image, no work for us to do | ||
// if the video already has a poster image, no work for us to do | ||
if (video.getAttribute('poster')) continue; | ||
@@ -201,5 +254,11 @@ let videoId = video.getAttribute('data-percy-element-id'); | ||
// If the canvas produces a blank image, skip | ||
// if the canvas produces a blank image, skip | ||
if (!dataUrl || dataUrl === 'data:,') continue; | ||
cloneEl.setAttribute('poster', dataUrl); | ||
// create a resource from the serialized data url | ||
let resource = resourceFromDataURL(videoId, dataUrl); | ||
resources.add(resource); | ||
// use a data attribute to avoid making a real request | ||
cloneEl.setAttribute('data-percy-serialized-attribute-poster', resource.url); | ||
} | ||
@@ -226,2 +285,11 @@ } | ||
// Serializes and returns the cloned DOM as an HTML string | ||
function serializeHTML(ctx) { | ||
let html = ctx.clone.documentElement.outerHTML; | ||
// replace serialized data attributes with real attributes | ||
html = html.replace(/ data-percy-serialized-attribute-(\w+?)=/ig, ' $1='); | ||
// include the doctype with the html string | ||
return doctype(ctx.dom) + html; | ||
} | ||
// Serializes a document and returns the resulting DOM string. | ||
@@ -233,19 +301,24 @@ function serializeDOM(options) { | ||
enableJavaScript = options === null || options === void 0 ? void 0 : options.enable_javascript, | ||
domTransformation = options === null || options === void 0 ? void 0 : options.dom_transformation | ||
domTransformation = options === null || options === void 0 ? void 0 : options.dom_transformation, | ||
stringifyResponse = options === null || options === void 0 ? void 0 : options.stringify_response | ||
} = options || {}; | ||
prepareDOM(dom); | ||
let clone = dom.cloneNode(true); | ||
serializeInputElements(dom, clone); | ||
serializeFrames(dom, clone, { | ||
// keep certain records throughout serialization | ||
let ctx = { | ||
resources: new Set(), | ||
warnings: new Set(), | ||
enableJavaScript | ||
}); | ||
serializeVideos(dom, clone); | ||
}; | ||
ctx.dom = prepareDOM(dom); | ||
ctx.clone = ctx.dom.cloneNode(true); | ||
serializeInputElements(ctx); | ||
serializeFrames(ctx); | ||
serializeVideos(ctx); | ||
if (!enableJavaScript) { | ||
serializeCSSOM(dom, clone); | ||
serializeCanvas(dom, clone); | ||
serializeCSSOM(ctx); | ||
serializeCanvas(ctx); | ||
} | ||
let doc = clone.documentElement; | ||
if (domTransformation) { | ||
try { | ||
domTransformation(doc); | ||
domTransformation(ctx.clone.documentElement); | ||
} catch (err) { | ||
@@ -255,3 +328,8 @@ console.error('Could not transform the dom:', err.message); | ||
} | ||
return doctype(dom) + doc.outerHTML; | ||
let result = { | ||
html: serializeHTML(ctx), | ||
warnings: Array.from(ctx.warnings), | ||
resources: Array.from(ctx.resources) | ||
}; | ||
return stringifyResponse ? JSON.stringify(result) : result; | ||
} | ||
@@ -258,0 +336,0 @@ |
{ | ||
"name": "@percy/dom", | ||
"version": "1.14.0", | ||
"version": "1.15.0", | ||
"license": "MIT", | ||
@@ -37,3 +37,3 @@ "repository": { | ||
}, | ||
"gitHead": "fd72688e449d6dd3eafd346fc07879cb3bb01a4e" | ||
"gitHead": "383ce2888d2e4fe6972368f9bbe8580b23431a98" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17182
300