174 lines
6 KiB
TypeScript
174 lines
6 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
import { CanvasHelper } from '#tests/helpers/canvas'
|
|
|
|
test('tool-created CJK text requests fallback through app font loading', async ({ page }) => {
|
|
const canvas = new CanvasHelper(page)
|
|
await page.goto('http://localhost:1420/?test&no-chrome&no-rulers')
|
|
await canvas.waitForInit()
|
|
|
|
const result = await page.evaluate(async () => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
const { ensureGraphFonts, loadFont } = await import('/src/app/editor/fonts/index.ts')
|
|
await loadFont('Inter', 'Regular')
|
|
const { fontManager } = await import('/packages/core/src/text/fonts.ts')
|
|
const originalEnsureFallbackPack = fontManager.ensureFallbackPack.bind(fontManager)
|
|
let requestedScripts: string[] = []
|
|
|
|
fontManager.ensureFallbackPack = async (scripts = ['cjk', 'arabic']) => {
|
|
requestedScripts = [...scripts]
|
|
return { cjk: ['Regression CJK Fallback'], arabic: [] }
|
|
}
|
|
|
|
const pageNode = store.graph.getNode(store.state.currentPageId)
|
|
if (!pageNode) throw new Error(`Page ${store.state.currentPageId} not found`)
|
|
const text = store.graph.createNode('TEXT', pageNode.id, {
|
|
name: 'Tool CJK Regression',
|
|
x: 80,
|
|
y: 80,
|
|
width: 300,
|
|
height: 60,
|
|
text: '你好世界',
|
|
fontSize: 32,
|
|
fontFamily: 'Inter',
|
|
textPicture: new Uint8Array([1, 2, 3]),
|
|
fills: [{ type: 'SOLID', color: { r: 0, g: 0, b: 0, a: 1 }, visible: true, opacity: 1 }]
|
|
})
|
|
|
|
try {
|
|
const changed = await ensureGraphFonts(store.graph, [text.id])
|
|
return {
|
|
changed,
|
|
requestedScripts,
|
|
textPictureCleared: store.graph.getNode(text.id)?.textPicture === null
|
|
}
|
|
} finally {
|
|
fontManager.ensureFallbackPack = originalEnsureFallbackPack
|
|
}
|
|
})
|
|
|
|
expect(result).toEqual({
|
|
changed: true,
|
|
requestedScripts: ['cjk'],
|
|
textPictureCleared: true
|
|
})
|
|
canvas.assertNoErrors()
|
|
})
|
|
|
|
test('CJK text waits for fallback fonts and repaints after they load', async ({ page }) => {
|
|
const canvas = new CanvasHelper(page)
|
|
await page.goto('http://localhost:1420/?test&no-chrome&no-rulers')
|
|
await canvas.waitForInit()
|
|
|
|
const result = await page.evaluate(async () => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
const renderer = store.renderer
|
|
if (!renderer) throw new Error('OpenPencil renderer not initialized')
|
|
|
|
const { fontManager } = await import('/packages/core/src/text/fonts.ts')
|
|
const manager = fontManager as typeof fontManager & {
|
|
cjkFallbackFamilies: string[]
|
|
cjkFallbackPromise: Promise<string[]> | null
|
|
arabicFallbackFamilies: string[]
|
|
arabicFallbackPromise: Promise<string[]> | null
|
|
}
|
|
const originalCJKFamilies = [...manager.cjkFallbackFamilies]
|
|
const originalCJKPromise = manager.cjkFallbackPromise
|
|
const originalArabicFamilies = [...manager.arabicFallbackFamilies]
|
|
const originalArabicPromise = manager.arabicFallbackPromise
|
|
const originalEnsureCJKFallback = fontManager.ensureCJKFallback.bind(fontManager)
|
|
const originalEnsureArabicFallback = fontManager.ensureArabicFallback.bind(fontManager)
|
|
|
|
let releaseCJKFallback: (() => void) | null = null
|
|
const fallbackGate = new Promise<void>((resolve) => {
|
|
releaseCJKFallback = resolve
|
|
})
|
|
|
|
manager.cjkFallbackFamilies = []
|
|
manager.cjkFallbackPromise = null
|
|
manager.arabicFallbackFamilies = []
|
|
manager.arabicFallbackPromise = null
|
|
|
|
let fallbackRenderCount = 0
|
|
let renderCount = 0
|
|
const originalRender = renderer.renderFromEditorState.bind(renderer)
|
|
|
|
fontManager.ensureCJKFallback = async () => {
|
|
await fallbackGate
|
|
fontManager.setCJKFallbackFamily('Regression CJK Fallback')
|
|
return ['Regression CJK Fallback']
|
|
}
|
|
fontManager.ensureArabicFallback = async () => []
|
|
renderer.renderFromEditorState = (
|
|
...args: Parameters<typeof renderer.renderFromEditorState>
|
|
) => {
|
|
renderCount += 1
|
|
return originalRender(...args)
|
|
}
|
|
|
|
const pageNode = store.graph.getNode(store.state.currentPageId)
|
|
if (!pageNode) throw new Error(`Page ${store.state.currentPageId} not found`)
|
|
const text = store.graph.createNode('TEXT', pageNode.id, {
|
|
name: 'CJK Regression',
|
|
x: 80,
|
|
y: 80,
|
|
width: 300,
|
|
height: 60,
|
|
text: '上班打卡App',
|
|
fontSize: 32,
|
|
fontFamily: 'Inter',
|
|
fills: [{ type: 'SOLID', color: { r: 0, g: 0, b: 0, a: 1 }, visible: true, opacity: 1 }]
|
|
})
|
|
|
|
try {
|
|
await renderer.loadFonts(() => {
|
|
fallbackRenderCount += 1
|
|
renderer.renderFromEditorState(
|
|
store.state,
|
|
store.graph,
|
|
store.textEditor,
|
|
800,
|
|
600,
|
|
false,
|
|
'full'
|
|
)
|
|
})
|
|
|
|
const loadedBeforeFallback = renderer.isNodeFontLoaded(text)
|
|
const beforeFallbackRenderCount = fallbackRenderCount
|
|
|
|
releaseCJKFallback?.()
|
|
await new Promise((resolve) => {
|
|
setTimeout(resolve, 0)
|
|
})
|
|
await new Promise(requestAnimationFrame)
|
|
|
|
return {
|
|
loadedBeforeFallback,
|
|
loadedAfterFallback: renderer.isNodeFontLoaded(text),
|
|
beforeFallbackRenderCount,
|
|
fallbackRenderCount,
|
|
renderCount
|
|
}
|
|
} finally {
|
|
manager.cjkFallbackFamilies = originalCJKFamilies
|
|
manager.cjkFallbackPromise = originalCJKPromise
|
|
manager.arabicFallbackFamilies = originalArabicFamilies
|
|
manager.arabicFallbackPromise = originalArabicPromise
|
|
fontManager.ensureCJKFallback = originalEnsureCJKFallback
|
|
fontManager.ensureArabicFallback = originalEnsureArabicFallback
|
|
renderer.renderFromEditorState = originalRender
|
|
}
|
|
})
|
|
|
|
expect(result.loadedBeforeFallback).toBe(false)
|
|
expect(result.loadedAfterFallback).toBe(true)
|
|
expect(result.beforeFallbackRenderCount).toBe(0)
|
|
expect(result.fallbackRenderCount).toBe(1)
|
|
expect(result.renderCount).toBeGreaterThan(0)
|
|
canvas.assertNoErrors()
|
|
})
|