diff --git a/tests/e2e/keyboard/shortcuts.spec.ts b/tests/e2e/keyboard/shortcuts.spec.ts index 70a0cbfff..873ca2ee1 100644 --- a/tests/e2e/keyboard/shortcuts.spec.ts +++ b/tests/e2e/keyboard/shortcuts.spec.ts @@ -1,5 +1,6 @@ import { expect, test, type Page } from '@playwright/test' +import { expectDefined } from '#tests/helpers/assert' import { CanvasHelper } from '#tests/helpers/canvas' let page: Page @@ -19,11 +20,19 @@ test.afterAll(async () => { }) function getActiveTool() { - return page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.activeTool) + return page.evaluate(() => { + const store = window.__OPEN_PENCIL_STORE__ + if (!store) throw new Error('OpenPencil store not initialized') + return store.state.activeTool + }) } function getSelectedCount() { - 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 + }) } function getPageChildren() { @@ -40,7 +49,19 @@ function getPageChildren() { } function getUIVisible() { - return page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.showUI) + return page.evaluate(() => { + const store = window.__OPEN_PENCIL_STORE__ + if (!store) throw new Error('OpenPencil store not initialized') + return store.state.showUI + }) +} + +function getZoom() { + return page.evaluate(() => { + const store = window.__OPEN_PENCIL_STORE__ + if (!store) throw new Error('OpenPencil store not initialized') + return store.state.zoom + }) } test.describe('tool switching', () => { @@ -125,11 +146,13 @@ test.describe('z-order shortcuts', () => { await canvas.drawRect(100, 100, 60, 60) const childrenBefore = await getPageChildren() - const firstId = childrenBefore[0].id + const firstId = expectDefined(childrenBefore[0], 'first page child').id // Select the first (bottom) node await page.evaluate((id) => { - window.__OPEN_PENCIL_STORE__!.select([id]) + const store = window.__OPEN_PENCIL_STORE__ + if (!store) throw new Error('OpenPencil store not initialized') + store.select([id]) }, firstId) await canvas.waitForRender() @@ -142,11 +165,13 @@ test.describe('z-order shortcuts', () => { test('[ sends to back', async () => { const children = await getPageChildren() - const lastId = children[children.length - 1].id + const lastId = expectDefined(children.at(-1), 'last page child').id // Select the last (top) node await page.evaluate((id) => { - window.__OPEN_PENCIL_STORE__!.select([id]) + const store = window.__OPEN_PENCIL_STORE__ + if (!store) throw new Error('OpenPencil store not initialized') + store.select([id]) }, lastId) await canvas.waitForRender() @@ -169,7 +194,7 @@ test.describe('group/ungroup shortcuts', () => { const children = await getPageChildren() const group = children.find((c) => c.type === 'GROUP') expect(group).toBeTruthy() - expect(group!.childIds.length).toBeGreaterThanOrEqual(2) + expect(group?.childIds.length).toBeGreaterThanOrEqual(2) }) test('⌘⇧G ungroups', async () => { @@ -229,7 +254,7 @@ test.describe('zoom shortcuts', () => { await page.keyboard.press('Meta+0') await canvas.waitForRender() - const zoomAfter = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.zoom) + const zoomAfter = await getZoom() expect(zoomAfter).toBe(1) }) @@ -237,7 +262,7 @@ test.describe('zoom shortcuts', () => { await page.keyboard.press('Meta+1') await canvas.waitForRender() - const zoom = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.zoom) + const zoom = await getZoom() expect(zoom).toBeGreaterThan(0) expect(zoom).toBeLessThanOrEqual(1) }) @@ -248,7 +273,7 @@ test.describe('zoom shortcuts', () => { await page.keyboard.press('Meta+2') await canvas.waitForRender() - const zoom = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.zoom) + const zoom = await getZoom() expect(zoom).toBeGreaterThan(0) expect(zoom).toBeLessThanOrEqual(1) }) @@ -257,7 +282,7 @@ test.describe('zoom shortcuts', () => { await page.keyboard.press('Shift+1') await canvas.waitForRender() - const zoom = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.zoom) + const zoom = await getZoom() expect(zoom).toBeGreaterThan(0) expect(zoom).toBeLessThanOrEqual(1) }) @@ -268,7 +293,7 @@ test.describe('zoom shortcuts', () => { await page.keyboard.press('Shift+2') await canvas.waitForRender() - const zoom = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.zoom) + const zoom = await getZoom() expect(zoom).toBeGreaterThan(0) expect(zoom).toBeLessThanOrEqual(1) }) diff --git a/tests/e2e/text/editing.spec.ts b/tests/e2e/text/editing.spec.ts index d3e0eae23..7016e9184 100644 --- a/tests/e2e/text/editing.spec.ts +++ b/tests/e2e/text/editing.spec.ts @@ -1,5 +1,6 @@ import { expect, test, type Page } from '@playwright/test' +import { expectDefined } from '#tests/helpers/assert' import { CanvasHelper } from '#tests/helpers/canvas' let page: Page @@ -55,7 +56,11 @@ function getPageChildren() { test('pressing T activates text tool', async () => { await page.keyboard.press('t') - const tool = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.activeTool) + const tool = await page.evaluate(() => { + const store = window.__OPEN_PENCIL_STORE__ + if (!store) throw new Error('OpenPencil store not initialized') + return store.state.activeTool + }) expect(tool).toBe('TEXT') }) @@ -73,8 +78,7 @@ test('clicking with text tool creates a text node', async () => { test('text node is selected after creation', async () => { const node = await getSelectedNode() - expect(node).toBeTruthy() - expect(node!.type).toBe('TEXT') + expect(node?.type).toBe('TEXT') }) test('typography section appears for text node', async () => { @@ -92,9 +96,8 @@ test('typography section appears for text node', async () => { test('text node has default font properties', async () => { const node = await getSelectedNode() - expect(node).toBeTruthy() - expect(node!.fontSize).toBeGreaterThan(0) - expect(node!.fontFamily).toBeTruthy() + expect(node?.fontSize).toBeGreaterThan(0) + expect(node?.fontFamily).toBeTruthy() }) test('creating text via store works', async () => { @@ -111,8 +114,8 @@ test('creating text via store works', async () => { await canvas.waitForRender() const node = await getSelectedNode() - expect(node!.text).toBe('Hello World') - expect(node!.fontSize).toBe(24) + expect(node?.text).toBe('Hello World') + expect(node?.fontSize).toBe(24) }) test('undo removes text node', async () => { @@ -130,10 +133,10 @@ test('frame tool creates FRAME node', async () => { await canvas.drag(400, 100, 600, 250) await canvas.waitForRender() - const node = await getSelectedNode() - expect(node!.type).toBe('FRAME') - expect(node!.width).toBeGreaterThan(0) - expect(node!.height).toBeGreaterThan(0) + const node = expectDefined(await getSelectedNode(), 'selected frame') + expect(node.type).toBe('FRAME') + expect(node.width).toBeGreaterThan(0) + expect(node.height).toBeGreaterThan(0) canvas.assertNoErrors() }) @@ -154,13 +157,17 @@ test('Enter key opens text editing and selects all without erasing', async () => await canvas.waitForRender() const before = await getSelectedNode() - expect(before!.text).toBe('Keep this text') - expect(before!.type).toBe('TEXT') + expect(before?.text).toBe('Keep this text') + expect(before?.type).toBe('TEXT') await page.keyboard.press('Enter') await page.waitForTimeout(200) - const editing = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.editingTextId) + const editing = await page.evaluate(() => { + const store = window.__OPEN_PENCIL_STORE__ + if (!store) throw new Error('OpenPencil store not initialized') + return store.state.editingTextId + }) expect(editing).toBe(textId) const after = await page.evaluate(() => { diff --git a/tests/engine/fig/import/legacy.test.ts b/tests/engine/fig/import/legacy.test.ts index 4a5e2e3df..67b113f80 100644 --- a/tests/engine/fig/import/legacy.test.ts +++ b/tests/engine/fig/import/legacy.test.ts @@ -2,6 +2,8 @@ import { describe, test, expect } from 'bun:test' import { importNodeChanges, type NodeChange } from '@open-pencil/core' +import { expectDefined } from '#tests/helpers/assert' + function doc(): NodeChange { return { guid: { sessionID: 0, localID: 0 }, @@ -125,8 +127,9 @@ describe('fig-import: gradient fills', () => { expect(n.fills).toHaveLength(1) expect(n.fills[0].type).toBe('GRADIENT_LINEAR') expect(n.fills[0].gradientStops).toHaveLength(2) - expect(n.fills[0].gradientStops![0].color.r).toBe(1) - expect(n.fills[0].gradientStops![1].color.b).toBe(1) + const gradientStops = expectDefined(n.fills[0].gradientStops, 'linear gradient stops') + expect(gradientStops[0]?.color.r).toBe(1) + expect(gradientStops[1]?.color.b).toBe(1) expect(n.fills[0].gradientTransform).toBeDefined() }) @@ -182,7 +185,7 @@ describe('fig-import: image fills', () => { const n = graph.getChildren(graph.getPages()[0].id)[0] expect(n.fills[0].type).toBe('IMAGE') expect(n.fills[0].imageHash).toBeDefined() - expect(n.fills[0].imageHash!.length).toBe(40) + expect(n.fills[0].imageHash?.length).toBe(40) expect(n.fills[0].imageScaleMode).toBe('FILL') }) @@ -350,9 +353,10 @@ describe('fig-import: arc data', () => { ]) const n = graph.getChildren(graph.getPages()[0].id)[0] expect(n.arcData).toBeDefined() - expect(n.arcData!.startingAngle).toBe(0) - expect(n.arcData!.endingAngle).toBeCloseTo(Math.PI) - expect(n.arcData!.innerRadius).toBe(0) + const arcData = expectDefined(n.arcData, 'ellipse arc data') + expect(arcData.startingAngle).toBe(0) + expect(arcData.endingAngle).toBeCloseTo(Math.PI) + expect(arcData.innerRadius).toBe(0) }) test('donut (inner radius)', () => { @@ -368,7 +372,7 @@ describe('fig-import: arc data', () => { } as Partial) ]) const n = graph.getChildren(graph.getPages()[0].id)[0] - expect(n.arcData!.innerRadius).toBe(0.5) + expect(n.arcData?.innerRadius).toBe(0.5) }) }) @@ -554,13 +558,19 @@ describe('fig-import: variable asset refs', () => { }) ]) - const primary = [...graph.variables.values()].find((v) => v.name === 'Primary')! - const resolved = graph.resolveColorVariable(primary.id)! + const primary = expectDefined( + [...graph.variables.values()].find((v) => v.name === 'Primary'), + 'Primary variable' + ) + const resolved = expectDefined(graph.resolveColorVariable(primary.id), 'resolved Primary color') expect(resolved.r).toBeCloseTo(0.14) expect(resolved.g).toBeCloseTo(0.39) expect(resolved.b).toBeCloseTo(0.92) - const text = [...graph.getAllNodes()].find((n) => n.type === 'TEXT' && n.text === 'browse')! + const text = expectDefined( + [...graph.getAllNodes()].find((n) => n.type === 'TEXT' && n.text === 'browse'), + 'browse text node' + ) expect(text.fills[0].color.r).toBeCloseTo(0.14) expect(text.fills[0].color.g).toBeCloseTo(0.39) expect(text.fills[0].color.b).toBeCloseTo(0.92)