openpencil/packages/fig/tests/export.test.ts
Danila Poyarkov c493bd0872 fix(fig): preserve vector network origin when resizing
- Scale imported vector vertices around their own geometry origin\n- Cover offset geometry and tangent scaling
2026-08-10 12:52:43 +03:00

139 lines
3.9 KiB
TypeScript

import { describe, expect, test } from 'bun:test'
import { SceneGraph } from '@open-pencil/scene-graph'
import {
buildComponentPropIndex,
fractionalPosition,
mapToFigmaType,
sceneNodeToKiwi,
type FigNodeChangeExportRuntime
} from '../src/node-change'
describe('@open-pencil/fig SceneGraph export policy', () => {
test('maps node types and sibling positions deterministically', () => {
expect(mapToFigmaType('COMPONENT')).toBe('SYMBOL')
expect([0, 93, 94, 188].map(fractionalPosition)).toEqual(['!', '~', '~!', '~~!'])
})
test('reuses an export-scoped component property definition index', () => {
const graph = new SceneGraph()
const page = graph.getPages()[0]
const component = graph.createNode('COMPONENT', page.id, {
componentPropertyDefinitions: [
{ id: '1:100', name: 'Label', type: 'TEXT', defaultValue: 'Default' }
]
})
const instance = graph.createNode('INSTANCE', page.id, {
componentId: component.id,
componentPropertyAssignments: { '1:100': 'Override' }
})
const serialize = (definitions?: ReturnType<typeof buildComponentPropIndex>) =>
sceneNodeToKiwi(
instance,
{ sessionID: 1, localID: 1 },
0,
{ value: 2 },
graph,
[],
new Map(),
undefined,
undefined,
undefined,
undefined,
new Set(),
undefined,
definitions
)[0].componentPropAssignments
const definitions = buildComponentPropIndex(graph)
expect(definitions.get('1:100')).toBe(component.componentPropertyDefinitions[0])
expect(serialize(definitions)).toEqual(serialize())
})
test('merges edited text into an existing override path', () => {
const graph = new SceneGraph()
const page = graph.getPages()[0]
const component = graph.createNode('COMPONENT', page.id)
const sourceText = graph.createNode('TEXT', component.id, {
overrideKey: '2:20',
text: 'Default'
})
const instance = graph.createInstance(component.id, page.id)
expect(instance).toBeDefined()
const targetText = graph.getChildren(instance?.id ?? '')[0]
expect(targetText).toBeDefined()
const originalOverride = {
guidPath: { guids: [{ sessionID: 2, localID: 20 }] },
textData: { characters: 'Stale' },
opacity: 0.5
}
graph.updateNode(instance?.id ?? '', {
overrides: { [`${targetText?.id}:text`]: 'Edited' },
source: {
...instance?.source,
fig: {
...instance?.source.fig,
symbolOverrides: [originalOverride]
}
}
})
const [change] = sceneNodeToKiwi(
graph.getNode(instance?.id ?? '') ?? instance,
{ sessionID: 1, localID: 1 },
0,
{ value: 2 },
graph,
[]
)
expect(sourceText.overrideKey).toBe('2:20')
expect(change.symbolData?.symbolOverrides).toEqual([
{
...originalOverride,
textData: { characters: 'Edited' }
}
])
})
test('injects runtime glyph outlines into derived text data', () => {
const graph = new SceneGraph()
const text = graph.createNode('TEXT', graph.getPages()[0].id, {
text: 'A',
width: 20,
height: 20,
fontSize: 16
})
const blobs: Uint8Array[] = []
const runtime: FigNodeChangeExportRuntime = {
getGlyphOutlineMetrics: () => [
{
commands: [{ type: 'M', x: 0, y: 0 }, { type: 'L', x: 8, y: 16 }, { type: 'Z' }],
x: 0,
advance: 10
}
]
}
const [change] = sceneNodeToKiwi(
text,
{ sessionID: 1, localID: 1 },
0,
{ value: 2 },
graph,
blobs,
undefined,
new Map([['Inter|Regular', new Uint8Array([1, 2, 3])]]),
undefined,
new Map(),
undefined,
undefined,
runtime
)
expect(change.derivedTextData?.glyphs).toHaveLength(1)
expect(blobs).toHaveLength(1)
})
})