Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added patch to capture dynamic media queries #1884

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
22 changes: 21 additions & 1 deletion packages/dom/src/clone-dom.js
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ import { handleErrors } from './utils';
const ignoreTags = ['NOSCRIPT'];

export function cloneNodeAndShadow(ctx) {
let { dom, disableShadowDOM, resources, cache } = ctx;
let { dom, disableShadowDOM, resources, cache, enableJavaScript } = ctx;
// clones shadow DOM and light DOM for a given node
let cloneNode = (node, parent) => {
try {
@@ -34,6 +34,26 @@ export function cloneNodeAndShadow(ctx) {

let clone = node.cloneNode();

// Handle <style> tag specifically for media queries
if (node.nodeName === 'STYLE' && !enableJavaScript) {
let cssText = node.textContent?.trim() || '';
if (!cssText && node.sheet) {
try {
const cssRules = node.sheet.cssRules;
if (cssRules && cssRules.length > 0) {
cssText = Array.from(cssRules).map(rule => rule.cssText).join('\n');
}
} catch (_) {
// ignore errors
}
}

if (cssText) {
clone.textContent = cssText;
clone.setAttribute('data-percy-cssom-serialized', 'true');
}
}

// We apply any element transformations here to avoid another treeWalk
applyElementTransformations(clone);

26 changes: 25 additions & 1 deletion packages/dom/test/serialize-css.test.js
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ describe('serializeCSSOM', () => {
let $cssom = parseDOM(serializeDOM(), platform)('[data-percy-cssom-serialized]');

// linked and unmodified stylesheets are not included
expect($cssom).toHaveSize(2);
expect($cssom).toHaveSize(3);
expect($cssom[0].innerHTML).toBe('.box { height: 500px; }');
expect($cssom[1].innerHTML).toBe('.box { width: 1000px; }');

@@ -78,6 +78,30 @@ describe('serializeCSSOM', () => {
expect($('[data-percy-cssom-serialized]')).toHaveSize(0);
});

it(`${platform}: skips empty CSSStyleSheets`, () => {
let cssomSheet = dom.styleSheets[0];
cssomSheet.deleteRule(0); // Remove all rules to make it empty
const serialized = serializeDOM();
let $cssom = parseDOM(serialized, platform)('[data-percy-cssom-serialized]');
expect($cssom).toHaveSize(2); // should skip the empty stylesheet
});

it(`${platform}: preserves media queries inside CSSOM`, () => {
let cssomSheet = dom.styleSheets[0];
cssomSheet.insertRule('@media screen and (min-width: 600px) { .box { display: none; } }');
const serialized = serializeDOM();
let $cssom = parseDOM(serialized, platform)('[data-percy-cssom-serialized]');
expect($cssom[0].innerHTML).toContain('@media screen and (min-width: 600px)');
});

it(`${platform}: maintains order of CSSOM serialization`, () => {
let cssomSheet = dom.styleSheets[0];
cssomSheet.insertRule('.box { padding: 20px; }', 0);
const serialized = serializeDOM();
let $cssom = parseDOM(serialized, platform)('[data-percy-cssom-serialized]');
expect($cssom[0].innerHTML.startsWith('.box { padding: 20px; }')).toBe(true);
});

it('captures adoptedStylesheets inside document', () => {
if (platform !== 'plain') {
return;