openpencil/tests/engine/cli/eval.test.ts
Danila Poyarkov 654aa421e9 chore(lint): reduce test rule exceptions
- Re-enable duplicate imports, radix, prefer-const, nested ternary, and silent catch checks in tests
- Drop the redundant spec-file override covered by the broader test override
- Fix the remaining test offenders surfaced by the stricter rules
2026-05-06 02:31:07 +03:00

249 lines
7.2 KiB
TypeScript

import { describe, expect, test, setDefaultTimeout } from 'bun:test'
import { randomUUID } from 'crypto'
import { tmpdir } from 'os'
import { join } from 'path'
import { cliSourcePath, repoPath } from '#tests/helpers/paths'
import { heavy } from '#tests/helpers/test-utils'
setDefaultTimeout(30_000)
const CLI = cliSourcePath('index.ts')
const FIXTURE = repoPath('tests/fixtures/gold-preview.fig')
async function run(
args: string[],
stdin?: string
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
const proc = Bun.spawn(['bun', CLI, ...args], {
stdout: 'pipe',
stderr: 'pipe',
stdin: stdin ? Buffer.from(stdin) : undefined
})
const [stdout, stderr] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text()
])
const exitCode = await proc.exited
return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode }
}
heavy('eval CLI', () => {
test('returns page name', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--code',
'return figma.currentPage.name'
])
expect(exitCode).toBe(0)
expect(stdout.length).toBeGreaterThan(0)
})
test('returns primitive number', async () => {
const { stdout, exitCode } = await run(['eval', FIXTURE, '--code', 'return 42'])
expect(exitCode).toBe(0)
expect(stdout).toBe('42')
})
test('returns page count', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--code',
'return figma.root.children.length'
])
expect(exitCode).toBe(0)
const count = parseInt(stdout, 10)
expect(count).toBeGreaterThan(0)
})
test('findAll queries nodes', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--json',
'--code',
'return figma.currentPage.findAll(n => n.type === "TEXT").slice(0, 3).map(n => ({ name: n.name, type: n.type }))'
])
expect(exitCode).toBe(0)
const data = JSON.parse(stdout)
expect(Array.isArray(data)).toBe(true)
expect(data.length).toBeGreaterThan(0)
expect(data[0].type).toBe('TEXT')
})
test('create frame and return properties', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--json',
'--code',
`const f = figma.createFrame(); f.name = "Test"; f.resize(300, 200); return f`
])
expect(exitCode).toBe(0)
const data = JSON.parse(stdout)
expect(data.name).toBe('Test')
expect(data.width).toBe(300)
expect(data.height).toBe(200)
expect(data.type).toBe('FRAME')
})
test('auto-layout script', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--json',
'--code',
`const f = figma.createFrame()
f.layoutMode = "VERTICAL"
f.itemSpacing = 8
f.paddingTop = f.paddingBottom = 16
return { layoutMode: f.layoutMode, itemSpacing: f.itemSpacing, paddingTop: f.paddingTop }`
])
expect(exitCode).toBe(0)
const data = JSON.parse(stdout)
expect(data.layoutMode).toBe('VERTICAL')
expect(data.itemSpacing).toBe(8)
expect(data.paddingTop).toBe(16)
})
test('text node with characters', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--json',
'--code',
`const t = figma.createText()
t.characters = "Hello"
t.fontSize = 24
return { characters: t.characters, fontSize: t.fontSize }`
])
expect(exitCode).toBe(0)
const data = JSON.parse(stdout)
expect(data.characters).toBe('Hello')
expect(data.fontSize).toBe(24)
})
test('top-level await works', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--code',
'await figma.loadFontAsync({ family: "Inter", style: "Regular" }); return "ok"'
])
expect(exitCode).toBe(0)
expect(stdout).toContain('ok')
})
test('stdin reads code from pipe', async () => {
const proc = Bun.spawn(['bun', CLI, 'eval', FIXTURE, '--stdin'], {
stdout: 'pipe',
stderr: 'pipe',
stdin: 'pipe'
})
proc.stdin.write('return figma.currentPage.name')
proc.stdin.end()
const [stdout, stderr] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text()
])
const exitCode = await proc.exited
if (exitCode !== 0) throw new Error(`exit ${exitCode}: ${stderr}`)
expect(stdout.trim().length).toBeGreaterThan(0)
})
test('--json outputs valid JSON', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--json',
'--code',
'return { a: 1, b: "two" }'
])
expect(exitCode).toBe(0)
const data = JSON.parse(stdout)
expect(data).toEqual({ a: 1, b: 'two' })
})
test('syntax error exits with error', async () => {
const { exitCode, stderr } = await run(['eval', FIXTURE, '--code', 'return {{{'])
expect(exitCode).not.toBe(0)
expect(stderr.length).toBeGreaterThan(0)
})
test('runtime error exits with error', async () => {
const { exitCode, stderr } = await run(['eval', FIXTURE, '--code', 'throw new Error("boom")'])
expect(exitCode).not.toBe(0)
expect(stderr).toContain('boom')
})
test('no code exits with error', async () => {
const { exitCode, stderr } = await run(['eval', FIXTURE])
expect(exitCode).not.toBe(0)
expect(stderr).toContain('code')
})
test('undefined result produces no output', async () => {
const { stdout, exitCode } = await run(['eval', FIXTURE, '--code', 'figma.createFrame()'])
expect(exitCode).toBe(0)
expect(stdout).toBe('')
})
test('--output writes file', async () => {
const outFile = join(tmpdir(), `eval-test-${randomUUID()}.fig`)
const { exitCode, stderr } = await run([
'eval',
FIXTURE,
'--quiet',
'--code',
'const f = figma.createFrame(); f.name = "Written"',
'-o',
outFile
])
// exportFigFile may fail on files with COMPONENT nodes (Kiwi schema limitation)
// but the eval itself should succeed — verify it at least attempted to write
if (exitCode === 0) {
const stat = Bun.file(outFile)
expect(await stat.exists()).toBe(true)
expect(stat.size).toBeGreaterThan(0)
} else {
expect(stderr).toContain('NodeType')
}
})
test('array of nodes serializes', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--json',
'--code',
`const a = figma.createFrame(); a.name = "A"
const b = figma.createRectangle(); b.name = "B"
return [a, b]`
])
expect(exitCode).toBe(0)
const data = JSON.parse(stdout)
expect(Array.isArray(data)).toBe(true)
expect(data[0].name).toBe('A')
expect(data[1].name).toBe('B')
})
test('getNodeById on real file', async () => {
const { stdout, exitCode } = await run([
'eval',
FIXTURE,
'--json',
'--code',
`const page = figma.currentPage
const first = page.children[0]
const found = figma.getNodeById(first.id)
return { same: found.id === first.id, name: found.name }`
])
expect(exitCode).toBe(0)
const data = JSON.parse(stdout)
expect(data.same).toBe(true)
})
})