chore(tests): remove easy non-null assertions
- Replace browser store non-null assertions with explicit initialization guards - Use expectDefined for optional test resources and tool results - Clean low-count non-null assertions in font, icon, snap, OKHCL, and visual tests
This commit is contained in:
parent
9e4d7bee5d
commit
39675b2969
|
|
@ -101,7 +101,8 @@ test('Duplicate via Edit menu works', async () => {
|
|||
await canvas.drawRect(300, 300, 80, 80)
|
||||
|
||||
const countBefore = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).length
|
||||
})
|
||||
|
||||
|
|
@ -110,7 +111,8 @@ test('Duplicate via Edit menu works', async () => {
|
|||
await canvas.waitForRender()
|
||||
|
||||
const countAfter = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).length
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ async function expectCanvas(name: string) {
|
|||
|
||||
async function createOverlayDemo(rotation: number) {
|
||||
await page.evaluate((frameRotation) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
|
||||
const frame = store.graph.createNode('FRAME', pageId, {
|
||||
|
|
@ -102,7 +103,8 @@ test('rotation preview updates frame labels before mouse up', async () => {
|
|||
await createOverlayDemo(0)
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const frameId = [...store.state.selectedIds][0]
|
||||
store.state.rotationPreview = { nodeId: frameId, angle: 28 }
|
||||
store.requestRepaint()
|
||||
|
|
|
|||
|
|
@ -87,7 +87,8 @@ test('resize corner handle drag increases node dimensions', async () => {
|
|||
expect(before).not.toBeNull()
|
||||
|
||||
const viewport = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
const n = store.graph.getNode(id)
|
||||
if (!n) return null
|
||||
|
|
@ -131,7 +132,8 @@ test('rotation handle drag rotates node', async () => {
|
|||
const initialRotation = before!.rotation ?? 0
|
||||
|
||||
const viewport = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
const n = store.graph.getNode(id)
|
||||
if (!n) return null
|
||||
|
|
@ -183,7 +185,8 @@ async function setupFrameChild(rotation: number) {
|
|||
await canvas.clearCanvas()
|
||||
|
||||
const setup = await page.evaluate((frameRotation) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const frameId = store.createShape('FRAME', 180, 160, 240, 160)
|
||||
if (!frameId) return null
|
||||
store.updateNode(frameId, { rotation: frameRotation })
|
||||
|
|
@ -200,7 +203,8 @@ async function setupFrameChild(rotation: number) {
|
|||
await canvas.waitForRender()
|
||||
|
||||
const state = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
const pageNode = store.graph.getNode(pageId)
|
||||
const frame = pageNode?.childIds
|
||||
|
|
@ -278,7 +282,8 @@ test('rotation drag exposes live rotation preview state', async () => {
|
|||
await canvas.waitForRender()
|
||||
|
||||
const viewport = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
const n = store.graph.getNode(id)
|
||||
if (!n) return null
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
function getPageChildCount() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).length
|
||||
})
|
||||
}
|
||||
|
|
@ -31,7 +32,8 @@ function getSelectedCount() {
|
|||
|
||||
function getSelectedNodes() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return [...store.state.selectedIds].map((id) => {
|
||||
const n = store.graph.getNode(id)!
|
||||
return {
|
||||
|
|
@ -55,7 +57,8 @@ test('copy + paste via store duplicates a shape', async () => {
|
|||
const countBefore = await getPageChildCount()
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const data = new DataTransfer()
|
||||
await store.writeCopyData(data)
|
||||
const html = data.getData('text/html')
|
||||
|
|
@ -92,7 +95,8 @@ test('⌘D duplicates in place', async () => {
|
|||
test('duplicate preserves fills', async () => {
|
||||
// Set a custom fill on the selected node
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
store.updateNodeWithUndo(
|
||||
id,
|
||||
|
|
@ -127,7 +131,8 @@ test('cut removes original', async () => {
|
|||
|
||||
// Cut via store
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const data = new DataTransfer()
|
||||
store.writeCopyData(data)
|
||||
store.deleteSelected()
|
||||
|
|
|
|||
|
|
@ -99,7 +99,8 @@ test('deselecting shows empty state again', async () => {
|
|||
test('selecting a frame shows Frame in JSX', async () => {
|
||||
// Create a frame via store to avoid click-targeting issues
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.createShape('FRAME', 300, 100, 200, 200)
|
||||
store.select([id])
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
async function getSelectedFill() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
if (!id) return null
|
||||
const node = store.graph.getNode(id)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ async function selectDemoCard(page: Parameters<typeof test>[0]['page'], canvas:
|
|||
await canvas.waitForInit()
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const nodes = Array.from(store.graph.nodes.values())
|
||||
const card =
|
||||
nodes.find((node) => node.name === 'Card' && node.type === 'COMPONENT') ??
|
||||
|
|
@ -49,7 +50,8 @@ async function selectDemoCard(page: Parameters<typeof test>[0]['page'], canvas:
|
|||
|
||||
async function getSelectedFill(page: Parameters<typeof test>[0]['page']) {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
const node = store.graph.getNode(id)
|
||||
return node
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
function getNodeById(id: string) {
|
||||
return page.evaluate((nodeId) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const n = store.graph.getNode(nodeId)
|
||||
if (!n) return null
|
||||
return { type: n.type, name: n.name, componentId: n.componentId, childIds: n.childIds }
|
||||
|
|
@ -33,7 +34,8 @@ function getSelectedIds() {
|
|||
|
||||
function getPageChildren() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).map((n) => ({
|
||||
id: n.id,
|
||||
type: n.type,
|
||||
|
|
@ -71,7 +73,8 @@ test('component visible in layers panel', async () => {
|
|||
expect(count).toBeGreaterThan(0)
|
||||
|
||||
const types = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).map((n) => n.type)
|
||||
})
|
||||
expect(types).toContain('COMPONENT')
|
||||
|
|
@ -84,7 +87,8 @@ test('create instance from component (context menu)', async () => {
|
|||
|
||||
// Use store directly to create instance
|
||||
await page.evaluate((compId) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.createInstanceFromComponent(compId, 300, 100)
|
||||
}, comp!.id)
|
||||
await canvas.waitForRender()
|
||||
|
|
@ -127,7 +131,8 @@ test('modifying component propagates to instance', async () => {
|
|||
|
||||
// Change component fill
|
||||
await page.evaluate((id) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.updateNodeWithUndo(
|
||||
id,
|
||||
{
|
||||
|
|
@ -150,7 +155,8 @@ test('modifying component propagates to instance', async () => {
|
|||
const children = await getPageChildren()
|
||||
const instance = children.find((c) => c.type === 'INSTANCE')!
|
||||
const instanceNode = await page.evaluate((id) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const n = store.graph.getNode(id)
|
||||
const child = store.graph.getChildren(id)[0]
|
||||
return child ? { fills: child.fills } : { fills: n?.fills ?? [] }
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
function getPageChildren() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).map((n) => ({
|
||||
id: n.id,
|
||||
type: n.type,
|
||||
|
|
@ -117,7 +118,8 @@ test('toggle visibility via context menu', async () => {
|
|||
|
||||
// Toggle back: select via store since invisible nodes can't be hit-tested
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.toggleVisibility()
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
|
|
@ -177,7 +179,8 @@ test('group via context menu', async () => {
|
|||
test('ungroup via store after context-menu group', async () => {
|
||||
// Groups are click-through, so ungroup via store instead
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const group = store.graph.getChildren(store.state.currentPageId).find((n) => n.type === 'GROUP')
|
||||
if (group) store.select([group.id])
|
||||
store.ungroupSelected()
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ function effectsSection() {
|
|||
|
||||
function getNode(id: string) {
|
||||
return page.evaluate((nodeId) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const n = store.graph.getNode(nodeId)
|
||||
if (!n) return null
|
||||
return {
|
||||
|
|
@ -64,7 +65,8 @@ function getNode(id: string) {
|
|||
|
||||
function getSelectedId() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return [...store.state.selectedIds][0] ?? null
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ test.beforeEach(async () => {
|
|||
|
||||
async function rectangleCount() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return [...store.graph.nodes.values()].filter((node) => node.type === 'RECTANGLE').length
|
||||
})
|
||||
}
|
||||
|
|
@ -35,7 +36,8 @@ async function layerItems() {
|
|||
|
||||
async function historyState() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return {
|
||||
canUndo: store.undo.canUndo,
|
||||
canRedo: store.undo.canRedo,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ test('font settings popover is available from typography panel', async ({ page }
|
|||
await canvas.waitForInit()
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.createShape('TEXT', 120, 120, 240, 40)
|
||||
store.updateNode(id, { characters: 'Font settings smoke' })
|
||||
store.select([id])
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ function getSelectedCount() {
|
|||
|
||||
function getPageChildren() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).map((n) => ({
|
||||
id: n.id,
|
||||
type: n.type,
|
||||
|
|
@ -219,7 +220,8 @@ test.describe('zoom shortcuts', () => {
|
|||
|
||||
// Set zoom to something other than 100%
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.state.zoom = 2
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
|
|
@ -300,14 +302,16 @@ test.describe('auto-layout shortcut', () => {
|
|||
|
||||
// Change to frame type for auto-layout
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const nodes = [...store.state.selectedIds]
|
||||
if (nodes[0]) store.updateNode(nodes[0], { type: 'FRAME' })
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
|
||||
const layoutBefore = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const nodes = [...store.state.selectedIds]
|
||||
return store.graph.getNode(nodes[0])?.layoutMode
|
||||
})
|
||||
|
|
@ -317,7 +321,8 @@ test.describe('auto-layout shortcut', () => {
|
|||
await canvas.waitForRender()
|
||||
|
||||
const layoutAfter = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const nodes = [...store.state.selectedIds]
|
||||
return store.graph.getNode(nodes[0])?.layoutMode
|
||||
})
|
||||
|
|
@ -328,7 +333,8 @@ test.describe('auto-layout shortcut', () => {
|
|||
await canvas.waitForRender()
|
||||
|
||||
const layoutFinal = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const nodes = [...store.state.selectedIds]
|
||||
return store.graph.getNode(nodes[0])?.layoutMode
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
function getPages() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getPages().map((p) => ({ id: p.id, name: p.name }))
|
||||
})
|
||||
}
|
||||
|
|
@ -31,7 +32,8 @@ function getCurrentPageId() {
|
|||
|
||||
function getPageChildCount() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).length
|
||||
})
|
||||
}
|
||||
|
|
@ -120,7 +122,8 @@ test('delete current page switches to adjacent', async () => {
|
|||
const deletingId = await getCurrentPageId()
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.deletePage(store.state.currentPageId)
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
|
|
@ -191,7 +194,8 @@ test('cannot delete the last page', async () => {
|
|||
let pages = await getPages()
|
||||
while (pages.length > 1) {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.deletePage(store.state.currentPageId)
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
|
|
@ -202,7 +206,8 @@ test('cannot delete the last page', async () => {
|
|||
|
||||
// Try deleting the last one — should be a no-op
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.deletePage(store.state.currentPageId)
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ test.describe('Render performance', () => {
|
|||
await helper.waitForInit()
|
||||
|
||||
await page.evaluate((count: number) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const arr = new Uint8Array(count * 3)
|
||||
crypto.getRandomValues(arr)
|
||||
const cols = Math.ceil(Math.sqrt(count))
|
||||
|
|
@ -86,7 +87,8 @@ test.describe('Render performance', () => {
|
|||
|
||||
test('benchmark: synchronous render throughput', async () => {
|
||||
const results = await helper.page.evaluate((iterations: number) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const renderer = store.renderer!
|
||||
|
||||
function setupRenderer() {
|
||||
|
|
@ -173,7 +175,8 @@ test.describe('Render performance', () => {
|
|||
|
||||
const results = await helper.page.evaluate(
|
||||
({ count, iterations }) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const renderer = store.renderer!
|
||||
const graph = store.graph
|
||||
const pageId = store.state.currentPageId
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
async function getSelectedNodeFlags() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
if (!id) return null
|
||||
const n = store.graph.getNode(id)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ async function expectCanvas(name: string) {
|
|||
|
||||
test('drop shadow on white card', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('FRAME', pageId, {
|
||||
name: 'Card',
|
||||
|
|
@ -60,7 +61,8 @@ test('drop shadow on white card', async () => {
|
|||
|
||||
test('drop shadow with spread', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('FRAME', pageId, {
|
||||
name: 'Card',
|
||||
|
|
@ -90,7 +92,8 @@ test('drop shadow with spread', async () => {
|
|||
|
||||
test('inner shadow', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('FRAME', pageId, {
|
||||
name: 'Card',
|
||||
|
|
@ -122,7 +125,8 @@ test('inner shadow', async () => {
|
|||
|
||||
test('inner shadow with spread', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('FRAME', pageId, {
|
||||
name: 'Card',
|
||||
|
|
@ -154,7 +158,8 @@ test('inner shadow with spread', async () => {
|
|||
|
||||
test('drop shadow on ellipse', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('ELLIPSE', pageId, {
|
||||
name: 'Circle',
|
||||
|
|
@ -185,7 +190,8 @@ test('drop shadow on ellipse', async () => {
|
|||
|
||||
test('combined drop and inner shadow', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('FRAME', pageId, {
|
||||
name: 'Card',
|
||||
|
|
@ -223,7 +229,8 @@ test('combined drop and inner shadow', async () => {
|
|||
|
||||
test('text drop shadow on glyphs', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('TEXT', pageId, {
|
||||
name: 'Shadow Text',
|
||||
|
|
@ -258,7 +265,8 @@ test('text drop shadow on glyphs', async () => {
|
|||
|
||||
test('layer blur', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('RECTANGLE', pageId, {
|
||||
name: 'Blurred',
|
||||
|
|
@ -290,7 +298,8 @@ test('layer blur', async () => {
|
|||
|
||||
test('invisible effect has no visual impact', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
store.graph.createNode('FRAME', pageId, {
|
||||
name: 'Card',
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ test('variable bind badge appears on fill', async () => {
|
|||
await canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const col = store.graph.createCollection('Colors')
|
||||
const v = store.graph.createVariable('brand-red', 'COLOR', col.id, { r: 1, g: 0, b: 0, a: 1 })
|
||||
const id = [...store.state.selectedIds][0]
|
||||
|
|
@ -115,7 +116,8 @@ test('fill color can bind an existing variable', async () => {
|
|||
await canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const col = store.graph.createCollection('Colors')
|
||||
const variable = store.graph.createVariable('test-brand-red', 'COLOR', col.id, {
|
||||
r: 1,
|
||||
|
|
@ -143,7 +145,8 @@ test('fill color can bind an existing variable', async () => {
|
|||
await canvas.waitForRender()
|
||||
await expect(page.locator('[data-test-id="fill-unbind-variable"]')).toBeHidden()
|
||||
const boundVariableId = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
return id ? (store.getNode(id)?.boundVariables['fills/0/color'] ?? null) : null
|
||||
})
|
||||
|
|
@ -164,7 +167,8 @@ test('fill color can create and bind a variable', async () => {
|
|||
|
||||
await expect(page.locator('[data-test-id="fill-unbind-variable"]')).toBeVisible()
|
||||
const boundVariable = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
if (!id) return null
|
||||
const node = store.getNode(id)
|
||||
|
|
@ -189,7 +193,8 @@ test('width can create, bind, and detach a number variable', async () => {
|
|||
|
||||
await expect(page.locator('[data-test-id="layout-width-unbind-variable"]')).toBeVisible()
|
||||
const boundVariable = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
if (!id) return null
|
||||
const node = store.getNode(id)
|
||||
|
|
@ -207,7 +212,8 @@ test('width can create, bind, and detach a number variable', async () => {
|
|||
|
||||
await expect(page.locator('[data-test-id="layout-width-unbind-variable"]')).toBeHidden()
|
||||
const directWidth = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
const node = id ? store.getNode(id) : null
|
||||
return node ? { width: node.width, binding: node.boundVariables.width ?? null } : null
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
function getSelectedNode() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
if (!id) return null
|
||||
const n = store.graph.getNode(id)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
await helper.waitForInit()
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageId = store.state.currentPageId
|
||||
|
||||
store.graph.createNode('FRAME', pageId, {
|
||||
|
|
@ -74,7 +75,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
// We simulate this by calling invalidateScenePicture() to force the next
|
||||
// render to re-record, then verify hover on/off doesn't lose text.
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
// Simulate what loadFonts() does: invalidate the cached picture
|
||||
store.renderer!.invalidateScenePicture()
|
||||
store.requestRender()
|
||||
|
|
@ -83,7 +85,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
|
||||
// Extra render to stabilize the SkPicture cache
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.requestRender()
|
||||
})
|
||||
await helper.waitForRender()
|
||||
|
|
@ -93,7 +96,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
|
||||
// Hover frame → un-hover: replays the newly recorded picture
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pg = store.graph.getNode(store.state.currentPageId)!
|
||||
const frame = pg.childIds.find((id: string) => store.graph.getNode(id)?.type === 'FRAME')
|
||||
store.setHoveredNode(frame ?? null)
|
||||
|
|
@ -101,7 +105,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
await helper.waitForRender()
|
||||
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.setHoveredNode(null)
|
||||
})
|
||||
await helper.waitForRender()
|
||||
|
|
@ -113,14 +118,16 @@ test.describe('SkPicture scene caching', () => {
|
|||
test('text survives hover on/off cycle', async () => {
|
||||
// 1. Baseline: no hover — this records the SkPicture cache
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.setHoveredNode(null)
|
||||
store.requestRender()
|
||||
})
|
||||
await helper.waitForRender()
|
||||
// Extra render cycle to ensure SkPicture cache is fully recorded
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.requestRender()
|
||||
})
|
||||
await helper.waitForRender()
|
||||
|
|
@ -128,7 +135,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
|
||||
// 2. Hover a frame — uses requestRepaint (only renderVersion, not sceneVersion)
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const page = store.graph.getNode(store.state.currentPageId)!
|
||||
const frame = page.childIds.find((id: string) => store.graph.getNode(id)?.type === 'FRAME')
|
||||
store.setHoveredNode(frame ?? null)
|
||||
|
|
@ -137,7 +145,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
|
||||
// 3. Hover off — should replay cached SkPicture (the critical transition)
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.setHoveredNode(null)
|
||||
})
|
||||
await helper.waitForRender()
|
||||
|
|
@ -151,7 +160,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
test('text survives multiple hover cycles', async () => {
|
||||
// Rapid hover on/off using the real setHoveredNode path (requestRepaint)
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const page = store.graph.getNode(store.state.currentPageId)!
|
||||
const frame = page.childIds.find((id: string) => store.graph.getNode(id)?.type === 'FRAME')
|
||||
|
||||
|
|
@ -168,7 +178,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
test('text survives real mouse hover on/off', async () => {
|
||||
// Use actual mouse movement instead of programmatic setHoveredNode
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.setHoveredNode(null)
|
||||
store.requestRender()
|
||||
})
|
||||
|
|
@ -195,7 +206,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
test('text survives scene change then hover cycle', async () => {
|
||||
// Mutate scene to invalidate picture cache
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const page = store.graph.getNode(store.state.currentPageId)!
|
||||
const frame = page.childIds.find((id: string) => store.graph.getNode(id)?.type === 'FRAME')
|
||||
if (frame) store.graph.updateNode(frame, { width: 310 })
|
||||
|
|
@ -205,7 +217,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
|
||||
// Hover on then off using real path
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const page = store.graph.getNode(store.state.currentPageId)!
|
||||
const frame = page.childIds.find((id: string) => store.graph.getNode(id)?.type === 'FRAME')
|
||||
store.setHoveredNode(frame ?? null)
|
||||
|
|
@ -213,7 +226,8 @@ test.describe('SkPicture scene caching', () => {
|
|||
await helper.waitForRender()
|
||||
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.setHoveredNode(null)
|
||||
})
|
||||
await helper.waitForRender()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ test.afterAll(async () => {
|
|||
async function createRects() {
|
||||
await canvas.clearCanvas()
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.createShape('RECTANGLE', 100, 100, 80, 80)
|
||||
const b = store.createShape('RECTANGLE', 300, 100, 80, 80)
|
||||
store.select([b])
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ async function chooseFormat(page: Page, label: 'RGB' | 'HSL' | 'HSB' | 'OkHCL')
|
|||
|
||||
async function getSelectedStroke(page: Page) {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
const node = store.graph.getNode(id)
|
||||
return node?.strokes?.[0] ?? null
|
||||
|
|
@ -87,7 +88,8 @@ test('stroke picker hsb saturation and brightness sliders update stroke color on
|
|||
await canvas.waitForInit()
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const nodes = Array.from(store.graph.nodes.values())
|
||||
const card =
|
||||
nodes.find((node) => node.name === 'Card' && node.type === 'COMPONENT') ??
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
function getSelectedNode() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
if (!id) return null
|
||||
const n = store.graph.getNode(id)
|
||||
|
|
@ -41,7 +42,8 @@ function getSelectedNode() {
|
|||
|
||||
function getPageChildren() {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).map((n) => ({
|
||||
type: n.type,
|
||||
name: n.name,
|
||||
|
|
@ -100,7 +102,8 @@ test('creating text via store works', async () => {
|
|||
await canvas.waitForRender()
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.createShape('TEXT', 100, 300, 200, 30)
|
||||
store.graph.updateNode(id, { text: 'Hello World', fontSize: 24, fontFamily: 'Inter' })
|
||||
store.select([id])
|
||||
|
|
@ -140,7 +143,8 @@ test('Enter key opens text editing and selects all without erasing', async () =>
|
|||
await canvas.waitForRender()
|
||||
|
||||
const textId = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.createShape('TEXT', 300, 300, 200, 30)
|
||||
store.graph.updateNode(id, { text: 'Keep this text' })
|
||||
store.select([id])
|
||||
|
|
@ -160,7 +164,8 @@ test('Enter key opens text editing and selects all without erasing', async () =>
|
|||
expect(editing).toBe(textId)
|
||||
|
||||
const after = await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.state.editingTextId
|
||||
if (!id) return null
|
||||
return store.graph.getNode(id)?.text ?? null
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ test.beforeAll(async ({ browser }) => {
|
|||
await canvas.clearCanvas()
|
||||
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.state.zoom = 1
|
||||
store.state.panX = 0
|
||||
store.state.panY = 0
|
||||
|
|
@ -52,7 +53,8 @@ test('bold button toggles fontWeight to 700 then back to 400', async () => {
|
|||
|
||||
// ensure starting weight is 400 via undo-safe store method
|
||||
await page.evaluate(async (id: string) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.updateNodeWithUndo(id, { fontWeight: 400 }, 'reset')
|
||||
store.state.sceneVersion++
|
||||
await new Promise(requestAnimationFrame)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ test.afterAll(async () => {
|
|||
|
||||
async function createColorVariable(name: string) {
|
||||
return page.evaluate((varName: string) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const existing = [...store.graph.variableCollections.values()]
|
||||
const col = existing.length > 0 ? existing[0] : store.graph.createCollection('Test Collection')
|
||||
const v = store.graph.createVariable(varName, 'COLOR', col.id, { r: 1, g: 0, b: 0, a: 1 })
|
||||
|
|
@ -43,7 +44,8 @@ test('variables dialog opens', async () => {
|
|||
|
||||
test('search filters variable rows', async () => {
|
||||
await page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const col = [...store.graph.variableCollections.values()][0]
|
||||
store.graph.createVariable('beta-spacing', 'FLOAT', col.id, 8)
|
||||
store.state.sceneVersion++
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ test.describe('Zoom and pan', () => {
|
|||
await helper.waitForInit()
|
||||
|
||||
await page.evaluate((count: number) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const cols = Math.ceil(Math.sqrt(count))
|
||||
for (let i = 0; i < count; i++) {
|
||||
store.graph.createNode('RECTANGLE', store.state.currentPageId, {
|
||||
|
|
@ -38,7 +39,8 @@ test.describe('Zoom and pan', () => {
|
|||
|
||||
test('wheel zoom updates viewport correctly', async () => {
|
||||
const before = await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return { panX: store.state.panX, panY: store.state.panY, zoom: store.state.zoom }
|
||||
})
|
||||
|
||||
|
|
@ -52,7 +54,8 @@ test.describe('Zoom and pan', () => {
|
|||
await helper.page.waitForTimeout(50)
|
||||
|
||||
const after = await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return { panX: store.state.panX, panY: store.state.panY, zoom: store.state.zoom }
|
||||
})
|
||||
|
||||
|
|
@ -62,7 +65,8 @@ test.describe('Zoom and pan', () => {
|
|||
|
||||
test('wheel pan updates viewport correctly', async () => {
|
||||
const before = await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return { panX: store.state.panX, panY: store.state.panY }
|
||||
})
|
||||
|
||||
|
|
@ -75,7 +79,8 @@ test.describe('Zoom and pan', () => {
|
|||
await helper.page.waitForTimeout(50)
|
||||
|
||||
const after = await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return { panX: store.state.panX, panY: store.state.panY }
|
||||
})
|
||||
|
||||
|
|
@ -96,7 +101,8 @@ test.describe('Zoom and pan', () => {
|
|||
await helper.page.waitForTimeout(50)
|
||||
|
||||
const state = await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return { zoom: store.state.zoom }
|
||||
})
|
||||
|
||||
|
|
@ -107,7 +113,8 @@ test.describe('Zoom and pan', () => {
|
|||
|
||||
test('shallowReactive: selection replace triggers UI update', async () => {
|
||||
const result = await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageNode = store.graph.getNode(store.state.currentPageId)!
|
||||
const firstId = pageNode.childIds[0]
|
||||
|
||||
|
|
@ -129,7 +136,8 @@ test.describe('Zoom and pan', () => {
|
|||
test('useRafFn loop picks up renderVersion changes', async () => {
|
||||
// Ensure clean state, wait for any pending renders
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
store.state.panX = 0
|
||||
store.state.panY = 0
|
||||
store.state.zoom = 1
|
||||
|
|
@ -142,7 +150,8 @@ test.describe('Zoom and pan', () => {
|
|||
|
||||
// Change fill color — always visible
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageNode = store.graph.getNode(store.state.currentPageId)!
|
||||
const firstId = pageNode.childIds[0]
|
||||
store.graph.updateNode(firstId, {
|
||||
|
|
@ -158,7 +167,8 @@ test.describe('Zoom and pan', () => {
|
|||
|
||||
// Restore
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageNode = store.graph.getNode(store.state.currentPageId)!
|
||||
const firstId = pageNode.childIds[0]
|
||||
store.graph.updateNode(firstId, {
|
||||
|
|
@ -175,7 +185,8 @@ test.describe('Zoom and pan', () => {
|
|||
const before = await helper.screenshotCanvas()
|
||||
|
||||
await helper.page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const pageNode = store.graph.getNode(store.state.currentPageId)!
|
||||
store.select([pageNode.childIds[0]])
|
||||
})
|
||||
|
|
@ -196,7 +207,8 @@ test.describe('Zoom and pan', () => {
|
|||
const ITERATIONS = 500
|
||||
|
||||
const results = await helper.page.evaluate((iterations: number) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
|
||||
// Reset viewport
|
||||
store.state.panX = 0
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import {
|
|||
fontFallbackManifest
|
||||
} from '@open-pencil/core'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
function pageId(graph: SceneGraph) {
|
||||
return graph.getPages()[0].id
|
||||
}
|
||||
|
|
@ -491,12 +493,12 @@ describe('fetchBundledFont', () => {
|
|||
test('loads Inter-Regular.ttf from assets in headless', async () => {
|
||||
const buffer = await fontManager.fetchBundledFont('/Inter-Regular.ttf')
|
||||
expect(buffer).toBeInstanceOf(ArrayBuffer)
|
||||
expect(buffer!.byteLength).toBeGreaterThan(100_000)
|
||||
expect(expectDefined(buffer, 'Inter font buffer').byteLength).toBeGreaterThan(100_000)
|
||||
})
|
||||
|
||||
test('returns valid TTF data', async () => {
|
||||
const buffer = await fontManager.fetchBundledFont('/Inter-Regular.ttf')
|
||||
const view = new DataView(buffer!)
|
||||
const view = new DataView(expectDefined(buffer, 'Inter font buffer'))
|
||||
// TrueType magic: 0x00010000
|
||||
expect(view.getUint32(0)).toBe(0x00010000)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { describe, expect, test, beforeAll } from 'bun:test'
|
|||
|
||||
import { fetchIcon, fetchIcons, searchIcons, searchIconsBatch } from '@open-pencil/core'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
let hasNetwork = true
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
|
|
@ -129,7 +131,7 @@ describe('searchIconsBatch', () => {
|
|||
networkTest('searches multiple queries in parallel', async () => {
|
||||
const results = await searchIconsBatch(['heart', 'arrow'], { limit: 5 })
|
||||
expect(results.size).toBe(2)
|
||||
expect(results.get('heart')!.icons.length).toBeGreaterThan(0)
|
||||
expect(results.get('arrow')!.icons.length).toBeGreaterThan(0)
|
||||
expect(expectDefined(results.get('heart'), 'heart results').icons.length).toBeGreaterThan(0)
|
||||
expect(expectDefined(results.get('arrow'), 'arrow results').icons.length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import {
|
|||
SceneGraph
|
||||
} from '@open-pencil/core'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
describe('OkHCL metadata', () => {
|
||||
test('applies rgba rendering color while preserving fill metadata', () => {
|
||||
const graph = new SceneGraph()
|
||||
|
|
@ -79,12 +81,12 @@ describe('OkHCL metadata', () => {
|
|||
)
|
||||
const parsedFrame = [...parsed.getAllNodes()].find((node) => node.name === 'OKHCL frame')
|
||||
|
||||
expect(parsedFrame).toBeDefined()
|
||||
expect(getFillOkHCL(parsedFrame!, 0)).toMatchObject({
|
||||
const parsedOkhclFrame = expectDefined(parsedFrame, 'parsed OKHCL frame')
|
||||
expect(getFillOkHCL(parsedOkhclFrame, 0)).toMatchObject({
|
||||
kind: 'fill',
|
||||
color: { h: 210, c: 0.1, l: 0.65, a: 1 }
|
||||
})
|
||||
expect(getStrokeOkHCL(parsedFrame!, 0)).toMatchObject({
|
||||
expect(getStrokeOkHCL(parsedOkhclFrame, 0)).toMatchObject({
|
||||
kind: 'stroke',
|
||||
color: { h: 320, c: 0.09, l: 0.55, a: 0.9 }
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { describe, test, expect } from 'bun:test'
|
|||
|
||||
import { computeSnap, computeSelectionBounds, type SceneNode } from '@open-pencil/core'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
function node(overrides: Partial<SceneNode> & { id: string }): SceneNode {
|
||||
return { x: 0, y: 0, width: 100, height: 100, rotation: 0, ...overrides } as SceneNode
|
||||
}
|
||||
|
|
@ -25,9 +27,10 @@ describe('computeSelectionBounds', () => {
|
|||
})
|
||||
|
||||
test('rotated node expands bbox', () => {
|
||||
const bounds = computeSelectionBounds([
|
||||
node({ id: 'a', x: 0, y: 0, width: 100, height: 0, rotation: 45 })
|
||||
])!
|
||||
const bounds = expectDefined(
|
||||
computeSelectionBounds([node({ id: 'a', x: 0, y: 0, width: 100, height: 0, rotation: 45 })]),
|
||||
'rotated node bounds'
|
||||
)
|
||||
expect(bounds.width).toBeGreaterThan(0)
|
||||
expect(bounds.height).toBeGreaterThan(0)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { describe, expect, test } from 'bun:test'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
import { getTool, setupToolTest, type ToolResult } from '#tests/helpers/tools'
|
||||
|
||||
describe('create_shape', () => {
|
||||
|
|
@ -17,7 +18,10 @@ describe('create_shape', () => {
|
|||
expect(result.name).toBe('Test Frame')
|
||||
expect(result.type).toBe('FRAME')
|
||||
|
||||
const node = figma.getNodeById(result.id)!
|
||||
const node = expectDefined(
|
||||
figma.getNodeById(expectDefined(result.id, 'created node id')),
|
||||
'created node'
|
||||
)
|
||||
expect(node.x).toBe(100)
|
||||
expect(node.y).toBe(200)
|
||||
expect(node.width).toBe(300)
|
||||
|
|
@ -44,7 +48,10 @@ describe('create_shape', () => {
|
|||
parent_id: parent.id
|
||||
}) as ToolResult
|
||||
|
||||
const parentNode = figma.getNodeById(parent.id)!
|
||||
const parentNode = expectDefined(
|
||||
figma.getNodeById(expectDefined(parent.id, 'created parent id')),
|
||||
'created parent node'
|
||||
)
|
||||
expect(parentNode.children.some((c) => c.id === child.id)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import type { SceneNode } from '#core/scene-graph'
|
|||
import { SceneGraph } from '#core/scene-graph'
|
||||
import { fontManager } from '#core/text'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
async function main() {
|
||||
const ck = await initCanvasKit()
|
||||
|
||||
|
|
@ -55,7 +57,7 @@ async function main() {
|
|||
|
||||
graph.createNode('TEXT', pageId, textProps)
|
||||
|
||||
const surface = ck.MakeSurface(width, height)!
|
||||
const surface = expectDefined(ck.MakeSurface(width, height), 'CanvasKit surface')
|
||||
const canvas = surface.getCanvas()
|
||||
const renderer = new SkiaRenderer(ck, surface)
|
||||
renderer.fontProvider = fontProvider
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import type { SceneNode } from '#core/scene-graph'
|
|||
import { fontManager } from '#core/text'
|
||||
import type { Color, Vector } from '#core/types'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
interface TestCase {
|
||||
text: string
|
||||
fontSize: number
|
||||
|
|
@ -202,7 +204,7 @@ async function main() {
|
|||
|
||||
const surfW = Math.ceil(textProps.width) + 40
|
||||
const surfH = Math.ceil(textProps.height) + 40
|
||||
const surface = ck.MakeSurface(surfW, surfH)!
|
||||
const surface = expectDefined(ck.MakeSurface(surfW, surfH), 'CanvasKit surface')
|
||||
const renderer = new SkiaRenderer(ck, surface)
|
||||
renderer.fontProvider = fontProvider
|
||||
renderer.fontsLoaded = true
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { SceneGraph } from '#core/scene-graph'
|
|||
import type { SceneNode } from '#core/scene-graph'
|
||||
import { fontManager } from '#core/text'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
|
||||
async function main() {
|
||||
const ck = await initCanvasKit()
|
||||
|
||||
|
|
@ -55,7 +57,7 @@ async function main() {
|
|||
const textNode = graph.createNode('TEXT', pageId, textProps)
|
||||
const nodeId = textNode.id
|
||||
|
||||
const surface = ck.MakeSurface(800, 300)!
|
||||
const surface = expectDefined(ck.MakeSurface(800, 300), 'CanvasKit surface')
|
||||
const renderer = new SkiaRenderer(ck, surface)
|
||||
renderer.fontProvider = fontProvider
|
||||
renderer.fontsLoaded = true
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ export class FigmaHelper {
|
|||
}
|
||||
|
||||
private async canvasBounds() {
|
||||
return this.canvas.boundingBox().then((b) => b!)
|
||||
const bounds = await this.canvas.boundingBox()
|
||||
if (!bounds) throw new Error('Figma canvas bounds unavailable')
|
||||
return bounds
|
||||
}
|
||||
|
||||
async waitForRender() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
import type { Page } from '@playwright/test'
|
||||
|
||||
export function getSelectedIds(page: Page) {
|
||||
return page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.selectedIds.size)
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.state.selectedIds.size
|
||||
})
|
||||
}
|
||||
|
||||
export function getPageChildren(page: Page) {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).map((n) => ({
|
||||
id: n.id,
|
||||
type: n.type,
|
||||
|
|
@ -23,7 +28,8 @@ export function getPageChildren(page: Page) {
|
|||
|
||||
export function getSelectedNode(page: Page) {
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
if (!id) return null
|
||||
const n = store.graph.getNode(id)
|
||||
|
|
@ -53,7 +59,8 @@ export function getSelectedNode(page: Page) {
|
|||
|
||||
export function getNodeById(page: Page, id: string) {
|
||||
return page.evaluate((nodeId: string) => {
|
||||
const store = window.__OPEN_PENCIL_STORE__!
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const n = store.graph.getNode(nodeId)
|
||||
if (!n) return null
|
||||
return {
|
||||
|
|
@ -91,5 +98,9 @@ export function getNodeById(page: Page, id: string) {
|
|||
}
|
||||
|
||||
export function getEditingTextId(page: Page) {
|
||||
return page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.editingTextId)
|
||||
return page.evaluate(() => {
|
||||
const store = window.__OPEN_PENCIL_STORE__
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.state.editingTextId
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export { ALL_TOOLS }
|
|||
import { expectDefined } from './assert'
|
||||
|
||||
export interface ToolResult {
|
||||
id: string
|
||||
id?: string
|
||||
name?: string
|
||||
type?: string
|
||||
error?: string
|
||||
|
|
|
|||
Loading…
Reference in a new issue