* test(fig): pin override and variable precedence to what Figma renders The rule that decides between a bound variable and an instance's text override is not stated anywhere we control: an owner's consumption entry binds the field when it carries variableData and clears it when it is bare, and a binding beats a literal an outer owner recorded. The archive is a six-node file authored in Figma for exactly these cases, so the expected strings are Figma's own output rather than ours. It replaces reaching for a 44k-record community file to check this. * ci: pull the new fixture in the quick unit shards The quick shards fetch an explicit list of small LFS fixtures, so a new one arrives as a pointer and the fig shard fails parsing it.
51 lines
2.1 KiB
TypeScript
51 lines
2.1 KiB
TypeScript
import { describe, expect, test } from 'bun:test'
|
|
|
|
import { readFixtureArrayBuffer } from '#fig-tests/helpers/fig-fixtures'
|
|
|
|
import { materializeFigArchive } from '@open-pencil/fig'
|
|
import type { SceneGraph, SceneNode } from '@open-pencil/scene-graph'
|
|
|
|
/**
|
|
* An override and a variable binding can both state a text node's content, and which one Figma
|
|
* shows is decided by the owner's consumption entry: an entry carrying `variableData` binds the
|
|
* field, a bare entry clears it. The archive was authored in Figma for these four cases and the
|
|
* expected strings are what Figma itself renders for them, so this pins our reading to that.
|
|
*/
|
|
const FIGMA_RENDERS: Record<string, string> = {
|
|
// The component re-binds the field, so the binding wins over the literal each instance records.
|
|
'same-literal': 'Button-sm',
|
|
'different-literal': 'Button-sm',
|
|
// Its literal override is stale — the field was rebound after the override was recorded.
|
|
'return-to-inherited': 'Button-sm',
|
|
// Its owner's entry is bare, which clears the binding, so the literal shows.
|
|
staged: 'Learn more'
|
|
}
|
|
|
|
function firstText(graph: SceneGraph, instance: SceneNode): string | undefined {
|
|
const stack = [...instance.childIds]
|
|
while (stack.length > 0) {
|
|
const node = graph.getNode(stack.pop() as string)
|
|
if (!node) continue
|
|
if (node.type === 'TEXT') return node.text
|
|
stack.push(...node.childIds)
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
describe('variable binding against an instance override', () => {
|
|
test('resolves each authored case the way Figma renders it', () => {
|
|
const { graph } = materializeFigArchive(
|
|
readFixtureArrayBuffer('variable-override-precedence.fig')
|
|
)
|
|
const instances = [...graph.getAllNodes()].filter((node) => node.type === 'INSTANCE')
|
|
const rendered: Record<string, string | undefined> = {}
|
|
for (const name of Object.keys(FIGMA_RENDERS)) {
|
|
const instance = instances.find((node) => node.name === name)
|
|
expect(instance, `missing instance ${name}`).toBeDefined()
|
|
rendered[name] = instance ? firstText(graph, instance) : undefined
|
|
}
|
|
|
|
expect(rendered).toEqual(FIGMA_RENDERS)
|
|
})
|
|
})
|