test(dom-css): cover browser CSS runtime pipeline

This commit is contained in:
Danila Poyarkov 2026-06-03 19:04:14 +03:00
parent 1f55891c3d
commit 50c9961d75

View file

@ -1,8 +1,17 @@
import process from 'node:process'
import type { Page } from '@playwright/test'
import { compileTailwindCSS } from '@open-pencil/dom-css'
import {
compileTailwindCSS,
designDocumentToSceneGraph,
jsx,
jsxToDesignDocument,
type DesignDocument
} from '@open-pencil/dom-css'
import {
DOM_CSS_COLORS,
fixtureMatrixCSS,
fixtureMatrixHTML,
tailwindBadgeClasses,
@ -21,6 +30,33 @@ async function setStyledContent(page: Page, css: string, body: string) {
`)
}
const BROWSER_RUNTIME_MODULE = `http://localhost:1420/@fs${process.cwd()}/packages/dom-css/src/runtime/browser.ts`
async function browserRuntimeComputeStyles(
page: Page,
document: DesignDocument,
cssText: string,
sandbox: 'shadow-root' | 'iframe' = 'iframe'
) {
if (!page.url().startsWith('http://localhost:1420')) {
await page.goto('/')
}
return page.evaluate(
async ({ designDocument, css, modulePath, sandboxMode }) => {
const { createBrowserCSSRuntime } = await import(modulePath)
const runtime = createBrowserCSSRuntime({ document: window.document, sandbox: sandboxMode })
return runtime.computeStyles(designDocument, css)
},
{
designDocument: document,
css: cssText,
modulePath: BROWSER_RUNTIME_MODULE,
sandboxMode: sandbox
}
)
}
async function computedStyleProperties(
page: Page,
selector: string,
@ -280,4 +316,83 @@ test.describe('@open-pencil/dom-css browser CSS runtime oracle', () => {
expect(styles.paddingTop).toBe('24px')
expect(styles.width).toBe('176px')
})
test('computes styles through the browser runtime sandbox', async ({ page }) => {
await page.goto('/')
await setStyledContent(page, '.card { width: 20px; }', '<article class="card">Host</article>')
const document: DesignDocument = {
type: 'document',
children: [
{
type: 'element',
tagName: 'article',
attrs: { class: 'card' },
children: [{ type: 'text', text: 'OpenPencil' }]
}
]
}
const css = `
:root { --spacing: 0.25rem; }
.card {
display: flex;
flex-direction: column;
gap: calc(var(--spacing) * 3);
width: calc(10rem + 16px);
padding: calc(var(--spacing) * 6);
box-shadow: 0px 8px 24px 0px ${DOM_CSS_COLORS.dialogShadow};
}
`
const iframeDocument = await browserRuntimeComputeStyles(page, document, css, 'iframe')
const shadowDocument = await browserRuntimeComputeStyles(page, document, css, 'shadow-root')
const iframeCard = iframeDocument.children[0]
const shadowCard = shadowDocument.children[0]
expect(iframeCard?.type).toBe('element')
expect(shadowCard?.type).toBe('element')
if (iframeCard?.type !== 'element' || shadowCard?.type !== 'element') return
expect(iframeCard.computedStyle?.width).toBe('176px')
expect(shadowCard.computedStyle?.width).toBe('176px')
expect(iframeCard.computedStyle?.gap).toBe('12px')
expect(iframeCard.computedStyle?.['padding-top']).toBe('24px')
expect(iframeCard.computedStyle?.['box-shadow']).toContain('rgba(15, 23, 42, 0.16)')
const hostWidth = await page
.locator('article.card')
.evaluate((element) => getComputedStyle(element).width)
expect(hostWidth).toBe('20px')
})
test('projects JSX and Tailwind through browser computed styles into scene graph', async ({
page
}) => {
const classes = [...tailwindCardClasses]
const document = await jsxToDesignDocument(
jsx('article', {
class: classes.join(' '),
children: jsx('h1', { children: 'OpenPencil' })
})
)
const css = await compileTailwindCSS(classes)
const computedDocument = await browserRuntimeComputeStyles(page, document, css, 'iframe')
const graph = designDocumentToSceneGraph(computedDocument)
const designCard = computedDocument.children[0]
const pageNode = graph.getPages()[0]
const card = pageNode ? graph.getChildren(pageNode.id)[0] : undefined
expect(designCard?.type).toBe('element')
if (designCard?.type !== 'element') return
expect(designCard.computedStyle?.display).toBe('flex')
expect(designCard.computedStyle?.width).toBe('320px')
expect(designCard.computedStyle?.height).toBe('176px')
expect(designCard.computedStyle?.['padding-top']).toBe('24px')
expect(card?.type).toBe('FRAME')
if (card?.type !== 'FRAME') return
expect(card.width).toBe(320)
expect(card.height).toBe(176)
expect(card.layoutMode).toBe('VERTICAL')
expect(card.itemSpacing).toBe(12)
expect(card.paddingLeft).toBe(24)
})
})