openpencil/packages/fig/tests/document/bindings/references.test.ts
Danila Poyarkov 2101279e1d
refactor(fig): group prefixed siblings into folders
* refactor(tools): group the oracle captures and mirror the bindings tests

Two names broke the rule that a multi-file domain gets a subfolder
rather than repeated sibling prefixes. `document-capture.ts` repeated
its own folder and sat beside `capture-scene.ts`, so both become
`document/capture/{document,scene}.ts` behind `#visual/capture/*`.

The document bindings tests were split between `document/binding/` and
a `text-bindings.test.ts` one level above, while their source is
`document/bindings/`. They move into `document/bindings/` and take the
source file names, so the test tree mirrors it.

* refactor(fig): group the node-change prefix families into folders

`node-change/` held five pairs of files sharing a leading segment —
derived-text, style, text, variable, vector — plus `instance-geometry`
beside an `instance-linkage/` folder. The guide asks for a subfolder
rather than a repeated prefix, so each becomes one: `style/{refs,runs}`,
`variable/{bindings,expression}`, and so on, with `instance-linkage/`
folded in beside `instance/geometry.ts`.

Imports across seventeen files follow. Two of the moved files reached
out of the package tree with `../../` once they were a level deeper;
those use `#fig/*` instead.
2026-10-01 16:52:40 +04:00

110 lines
3.6 KiB
TypeScript

import { expect, test } from 'bun:test'
import { guid } from '#fig-tests/helpers/guid'
import { resolveDocumentBindingReferences } from '#fig/document/bindings/references'
import { nodeChangeToProps } from '@open-pencil/fig/node-change'
import type { NodeChange } from '@open-pencil/kiwi/fig/codec'
test('shadowed legacy resource references are not treated as required bindings', () => {
const diagnostics: unknown[] = []
resolveDocumentBindingReferences(
[
{
guid: guid(1),
type: 'RECTANGLE',
variableConsumptionMap: {
entries: [
{
variableField: 'WIDTH',
variableData: {
dataType: 'ALIAS',
value: { alias: { assetRef: { key: 'retired' } } }
}
}
]
},
parameterConsumptionMap: { entries: [{ variableField: 'WIDTH' }] }
}
],
(diagnostic) => diagnostics.push(diagnostic)
)
expect(diagnostics).toEqual([])
})
test('modern explicit clearing removes only the targeted legacy binding', () => {
const props = nodeChangeToProps(
{
type: 'RECTANGLE',
variableConsumptionMap: {
entries: [
{ variableField: 'WIDTH', variableData: { value: { alias: { guid: guid(1) } } } },
{ variableField: 'HEIGHT', variableData: { value: { alias: { guid: guid(2) } } } }
]
},
parameterConsumptionMap: { entries: [{ variableField: 'WIDTH' }] }
},
[]
)
expect(props.boundVariables).toEqual({ height: '1:2' })
})
for (const map of ['variableConsumptionMap', 'parameterConsumptionMap'] as const) {
for (const expression of [false, true]) {
test(`${map} ${expression ? 'expression' : 'alias'} normalizes versioned references without mutating source records`, () => {
const alias = { value: { alias: { assetRef: { key: 'number', version: 'v1' } } } }
const variableData = expression
? {
dataType: 'EXPRESSION',
value: {
expressionValue: {
expressionFunction: 'MULTIPLY',
expressionArguments: [{ value: { floatValue: 0.5 } }, alias]
}
}
}
: alias
const changes = [
{ guid: guid(1), type: 'VARIABLE', key: 'number', version: 'v1' },
{ guid: guid(2), type: 'VARIABLE', key: 'color', version: 'v1' },
{ guid: guid(3), type: 'VARIABLE_SET', key: 'set', version: 'v1' },
{
guid: guid(4),
type: 'RECTANGLE',
[map]: {
entries: [
{
variableField: 'WIDTH',
variableData
}
]
},
fillPaints: [
{
type: 'SOLID',
colorVar: { value: { alias: { assetRef: { key: 'color', version: 'v1' } } } }
}
],
variableModeBySetMap: {
entries: [
{
variableSetID: { assetRef: { key: 'set', version: 'v1' } },
variableModeID: guid(10)
}
]
}
}
] as NodeChange[]
const before = structuredClone(changes)
const diagnostics: unknown[] = []
const normalized = resolveDocumentBindingReferences(changes, (d) => diagnostics.push(d))
const props = nodeChangeToProps(normalized[3], [])
expect(props.boundVariables).toEqual({ width: '1:1', 'fills/0/color': '1:2' })
expect(props.variableModes).toEqual({ '1:3': '1:10' })
expect(diagnostics).toEqual([])
expect(changes).toEqual(before)
expect(props.variableBindingScales?.width).toBe(expression ? 0.5 : 1)
})
}
}