openpencil/tests/helpers/dom-css-browser.ts
2026-06-30 10:51:37 +03:00

154 lines
4.9 KiB
TypeScript

import process from 'node:process'
import type { Page } from '@playwright/test'
import type { DesignDocument } from '@open-pencil/dom-css'
const BROWSER_RUNTIME_MODULE = `http://localhost:1420/@fs${process.cwd()}/packages/dom-css/src/runtime/browser.ts`
const DOM_CSS_BROWSER_MODULE = 'http://localhost:1420/@id/@open-pencil/dom-css/browser'
async function ensureAppPage(page: Page) {
if (!page.url().startsWith('http://localhost:1420')) {
await page.goto('/')
}
}
export async function setStyledContent(page: Page, css: string, body: string) {
await page.setContent(`
<style>${css}</style>
${body}
`)
}
export async function browserRuntimeComputeStyles(
page: Page,
document: DesignDocument,
cssText: string,
sandbox: 'shadow-root' | 'iframe' = 'iframe'
) {
await ensureAppPage(page)
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
}
)
}
export async function publicBrowserHTMLSceneGraph(page: Page, html: string, cssText = '') {
await ensureAppPage(page)
await page.setContent('<main></main>')
return page.evaluate(
async ({ sourceHTML, css, modulePath }) => {
const { browserHTMLToSceneGraph } = await import(modulePath)
const graph = await browserHTMLToSceneGraph(sourceHTML, { cssText: css })
const pageNode = graph.getPages()[0]
const card = pageNode ? graph.getChildren(pageNode.id)[0] : undefined
return card
? {
height: card.height,
itemSpacing: card.itemSpacing,
layoutMode: card.layoutMode,
paddingLeft: card.paddingLeft,
type: card.type,
width: card.width
}
: null
},
{ sourceHTML: html, css: cssText, modulePath: DOM_CSS_BROWSER_MODULE }
)
}
export async function publicBrowserSceneGraph(page: Page, classes: string[], cssText: string) {
await ensureAppPage(page)
await page.setContent('<main></main>')
return page.evaluate(
async ({ candidates, css, modulePath }) => {
const { browserJSXToSceneGraph, jsx } = await import(modulePath)
const graph = await browserJSXToSceneGraph(
jsx('article', {
class: candidates.join(' '),
children: jsx('h1', { children: 'OpenPencil' })
}),
{ cssText: css }
)
const pageNode = graph.getPages()[0]
const card = pageNode ? graph.getChildren(pageNode.id)[0] : undefined
return card
? {
height: card.height,
itemSpacing: card.itemSpacing,
layoutMode: card.layoutMode,
paddingLeft: card.paddingLeft,
type: card.type,
width: card.width
}
: null
},
{ candidates: classes, css: cssText, modulePath: DOM_CSS_BROWSER_MODULE }
)
}
export async function publicBrowserImageNode(page: Page, html: string, cssText: string) {
await ensureAppPage(page)
await page.setContent('<main></main>')
return page.evaluate(
async ({ sourceHTML, css, modulePath }) => {
const { browserHTMLToSceneGraph } = await import(modulePath)
const graph = await browserHTMLToSceneGraph(sourceHTML, { cssText: css })
const pageNode = graph.getPages()[0]
const image = pageNode ? graph.getChildren(pageNode.id)[0] : undefined
const fill = image?.fills[0]
return image
? {
fillType: fill?.type,
hasImageBytes: fill?.imageHash ? graph.images.has(fill.imageHash) : false,
height: image.height,
imageScaleMode: fill?.imageScaleMode,
type: image.type,
width: image.width
}
: null
},
{ sourceHTML: html, css: cssText, modulePath: DOM_CSS_BROWSER_MODULE }
)
}
export async function publicBrowserTextNode(page: Page, html: string, cssText: string) {
await ensureAppPage(page)
await page.setContent('<main></main>')
return page.evaluate(
async ({ sourceHTML, css, modulePath }) => {
const { browserHTMLToSceneGraph } = await import(modulePath)
const graph = await browserHTMLToSceneGraph(sourceHTML, { cssText: css })
return graph.getAllNodes().find((node) => node.type === 'TEXT')
},
{ sourceHTML: html, css: cssText, modulePath: DOM_CSS_BROWSER_MODULE }
)
}
export async function computedStyleProperties(
page: Page,
selector: string,
properties: readonly string[]
) {
return page.locator(selector).evaluate((element, styleProperties) => {
const computed = getComputedStyle(element)
return Object.fromEntries(
styleProperties.map((property) => [property, computed.getPropertyValue(property)])
)
}, properties)
}