diff --git a/tests/e2e/app/menu.spec.ts b/tests/e2e/app/menu.spec.ts index 16302ca5f..0752797ad 100644 --- a/tests/e2e/app/menu.spec.ts +++ b/tests/e2e/app/menu.spec.ts @@ -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 }) diff --git a/tests/e2e/canvas/frame-overlays.spec.ts b/tests/e2e/canvas/frame-overlays.spec.ts index cec096261..a55af39c9 100644 --- a/tests/e2e/canvas/frame-overlays.spec.ts +++ b/tests/e2e/canvas/frame-overlays.spec.ts @@ -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() diff --git a/tests/e2e/canvas/manipulation.spec.ts b/tests/e2e/canvas/manipulation.spec.ts index b5d3dfda0..50b556003 100644 --- a/tests/e2e/canvas/manipulation.spec.ts +++ b/tests/e2e/canvas/manipulation.spec.ts @@ -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 diff --git a/tests/e2e/clipboard/copy-paste.spec.ts b/tests/e2e/clipboard/copy-paste.spec.ts index 9aef35b69..591cc3f9f 100644 --- a/tests/e2e/clipboard/copy-paste.spec.ts +++ b/tests/e2e/clipboard/copy-paste.spec.ts @@ -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() diff --git a/tests/e2e/code/panel.spec.ts b/tests/e2e/code/panel.spec.ts index 05ec8c8cc..8b37c0665 100644 --- a/tests/e2e/code/panel.spec.ts +++ b/tests/e2e/code/panel.spec.ts @@ -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]) }) diff --git a/tests/e2e/color-picker/basic.spec.ts b/tests/e2e/color-picker/basic.spec.ts index 05af45481..4a966c86e 100644 --- a/tests/e2e/color-picker/basic.spec.ts +++ b/tests/e2e/color-picker/basic.spec.ts @@ -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) diff --git a/tests/e2e/color-picker/demo-card.spec.ts b/tests/e2e/color-picker/demo-card.spec.ts index 6069797c1..c6bc5eede 100644 --- a/tests/e2e/color-picker/demo-card.spec.ts +++ b/tests/e2e/color-picker/demo-card.spec.ts @@ -26,7 +26,8 @@ async function selectDemoCard(page: Parameters[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[0]['page'], canvas: async function getSelectedFill(page: Parameters[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 diff --git a/tests/e2e/components.spec.ts b/tests/e2e/components.spec.ts index b7815f910..ad537e4b4 100644 --- a/tests/e2e/components.spec.ts +++ b/tests/e2e/components.spec.ts @@ -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 ?? [] } diff --git a/tests/e2e/context-menu/basic.spec.ts b/tests/e2e/context-menu/basic.spec.ts index d75a967aa..c83a3b93b 100644 --- a/tests/e2e/context-menu/basic.spec.ts +++ b/tests/e2e/context-menu/basic.spec.ts @@ -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() diff --git a/tests/e2e/design/panel.spec.ts b/tests/e2e/design/panel.spec.ts index c48f00c82..a47c7bbbb 100644 --- a/tests/e2e/design/panel.spec.ts +++ b/tests/e2e/design/panel.spec.ts @@ -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 }) } diff --git a/tests/e2e/editor/duplicate-undo.spec.ts b/tests/e2e/editor/duplicate-undo.spec.ts index 7e92b4deb..ff13aa3a2 100644 --- a/tests/e2e/editor/duplicate-undo.spec.ts +++ b/tests/e2e/editor/duplicate-undo.spec.ts @@ -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, diff --git a/tests/e2e/fonts/settings.spec.ts b/tests/e2e/fonts/settings.spec.ts index 3c476a7f5..2a3f7b4ca 100644 --- a/tests/e2e/fonts/settings.spec.ts +++ b/tests/e2e/fonts/settings.spec.ts @@ -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]) diff --git a/tests/e2e/keyboard/shortcuts.spec.ts b/tests/e2e/keyboard/shortcuts.spec.ts index f513d1c85..70a0cbfff 100644 --- a/tests/e2e/keyboard/shortcuts.spec.ts +++ b/tests/e2e/keyboard/shortcuts.spec.ts @@ -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 }) diff --git a/tests/e2e/pages/multi-page.spec.ts b/tests/e2e/pages/multi-page.spec.ts index be1b3ecf2..6a74606a2 100644 --- a/tests/e2e/pages/multi-page.spec.ts +++ b/tests/e2e/pages/multi-page.spec.ts @@ -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() diff --git a/tests/e2e/perf/basic.spec.ts b/tests/e2e/perf/basic.spec.ts index de0b88d13..c13f0a866 100644 --- a/tests/e2e/perf/basic.spec.ts +++ b/tests/e2e/perf/basic.spec.ts @@ -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 diff --git a/tests/e2e/properties/corner-stroke-toggles.spec.ts b/tests/e2e/properties/corner-stroke-toggles.spec.ts index 79a93ba6a..2962f9d53 100644 --- a/tests/e2e/properties/corner-stroke-toggles.spec.ts +++ b/tests/e2e/properties/corner-stroke-toggles.spec.ts @@ -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) diff --git a/tests/e2e/properties/effects.spec.ts b/tests/e2e/properties/effects.spec.ts index 023fd3124..bde686716 100644 --- a/tests/e2e/properties/effects.spec.ts +++ b/tests/e2e/properties/effects.spec.ts @@ -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', diff --git a/tests/e2e/properties/panel.spec.ts b/tests/e2e/properties/panel.spec.ts index 91eb92332..154bb64b7 100644 --- a/tests/e2e/properties/panel.spec.ts +++ b/tests/e2e/properties/panel.spec.ts @@ -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 diff --git a/tests/e2e/properties/visibility.spec.ts b/tests/e2e/properties/visibility.spec.ts index 046a93664..49c68239d 100644 --- a/tests/e2e/properties/visibility.spec.ts +++ b/tests/e2e/properties/visibility.spec.ts @@ -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) diff --git a/tests/e2e/scene/cache.spec.ts b/tests/e2e/scene/cache.spec.ts index 75dc14681..81a034d0c 100644 --- a/tests/e2e/scene/cache.spec.ts +++ b/tests/e2e/scene/cache.spec.ts @@ -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() diff --git a/tests/e2e/snap/guides.spec.ts b/tests/e2e/snap/guides.spec.ts index a04cf972e..07a9c3c8f 100644 --- a/tests/e2e/snap/guides.spec.ts +++ b/tests/e2e/snap/guides.spec.ts @@ -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]) diff --git a/tests/e2e/stroke-picker/basic.spec.ts b/tests/e2e/stroke-picker/basic.spec.ts index 6ec05b96a..e11e0cecc 100644 --- a/tests/e2e/stroke-picker/basic.spec.ts +++ b/tests/e2e/stroke-picker/basic.spec.ts @@ -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') ?? diff --git a/tests/e2e/text/editing.spec.ts b/tests/e2e/text/editing.spec.ts index 73cafb2f7..d3e0eae23 100644 --- a/tests/e2e/text/editing.spec.ts +++ b/tests/e2e/text/editing.spec.ts @@ -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 diff --git a/tests/e2e/text/formatting.spec.ts b/tests/e2e/text/formatting.spec.ts index 9fa974078..59aab4efa 100644 --- a/tests/e2e/text/formatting.spec.ts +++ b/tests/e2e/text/formatting.spec.ts @@ -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) diff --git a/tests/e2e/variables/dialog.spec.ts b/tests/e2e/variables/dialog.spec.ts index 57bb646e5..cd9e7fed7 100644 --- a/tests/e2e/variables/dialog.spec.ts +++ b/tests/e2e/variables/dialog.spec.ts @@ -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++ diff --git a/tests/e2e/viewport/zoom-pan.spec.ts b/tests/e2e/viewport/zoom-pan.spec.ts index 0860d0d7c..7b5a0ea79 100644 --- a/tests/e2e/viewport/zoom-pan.spec.ts +++ b/tests/e2e/viewport/zoom-pan.spec.ts @@ -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 diff --git a/tests/engine/fonts/loading.test.ts b/tests/engine/fonts/loading.test.ts index 268cab40b..f81dc8f29 100644 --- a/tests/engine/fonts/loading.test.ts +++ b/tests/engine/fonts/loading.test.ts @@ -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) }) diff --git a/tests/engine/iconify.test.ts b/tests/engine/iconify.test.ts index 47b3f945b..ac9c33f43 100644 --- a/tests/engine/iconify.test.ts +++ b/tests/engine/iconify.test.ts @@ -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) }) }) diff --git a/tests/engine/okhcl.test.ts b/tests/engine/okhcl.test.ts index 54ae2e08f..2663d8b45 100644 --- a/tests/engine/okhcl.test.ts +++ b/tests/engine/okhcl.test.ts @@ -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 } }) diff --git a/tests/engine/snap.test.ts b/tests/engine/snap.test.ts index 731b4e965..691e9fce7 100644 --- a/tests/engine/snap.test.ts +++ b/tests/engine/snap.test.ts @@ -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 & { 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) }) diff --git a/tests/engine/tools/create.test.ts b/tests/engine/tools/create.test.ts index 46fb1e53a..4b6634d0d 100644 --- a/tests/engine/tools/create.test.ts +++ b/tests/engine/tools/create.test.ts @@ -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) }) }) diff --git a/tests/engine/visual-inner-shadow-counter.ts b/tests/engine/visual-inner-shadow-counter.ts index 5d2c94d0d..1ec48ee35 100644 --- a/tests/engine/visual-inner-shadow-counter.ts +++ b/tests/engine/visual-inner-shadow-counter.ts @@ -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 diff --git a/tests/engine/visual-sdf-test-matrix.ts b/tests/engine/visual-sdf-test-matrix.ts index 0b4fe24b4..f305ea43b 100644 --- a/tests/engine/visual-sdf-test-matrix.ts +++ b/tests/engine/visual-sdf-test-matrix.ts @@ -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 diff --git a/tests/engine/visual-verify-text-inner-shadow.ts b/tests/engine/visual-verify-text-inner-shadow.ts index 7ad9ae61a..383b1644f 100644 --- a/tests/engine/visual-verify-text-inner-shadow.ts +++ b/tests/engine/visual-verify-text-inner-shadow.ts @@ -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 diff --git a/tests/helpers/figma.ts b/tests/helpers/figma.ts index fc17ecaf4..996844ae9 100644 --- a/tests/helpers/figma.ts +++ b/tests/helpers/figma.ts @@ -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() { diff --git a/tests/helpers/store.ts b/tests/helpers/store.ts index 67c63e7d7..b256d7de0 100644 --- a/tests/helpers/store.ts +++ b/tests/helpers/store.ts @@ -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 + }) } diff --git a/tests/helpers/tools.ts b/tests/helpers/tools.ts index 013fcfbd5..e31683e7c 100644 --- a/tests/helpers/tools.ts +++ b/tests/helpers/tools.ts @@ -5,7 +5,7 @@ export { ALL_TOOLS } import { expectDefined } from './assert' export interface ToolResult { - id: string + id?: string name?: string type?: string error?: string