openpencil/tests/engine/io/fig/export/images.test.ts
2026-05-17 13:02:00 +03:00

124 lines
3.4 KiB
TypeScript

import { beforeAll, describe, expect, test } from 'bun:test'
import { unzipSync } from 'fflate'
import { exportFigFile, FigmaAPI, initCodec, parseFigFile, SceneGraph } from '@open-pencil/core'
import { expectDefined } from '#tests/helpers/assert'
describe('fig export/import with images', () => {
beforeAll(async () => {
await initCodec()
})
test('exported zip contains images entries', async () => {
const graph = new SceneGraph()
const page = graph.getPages()[0]
const figma = new FigmaAPI(graph)
const bytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x00, 0x01, 0x02, 0x03])
const { hash } = figma.createImage(bytes)
graph.createNode('RECTANGLE', page.id, {
name: 'ImageNode',
width: 100,
height: 100,
fills: [
{
type: 'IMAGE',
color: { r: 0, g: 0, b: 0, a: 1 },
opacity: 1,
visible: true,
imageHash: hash,
imageScaleMode: 'FILL'
}
]
})
const zip = await exportFigFile(graph)
const entries = unzipSync(zip)
expect(entries[`images/${hash}`]).toBeDefined()
expect(new Uint8Array(entries[`images/${hash}`])).toEqual(bytes)
})
test('graph without images has no images entries', async () => {
const graph = new SceneGraph()
graph.createNode('RECTANGLE', graph.getPages()[0].id, {
name: 'Plain',
width: 50,
height: 50
})
const zip = await exportFigFile(graph)
const entries = unzipSync(zip)
const imageKeys = Object.keys(entries).filter((k) => k.startsWith('images/'))
expect(imageKeys).toHaveLength(0)
})
test('round-trip preserves images', async () => {
const graph = new SceneGraph()
const page = graph.getPages()[0]
const figma = new FigmaAPI(graph)
const bytes1 = new Uint8Array([11, 22, 33, 44, 55])
const bytes2 = new Uint8Array([66, 77, 88, 99])
const { hash: hash1 } = figma.createImage(bytes1)
const { hash: hash2 } = figma.createImage(bytes2)
graph.createNode('RECTANGLE', page.id, {
name: 'Img1',
width: 100,
height: 100,
fills: [
{
type: 'IMAGE',
color: { r: 0, g: 0, b: 0, a: 1 },
opacity: 1,
visible: true,
imageHash: hash1,
imageScaleMode: 'FILL'
}
]
})
graph.createNode('ELLIPSE', page.id, {
name: 'Img2',
width: 80,
height: 80,
fills: [
{
type: 'IMAGE',
color: { r: 0, g: 0, b: 0, a: 1 },
opacity: 1,
visible: true,
imageHash: hash2,
imageScaleMode: 'FIT'
}
]
})
const zip = await exportFigFile(graph)
const restored = await parseFigFile(zip.buffer as ArrayBuffer)
expect(restored.images.size).toBe(2)
expect(new Uint8Array(expectDefined(restored.images.get(hash1), 'restored image 1'))).toEqual(
bytes1
)
expect(new Uint8Array(expectDefined(restored.images.get(hash2), 'restored image 2'))).toEqual(
bytes2
)
const restoredImg1 = expectDefined(
[...restored.getAllNodes()].find((node) => node.name === 'Img1'),
'restored image node 1'
)
const restoredImg2 = expectDefined(
[...restored.getAllNodes()].find((node) => node.name === 'Img2'),
'restored image node 2'
)
expect(restoredImg1.fills[0].imageHash).toBe(hash1)
expect(restoredImg2.fills[0].imageHash).toBe(hash2)
})
})