test(fig): pin override and variable precedence to what Figma renders (#912)

* 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.
This commit is contained in:
Danila Poyarkov 2026-10-06 09:08:51 +00:00 committed by GitHub
parent 708ec9e72d
commit 85b83cf050
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 1 deletions

View file

@ -243,7 +243,7 @@ jobs:
with:
lfs: 'true'
# Quick shards read only the small fixtures; heavy-tests.yml pulls the large .fig corpora.
lfs-include: tests/fixtures/gold-preview.fig,tests/fixtures/circle-text.fig,tests/fixtures/slots.fig,tests/fixtures/fonts/*
lfs-include: tests/fixtures/gold-preview.fig,tests/fixtures/circle-text.fig,tests/fixtures/slots.fig,tests/fixtures/variable-override-precedence.fig,tests/fixtures/fonts/*
- name: Build shared Core test dependency
run: bun --filter @open-pencil/core build

View file

@ -0,0 +1,50 @@
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)
})
})

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:636796f9c78161e084ebd972b5a7d78a7d7c5211e48047e576249946d3237e78
size 41243