openpencil/tests/e2e/fonts/variable-weights-visual.spec.ts

95 lines
3.4 KiB
TypeScript
Raw Normal View History

import { Buffer } from 'node:buffer'
import type { Page } from '@playwright/test'
import type { FontManager } from '#core/text/fonts'
import { expect, test } from '#tests/e2e/fixtures'
import { CanvasHelper } from '#tests/helpers/canvas'
import { mockFontsource } from '#tests/helpers/fonts/fontsource'
import { trackFontModuleResources } from '#tests/helpers/fonts/runtime'
async function openEditor(page: Page): Promise<void> {
await trackFontModuleResources(page)
// The variable fixture is installed explicitly; remote arrivals must not race it.
await mockFontsource(page, [])
await page.goto('/?test&no-chrome&no-rulers')
await new CanvasHelper(page).waitForInit()
}
test('variable fonts render each style at its named instance', async ({ page }) => {
await openEditor(page)
const result = await page.evaluate(async () => {
const store = window.openPencil?.getStore?.()
if (!store?.renderer) throw new Error('OpenPencil renderer not initialized')
const fontModuleURL = performance
.getEntriesByType('resource')
.map((entry) => entry.name)
.find((url) => url.includes('/packages/core/src/text/fonts.ts'))
if (!fontModuleURL) throw new Error('Active font manager module not found')
const { fontManager } = (await import(/* @vite-ignore */ fontModuleURL)) as {
fontManager: FontManager
}
const pageId = store.state.currentPageId
for (const id of store.graph.getNode(pageId)?.childIds.slice() ?? []) {
store.graph.deleteNode(id)
}
// One variable file serves every style, as installed variable fonts such as SF Pro do.
const data = await fetch('/tests/fixtures/fonts/InterVariable-Latin.ttf').then((response) =>
response.arrayBuffer()
)
for (const style of ['Regular', 'Medium', 'SemiBold', 'Bold']) {
fontManager.markLoaded('Inter Variable', style, data)
}
const samples = [
{ label: 'Regular 400', fontWeight: 400 },
{ label: 'Medium 500', fontWeight: 500 },
{ label: 'SemiBold 600', fontWeight: 600 },
{ label: 'Bold 700', fontWeight: 700 },
{
label: 'Bold, explicit wght 300',
fontWeight: 700,
fontVariations: [{ axis: 'wght', value: 300 }]
}
]
const ids = samples.map(
(sample, index) =>
store.graph.createNode('TEXT', pageId, {
name: sample.label,
x: 40,
y: 40 + index * 56,
width: 520,
height: 44,
text: `${sample.label} — Buttons`,
fontFamily: 'Inter Variable',
fontSize: 32,
fontWeight: sample.fontWeight,
fontVariations: sample.fontVariations ?? [],
textAutoResize: 'NONE',
fills: [
{ type: 'SOLID', color: { r: 0.04, g: 0.06, b: 0.12, a: 1 }, visible: true, opacity: 1 }
]
}).id
)
await store.loadFontsForNodes(ids)
store.renderer.invalidateAllPictures()
store.requestRender()
const image = await store.renderExportImage(ids, 2, 'PNG')
if (!image) throw new Error('Export failed')
return {
image: Array.from(image),
readiness: ids.map((id) => {
const node = store.graph.getNode(id)
return node ? store.renderer?.nodeFontReadiness(node) : 'missing'
})
}
})
expect(result.readiness).toEqual(['ready', 'ready', 'ready', 'ready', 'ready'])
expect(Buffer.from(result.image)).toMatchSnapshot('variable-font-weights.png')
})