From 85b83cf0505cff720a49b50e0177be8b3b83273c Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Tue, 6 Oct 2026 09:08:51 +0000 Subject: [PATCH] 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. --- .github/workflows/ci.yml | 2 +- .../variable-override-precedence.test.ts | 50 +++++++++++++++++++ .../fixtures/variable-override-precedence.fig | 3 ++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 packages/fig/tests/document/variable-override-precedence.test.ts create mode 100644 tests/fixtures/variable-override-precedence.fig 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