openpencil/tests/engine/tools/read.test.ts
Danila Poyarkov a63ac93a3d chore(lint): enforce type imports in tests
- Remove the broad test exception for consistent type imports
- Mark test-only core and helper imports as type imports where appropriate
- Keep targeted validation for affected engine tests
2026-05-06 02:35:57 +03:00

185 lines
5.7 KiB
TypeScript

import { describe, expect, test } from 'bun:test'
import { FigmaAPI } from '@open-pencil/core'
import { getTool, setupToolTest, type ALL_TOOLS, type ToolResult } from '#tests/helpers/tools'
describe('find_nodes', () => {
test('finds by name', () => {
const { figma } = setupToolTest()
const rect = figma.createRectangle()
rect.name = 'Button Primary'
const text = figma.createText()
text.name = 'Label'
const tool = getTool('find_nodes')
const result = tool.execute(figma, { name: 'button' }) as ToolResult
expect(result.count).toBe(1)
expect(result.nodes[0].name).toBe('Button Primary')
})
test('finds by type', () => {
const { figma } = setupToolTest()
figma.createRectangle()
figma.createRectangle()
figma.createText()
const tool = getTool('find_nodes')
const result = tool.execute(figma, { type: 'RECTANGLE' }) as ToolResult
expect(result.count).toBe(2)
})
})
describe('query_nodes', () => {
test('finds all frames with //FRAME', async () => {
const { figma } = setupToolTest()
const f1 = figma.createFrame()
f1.resize(200, 200)
f1.name = 'Frame A'
const f2 = figma.createFrame()
f2.resize(300, 300)
f2.name = 'Frame B'
figma.createRectangle()
const tool = getTool('query_nodes')
const result = (await tool.execute(figma, { selector: '//FRAME' })) as ToolResult
expect(result.count).toBe(2)
expect(result.nodes?.every((n) => n.type === 'FRAME')).toBe(true)
})
test('finds by attribute //RECTANGLE[@width < 200]', async () => {
const { figma } = setupToolTest()
const small = figma.createRectangle()
small.resize(100, 50)
small.name = 'Small'
const big = figma.createRectangle()
big.resize(400, 400)
big.name = 'Big'
const tool = getTool('query_nodes')
const result = (await tool.execute(figma, {
selector: '//RECTANGLE[@width < 200]'
})) as ToolResult
expect(result.count).toBe(1)
expect(result.nodes[0].name).toBe('Small')
})
test('finds by name with contains', async () => {
const { figma } = setupToolTest()
const t1 = figma.createText()
t1.name = 'Label Primary'
const t2 = figma.createText()
t2.name = 'Title'
const t3 = figma.createText()
t3.name = 'Label Secondary'
const tool = getTool('query_nodes')
const result = (await tool.execute(figma, {
selector: '//TEXT[contains(@name, "Label")]'
})) as ToolResult
expect(result.count).toBe(2)
expect(result.nodes?.every((n) => n.name.includes('Label'))).toBe(true)
})
test('returns error for invalid xpath', async () => {
const { figma } = setupToolTest()
const tool = getTool('query_nodes')
const result = (await tool.execute(figma, { selector: '///invalid[[[[' })) as ToolResult
expect(result.error).toBeTruthy()
expect(result.error).toContain('XPath error')
})
test('respects limit param', async () => {
const { figma } = setupToolTest()
for (let i = 0; i < 10; i++) {
const r = figma.createRectangle()
r.name = `Rect ${i}`
}
const tool = getTool('query_nodes')
const result = (await tool.execute(figma, { selector: '//RECTANGLE', limit: 3 })) as ToolResult
expect(result.count).toBe(3)
})
test('returns empty array when nothing matches', async () => {
const { figma } = setupToolTest()
figma.createRectangle()
const tool = getTool('query_nodes')
const result = (await tool.execute(figma, { selector: '//ELLIPSE' })) as ToolResult
expect(result.count).toBe(0)
expect(result.nodes).toEqual([])
})
})
describe('get_node', () => {
test('returns node details', () => {
const { figma } = setupToolTest()
const rect = figma.createRectangle()
rect.name = 'Test Rect'
rect.resize(100, 50)
const tool = getTool('get_node')
const result = tool.execute(figma, { id: rect.id }) as ToolResult
expect(result.name).toBe('Test Rect')
expect(result.width).toBe(100)
expect(result.height).toBe(50)
})
})
describe('page tools', () => {
test('list_pages returns pages', () => {
const { figma } = setupToolTest()
const tool = getTool('list_pages')
const result = tool.execute(figma, {}) as ToolResult
expect(result.pages.length).toBeGreaterThanOrEqual(1)
})
test('switch_page changes page', () => {
const { figma } = setupToolTest()
const page2 = figma.createPage()
page2.name = 'Page 2'
const tool = getTool('switch_page')
tool.execute(figma, { page: 'Page 2' })
expect(figma.currentPage.name).toBe('Page 2')
})
test('switch_page persists across separate FigmaAPI instances (RPC simulation)', () => {
const { graph } = setupToolTest()
const switchPage = getTool('switch_page')
const getCurrentPage = getTool('get_current_page')
const createPage = getTool('create_page')
let currentPageId = graph.getPages()[0].id
function rpcCall(tool: (typeof ALL_TOOLS)[number], args: Record<string, unknown>) {
const figma = new FigmaAPI(graph)
figma.currentPage = figma.wrapNode(currentPageId)
const result = tool.execute(figma, args)
if (figma.currentPageId !== currentPageId) {
currentPageId = figma.currentPageId
}
return result
}
rpcCall(createPage, { name: 'Second' })
rpcCall(switchPage, { page: 'Second' })
const result = rpcCall(getCurrentPage, {}) as { id: string; name: string }
expect(result.name).toBe('Second')
})
})
describe('eval', () => {
test('executes code with figma api', async () => {
const { figma } = setupToolTest()
const tool = getTool('eval')
const result = await tool.execute(figma, {
code: 'const r = figma.createRectangle(); r.name = "FromEval"; return r.name;'
})
expect(result).toBe('FromEval')
})
})