diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28752e7a8..597de19b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/packages/fig/tests/document/variable-override-precedence.test.ts b/packages/fig/tests/document/variable-override-precedence.test.ts new file mode 100644 index 000000000..8ee4536fe --- /dev/null +++ b/packages/fig/tests/document/variable-override-precedence.test.ts @@ -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 = { + // 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 = {} + 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) + }) +}) diff --git a/tests/fixtures/variable-override-precedence.fig b/tests/fixtures/variable-override-precedence.fig new file mode 100644 index 000000000..957b35a3f --- /dev/null +++ b/tests/fixtures/variable-override-precedence.fig @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:636796f9c78161e084ebd972b5a7d78a7d7c5211e48047e576249946d3237e78 +size 41243