openpencil/packages/kiwi/tests/codec.test.ts
Danila Poyarkov c48886920d
fix(fig)!: clear a colour variable binding when the layer is unbound
Unbinding a fill or stroke and saving handed Figma back the variable
the layer was imported with: the raw paint kept its `colorVar` and only
the bound path ever touched it. The unbound path now clears it, and an
emptied bindings record is dropped rather than written, since nothing
reads an empty map and it would otherwise sit in every exported file.

With that, nothing writes `colorVariableBinding`. It had no entry in
`fig.kiwi`, so encoding one meant splicing bytes into the encoded
message by searching for marker sequences; exports have written
`colorVar`, the field the schema defines, since the reader landed. The
splicing encoder and the three helpers it exposed are gone, leaving
`parseVariableId`. Importers still read the old field, so files earlier
versions wrote still open.

Co-authored-by: mrhard9090 <74858909+mrhard9090@users.noreply.github.com>
2026-10-01 17:06:00 +04:00

78 lines
1.8 KiB
TypeScript

import { describe, expect, test } from 'bun:test'
import {
createNodeChange,
createNodeChangesMessage,
decodeMessage,
encodeMessage,
getSchemaBytes,
initCodec,
isCodecReady,
peekMessageType,
type Color,
type NodeChange
} from '../src/fig/codec'
const red: Color = { r: 1, g: 0, b: 0, a: 1 }
describe('Figma Kiwi codec', () => {
test('initializes schema and exposes schema bytes', async () => {
await initCodec()
expect(isCodecReady()).toBe(true)
expect(getSchemaBytes().length).toBeGreaterThan(0)
})
test('creates normalized node changes', () => {
const nodeChange = createNodeChange({
sessionID: 1,
localID: 2,
parentSessionID: 1,
parentLocalID: 1,
type: 'RECTANGLE',
name: 'Box',
x: 10,
y: 20,
width: 30,
height: 40,
fill: red
})
expect(nodeChange.name).toBe('Box')
expect(nodeChange.fillPaints?.[0]?.color).toEqual(red)
})
test('encodes and decodes empty node change messages', async () => {
await initCodec()
const encoded = encodeMessage(createNodeChangesMessage(1, 0, []))
const decoded = decodeMessage(encoded)
expect(peekMessageType(encoded)).toBe(1)
expect(decoded.type).toBe('NODE_CHANGES')
})
test('encodes variable-bound paint messages', async () => {
await initCodec()
const nodeChange: NodeChange = {
guid: { sessionID: 1, localID: 2 },
type: 'RECTANGLE',
fillPaints: [
{
type: 'SOLID',
color: red,
colorVar: {
dataType: 'ALIAS',
resolvedDataType: 'COLOR',
value: { alias: { guid: { sessionID: 7, localID: 9 } } }
}
}
]
}
const encoded = encodeMessage(createNodeChangesMessage(1, 0, [nodeChange]))
expect(encoded.length).toBeGreaterThan(0)
})
})