chore(tests): remove targeted non-null assertions

- Replace low-count E2E non-null assertions with store guards and optional expectations
- Use expectDefined for tool adapter and XPath node lookups
- Validate affected engine and E2E tests
This commit is contained in:
Danila Poyarkov 2026-05-06 03:13:58 +03:00
parent 39675b2969
commit 6ec18f1dfb
8 changed files with 48 additions and 21 deletions

View file

@ -63,9 +63,11 @@ test('autosave triggers after scene changes with a file handle', async () => {
await canvas.drawRect(400, 400, 60, 60)
// Check that the scene version changed
const versionAfterDraw = await page.evaluate(
() => window.__OPEN_PENCIL_STORE__!.state.sceneVersion
)
const versionAfterDraw = await page.evaluate(() => {
const store = window.__OPEN_PENCIL_STORE__
if (!store) throw new Error('OpenPencil store not initialized')
return store.state.sceneVersion
})
expect(versionAfterDraw).toBeGreaterThan(0)
// Wait for autosave debounce (3s) + buffer

View file

@ -27,7 +27,11 @@ function getPageChildCount() {
}
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 getSelectedNodes() {
@ -35,7 +39,8 @@ function getSelectedNodes() {
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)!
const n = store.graph.getNode(id)
if (!n) throw new Error(`Selected node ${id} not found`)
return {
id: n.id,
name: n.name,

View file

@ -61,7 +61,9 @@ async function getSceneTree(): Promise<SceneTreeNode> {
async function getSelectedCount(): Promise<number> {
return page.evaluate(() => {
return window.__OPEN_PENCIL_STORE__!.state.selectedIds.size
const store = window.__OPEN_PENCIL_STORE__
if (!store) throw new Error('OpenPencil store not initialized')
return store.state.selectedIds.size
})
}

View file

@ -27,7 +27,11 @@ function getPages() {
}
function getCurrentPageId() {
return page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.currentPageId)
return page.evaluate(() => {
const store = window.__OPEN_PENCIL_STORE__
if (!store) throw new Error('OpenPencil store not initialized')
return store.state.currentPageId
})
}
function getPageChildCount() {
@ -141,7 +145,9 @@ test('rename page via store', async () => {
await page.evaluate(
([id, name]) => {
window.__OPEN_PENCIL_STORE__!.renamePage(id, name)
const store = window.__OPEN_PENCIL_STORE__
if (!store) throw new Error('OpenPencil store not initialized')
store.renamePage(id, name)
},
[currentId, 'Renamed Page'] as [string, string]
)
@ -149,7 +155,7 @@ test('rename page via store', async () => {
const updated = await getPages()
const renamed = updated.find((p) => p.id === currentId)
expect(renamed!.name).toBe('Renamed Page')
expect(renamed?.name).toBe('Renamed Page')
canvas.assertNoErrors()
})

View file

@ -45,8 +45,8 @@ test('independent corners toggle shows per-corner inputs', async () => {
await canvas.waitForRender()
const flags = await getSelectedNodeFlags()
expect(flags!.type).toBe('FRAME')
expect(flags!.independentCorners).toBe(false)
expect(flags?.type).toBe('FRAME')
expect(flags?.independentCorners).toBe(false)
const toggle = page.locator('[data-test-id="independent-corners-toggle"]')
await expect(toggle).toBeVisible()
@ -54,7 +54,7 @@ test('independent corners toggle shows per-corner inputs', async () => {
await toggle.click()
await canvas.waitForRender()
expect((await getSelectedNodeFlags())!.independentCorners).toBe(true)
expect((await getSelectedNodeFlags())?.independentCorners).toBe(true)
const grid = page.locator('[data-test-id="independent-corners-grid"]')
await expect(grid).toBeVisible()
const cornerInputs = grid.locator('[data-test-id="scrub-input"]')

View file

@ -6,6 +6,8 @@ import * as v from 'valibot'
import { ALL_TOOLS, FigmaAPI, SceneGraph, toolsToAI } from '@open-pencil/core'
import { expectDefined } from '#tests/helpers/assert'
type AdapterTool = { execute(args: Record<string, unknown>): Promise<unknown>; description: string }
function adapterTool(tools: Record<string, unknown>, name: string): AdapterTool {
@ -62,7 +64,7 @@ describe('AI adapter', () => {
expect(result.type).toBe('RECTANGLE')
expect(result.name).toBe('Test Rect')
const node = figma.getNodeById(result.id)!
const node = expectDefined(figma.getNodeById(result.id), 'created node')
expect(node.x).toBe(10)
expect(node.y).toBe(20)
expect(node.width).toBe(100)
@ -76,7 +78,7 @@ describe('AI adapter', () => {
const setFill = adapterTool(tools, 'set_fill')
await setFill.execute({ id: rect.id, color: '#00ff00' })
const fills = figma.getNodeById(rect.id)!.fills
const fills = expectDefined(figma.getNodeById(rect.id), 'filled rectangle').fills
expect(fills.length).toBe(1)
expect(fills[0].color.g).toBeCloseTo(1)
})
@ -167,7 +169,7 @@ describe('AI adapter', () => {
padding: 16
})
const node = figma.getNodeById(frame.id)!
const node = expectDefined(figma.getNodeById(frame.id), 'layout frame')
expect(node.layoutMode).toBe('HORIZONTAL')
expect(node.itemSpacing).toBe(8)
expect(node.paddingLeft).toBe(16)

View file

@ -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('delete_node', () => {
@ -37,7 +38,7 @@ describe('rename_node', () => {
const tool = getTool('rename_node')
tool.execute(figma, { id: rect.id, name: 'My Rectangle' })
expect(figma.getNodeById(rect.id)!.name).toBe('My Rectangle')
expect(expectDefined(figma.getNodeById(rect.id), 'renamed rectangle').name).toBe('My Rectangle')
})
})
@ -52,7 +53,11 @@ describe('reparent_node', () => {
const tool = getTool('reparent_node')
tool.execute(figma, { id: rect.id, parent_id: frame.id })
expect(figma.getNodeById(frame.id)!.children.some((c) => c.id === rect.id)).toBe(true)
expect(
expectDefined(figma.getNodeById(frame.id), 'target frame').children.some(
(c) => c.id === rect.id
)
).toBe(true)
})
})
@ -68,7 +73,10 @@ describe('group_nodes', () => {
const result = tool.execute(figma, { ids: [r1.id, r2.id] }) as ToolResult
expect(result.type).toBe('GROUP')
const group = figma.getNodeById(result.id)!
const group = expectDefined(
figma.getNodeById(expectDefined(result.id, 'group id')),
'created group'
)
expect(group.children.length).toBe(2)
})
})

View file

@ -2,6 +2,8 @@ import { describe, expect, test } from 'bun:test'
import { FigmaAPI, SceneGraph, matchByXPath, nodeToXPath, queryByXPath } from '@open-pencil/core'
import { expectDefined } from '#tests/helpers/assert'
function setup() {
const graph = new SceneGraph()
const figma = new FigmaAPI(graph)
@ -164,7 +166,7 @@ describe('nodeToXPath', () => {
btn.name = 'Button'
frame.appendChild(btn)
const xpath = nodeToXPath(graph, btn.id)!
const xpath = expectDefined(nodeToXPath(graph, btn.id), 'button XPath')
const results = await queryByXPath(graph, xpath)
expect(results.length).toBe(1)
expect(results[0].id).toBe(btn.id)
@ -187,7 +189,7 @@ describe('matchByXPath', () => {
rect.resize(100, 100)
rect.name = 'TestRect'
const sceneNode = graph.getNode(rect.id)!
const sceneNode = expectDefined(graph.getNode(rect.id), 'matching rectangle')
const result = await matchByXPath(graph, '@name = "TestRect"', sceneNode)
expect(result).toBe(true)
})
@ -198,7 +200,7 @@ describe('matchByXPath', () => {
rect.resize(100, 100)
rect.name = 'Other'
const sceneNode = graph.getNode(rect.id)!
const sceneNode = expectDefined(graph.getNode(rect.id), 'non-matching rectangle')
const result = await matchByXPath(graph, '@name = "TestRect"', sceneNode)
expect(result).toBe(false)
})