@percy/agent
Version:
An agent process for integrating with Percy.
190 lines (189 loc) • 7.87 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const FORM_ELEMENTS_SELECTOR = 'input, textarea, select';
/**
* A single class to encapsulate all DOM operations that need to be performed to
* capture the customer's application state.
*
*/
class DOM {
constructor(dom, options) {
this.defaultDoctype = '';
this.originalDOM = dom;
this.options = options || {};
this.clonedDOM = this.cloneDOM();
}
/**
* Returns the final DOM string with all of the necessary transforms
* applied. This is the string that is passed to the API and then rendered by
* our API.
*
*/
snapshotString() {
// any since the cloned DOMs type can shift
let dom = this.clonedDOM;
const doctype = this.getDoctype();
// Sometimes you'll want to transform the DOM provided into one ready for snapshotting
// For example, if your test suite runs tests in an element inside a page that
// lists all yours tests. You'll want to "hoist" the contents of the testing container to be
// the full page. Using a dom transformation is how you'd achieve that.
if (this.options.domTransformation) {
try {
dom = this.options.domTransformation(dom);
}
catch (error) {
console.error('Could not transform the dom: ', error);
}
}
return doctype + dom.outerHTML;
}
getDoctype() {
return this.clonedDOM.doctype
? this.doctypeToString(this.clonedDOM.doctype)
: this.defaultDoctype;
}
doctypeToString(doctype) {
const publicDeclaration = doctype.publicId
? ` PUBLIC "${doctype.publicId}" `
: '';
const systemDeclaration = doctype.systemId
? ` SYSTEM "${doctype.systemId}" `
: '';
return (` 0;
return !hasHref && !hasStyleInDom && styleSheet.cssRules;
}
if (isCSSOM()) {
const $style = documentClone.createElement('style');
const cssRules = Array.from(styleSheet.cssRules);
const serializedStyles = cssRules.reduce((prev, cssRule) => {
return prev + cssRule.cssText;
}, '');
// Append the serialized styles to the cloned document
$style.type = 'text/css';
$style.setAttribute('data-percy-cssom-serialized', 'true');
$style.innerHTML = serializedStyles;
// TODO, it'd be better if we appended it right after the ownerNode in the clone
documentClone.head.appendChild($style);
}
});
}
/**
* A single place to mutate the original DOM. This should be the last resort!
* This will change the customer's DOM and have a possible impact on the
* customer's application.
*
*/
mutateOriginalDOM() {
function createUID($el) {
const ID = '_' +
Math.random()
.toString(36)
.substr(2, 9);
$el.setAttribute('data-percy-element-id', ID);
}
const formNodes = this.originalDOM.querySelectorAll(FORM_ELEMENTS_SELECTOR);
const formElements = Array.from(formNodes);
// loop through each form element and apply an ID for serialization later
formElements.forEach((elem) => {
if (!elem.getAttribute('data-percy-element-id')) {
createUID(elem);
}
});
}
}
exports.default = DOM;