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

Use textContent of stylesheet if available #61

Merged
merged 3 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = {
ignorePatterns: ['pages/'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2018,
ecmaVersion: 2020,
},
rules: {
'no-use-before-define': 2,
Expand Down
38 changes: 34 additions & 4 deletions playwright-tests/takeDOMSnapshot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,28 @@ test('regular elements', async ({ page }) => {
baseUrl: 'http://localhost:7700/regular-elements',
},
]);

expect(snapshot.cssBlocks).toEqual([]);
});

test('style collection', async ({ page }) => {
await setupPage(page);

await page.goto('/style-collection');

const snapshot = await page.evaluate(() => {
return window.happoTakeDOMSnapshot({ doc: document, element: document.body });
});

expect(snapshot.cssBlocks.length).toBe(2);
expect(snapshot.cssBlocks[0].content).toMatch(/--my-custom-font: 400 1rem/);
expect(snapshot.cssBlocks[0].content).toMatch(/--my-custom-font-weight: 400;/);
expect(snapshot.cssBlocks[0].content).toMatch(/font: var\(--my-custom-font\)/);
expect(snapshot.cssBlocks[0].content).toMatch(
/font-weight: var\(--my-custom-font-weight\);/,
);

expect(snapshot.cssBlocks[1].content).toMatch(/color: yellow;/);
});

test('one custom element', async ({ page }) => {
Expand Down Expand Up @@ -221,8 +243,12 @@ test('constructed styles', async ({ page }) => {

expect(snapshot.html).toMatch(/<p>world<\/p>/s);
expect(snapshot.cssBlocks.length).toBe(2);
expect(snapshot.cssBlocks[0].content).toEqual('p { color: blue; }');
expect(snapshot.cssBlocks[1].content).toEqual('p { color: red; }');
expect(snapshot.cssBlocks[0].content.replace(/\s+/g, ' ').trim()).toEqual(
'p { color: blue; }',
);
expect(snapshot.cssBlocks[1].content.replace(/\s+/g, ' ')).toEqual(
'p { color: red; }',
);
}

// Take another snapshot to make sure that the styles are not duplicated.
Expand All @@ -233,7 +259,11 @@ test('constructed styles', async ({ page }) => {

expect(snapshot.html).toMatch(/<h1>Hello<\/h1>/s);
expect(snapshot.cssBlocks.length).toBe(2);
expect(snapshot.cssBlocks[0].content).toEqual('p { color: blue; }');
expect(snapshot.cssBlocks[1].content).toEqual('p { color: red; }');
expect(snapshot.cssBlocks[0].content.replace(/\s+/g, ' ').trim()).toEqual(
'p { color: blue; }',
);
expect(snapshot.cssBlocks[1].content.replace(/\s+/g, ' ')).toEqual(
'p { color: red; }',
);
}
});
19 changes: 19 additions & 0 deletions playwright-tests/test-assets/style-collection/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<html>
<head>
<style type="text/css">
:root {
--my-custom-font: 400 1rem / 1.5rem Roboto, Arial, Helvetica, sans-serif;
--my-custom-font-weight: 400;
}
p {
font: var(--my-custom-font);
font-weight: var(--my-custom-font-weight);
}
</style>
<style type="text/css" id="dynamic-style"></style>
</head>
<body>
<main>Hello world</main>
</body>
<script src="/style-collection/index.js"></script>
</html>
3 changes: 3 additions & 0 deletions playwright-tests/test-assets/style-collection/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
document.head
.querySelector('#dynamic-style')
.sheet.insertRule('main { color: yellow; }');
41 changes: 25 additions & 16 deletions takeDOMSnapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ const findCSSAssetUrls = require('./src/findCSSAssetUrls');
const CSS_ELEMENTS_SELECTOR = 'style,link[rel="stylesheet"][href]';
const COMMENT_PATTERN = /^\/\*.+\*\/$/;

function getContentFromStyleSheet(element) {
let lines;

if (element.textContent) {
// Handle <style> elements with direct textContent
lines = element.textContent.split('\n').map((line) => line.trim());
} else if (element.sheet?.cssRules) {
// Handle <style> or <link> elements that have a sheet property
lines = Array.from(element.sheet.cssRules).map((rule) => rule.cssText);
} else if (element.cssRules) {
// Handle CSSStyleSheet objects (including adoptedStyleSheets)
lines = Array.from(element.cssRules).map((rule) => rule.cssText);
} else {
return '';
}

return lines.filter((line) => line && !COMMENT_PATTERN.test(line)).join('\n');
}

function extractCSSBlocks(doc) {
const blocks = [];
const styleElements = doc.querySelectorAll(CSS_ELEMENTS_SELECTOR);
Expand All @@ -21,25 +40,17 @@ function extractCSSBlocks(doc) {
const href = element.href || element.getAttribute('href');
blocks.push({ key: href, href, baseUrl: element.baseURI });
} else {
// <style>
const lines = Array.from(element.sheet.cssRules).map((r) => r.cssText);

// Filter out those lines that are comments (these are often source
// mappings)
const content = lines.filter((line) => !COMMENT_PATTERN.test(line)).join('\n');

const content = getContentFromStyleSheet(element);
// Create a hash so that we can dedupe equal styles
const key = md5(content).toString();
blocks.push({ content, key, baseUrl: element.baseURI });
}
});

(doc.adoptedStyleSheets || []).forEach((sheet) => {
const rules = Array.from(sheet.cssRules)
.map((r) => r.cssText)
.join('\n');
const key = md5(rules).toString();
blocks.push({ key, content: rules, baseUrl: sheet.href || document.baseURI });
const content = getContentFromStyleSheet(sheet);
const key = md5(content).toString();
blocks.push({ key, content, baseUrl: sheet.href || document.baseURI });
});
return blocks;
}
Expand Down Expand Up @@ -272,10 +283,8 @@ function inlineShadowRoots(element) {
for (const styleSheet of element.shadowRoot.adoptedStyleSheets) {
const styleElement = document.createElement('style');
styleElement.setAttribute('data-happo-inlined', 'true');
const rules = Array.from(styleSheet.cssRules)
.map((rule) => rule.cssText)
.join('\n');
styleElement.textContent = rules;
const styleContent = getContentFromStyleSheet(styleSheet);
styleElement.textContent = styleContent;
hiddenElement.appendChild(styleElement);
}

Expand Down