import { describe, expect, test } from 'bun:test' import { SceneGraph, sceneNodeToKiwi } from '@open-pencil/core' import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert' import { ROOT_GUID, pageId } from '../helpers' describe('Fix 1: auto-layout child transforms', () => { test('auto-layout child preserves its stored transform offsets', () => { const graph = new SceneGraph() const parent = graph.createNode('FRAME', pageId(graph), { name: 'AutoLayout', x: 0, y: 0, width: 300, height: 400, layoutMode: 'VERTICAL', itemSpacing: 8, paddingTop: 16, paddingLeft: 16, paddingBottom: 16, paddingRight: 16 }) graph.createNode('FRAME', parent.id, { name: 'Child', x: 50, y: 100, width: 200, height: 60 }) const blobs: Uint8Array[] = [] // Serialize the parent — children are serialized recursively const changes = sceneNodeToKiwi( getNodeOrThrow(graph, parent.id), ROOT_GUID, 0, { value: 100 }, graph, blobs ) // changes[0] = parent, changes[1] = child const childNc = expectDefined( changes.find((nc) => nc.name === 'Child'), 'child node change' ) expect(childNc).toBeDefined() expect(childNc.transform.m02).toBe(50) expect(childNc.transform.m12).toBe(100) }) test('absolute-positioned child inside auto-layout keeps its real x/y', () => { const graph = new SceneGraph() const parent = graph.createNode('FRAME', pageId(graph), { name: 'AutoLayout', x: 0, y: 0, width: 300, height: 400, layoutMode: 'VERTICAL', itemSpacing: 8 }) graph.createNode('FRAME', parent.id, { name: 'AbsChild', x: 75, y: 120, width: 50, height: 50, layoutPositioning: 'ABSOLUTE' }) const blobs: Uint8Array[] = [] const changes = sceneNodeToKiwi( getNodeOrThrow(graph, parent.id), ROOT_GUID, 0, { value: 100 }, graph, blobs ) const absNc = expectDefined( changes.find((nc) => nc.name === 'AbsChild'), 'absolute child node change' ) expect(absNc).toBeDefined() expect(absNc.transform.m02).toBe(75) expect(absNc.transform.m12).toBe(120) }) test('child in non-auto-layout parent keeps its real x/y', () => { const graph = new SceneGraph() const parent = graph.createNode('FRAME', pageId(graph), { name: 'PlainFrame', x: 0, y: 0, width: 300, height: 400 // layoutMode defaults to 'NONE' }) graph.createNode('FRAME', parent.id, { name: 'Child', x: 30, y: 45, width: 100, height: 80 }) const blobs: Uint8Array[] = [] const changes = sceneNodeToKiwi( getNodeOrThrow(graph, parent.id), ROOT_GUID, 0, { value: 100 }, graph, blobs ) const childNc = expectDefined( changes.find((nc) => nc.name === 'Child'), 'child node change' ) expect(childNc).toBeDefined() expect(childNc.transform.m02).toBe(30) expect(childNc.transform.m12).toBe(45) }) test('preserves imported Figma layout metadata for instance roundtrips', () => { const graph = new SceneGraph() const parent = graph.createNode('FRAME', pageId(graph), { name: 'Checked List', width: 878, height: 24, layoutMode: 'HORIZONTAL', counterAxisSizing: 'HUG', layoutAlignSelf: 'STRETCH' }) parent.source.fig.layout = { stackMode: 'HORIZONTAL', stackPrimarySizing: 'FIXED', stackCounterSizing: 'RESIZE_TO_FIT_WITH_IMPLICIT_SIZE', stackChildAlignSelf: 'STRETCH' } const child = graph.createNode('INSTANCE', parent.id, { name: 'Lists', width: 282.6666564941406, height: 24, layoutMode: 'VERTICAL', layoutGrow: 1 }) child.source.fig.layout = { stackMode: 'VERTICAL', stackChildPrimaryGrow: 1 } const blobs: Uint8Array[] = [] const changes = sceneNodeToKiwi( getNodeOrThrow(graph, parent.id), ROOT_GUID, 0, { value: 100 }, graph, blobs ) const parentNc = expectDefined( changes.find((nc) => nc.name === 'Checked List'), 'parent node change' ) expect(parentNc.stackCounterSizing).toBe('RESIZE_TO_FIT_WITH_IMPLICIT_SIZE') const instanceNc = expectDefined( changes.find((nc) => nc.name === 'Lists'), 'instance node change' ) expect(instanceNc.stackMode).toBe('VERTICAL') expect(instanceNc.stackChildPrimaryGrow).toBe(1) expect(instanceNc.stackPrimarySizing).toBeUndefined() expect(instanceNc.stackCounterSizing).toBeUndefined() expect(instanceNc.stackPrimaryAlignItems).toBeUndefined() expect(instanceNc.stackCounterAlignItems).toBeUndefined() }) test('horizontal auto-layout child also preserves its stored transform offsets', () => { const graph = new SceneGraph() const parent = graph.createNode('FRAME', pageId(graph), { name: 'HorizontalLayout', x: 0, y: 0, width: 600, height: 100, layoutMode: 'HORIZONTAL', itemSpacing: 12 }) graph.createNode('RECTANGLE', parent.id, { name: 'Item', x: 200, y: 50, width: 80, height: 80 }) const blobs: Uint8Array[] = [] const changes = sceneNodeToKiwi( getNodeOrThrow(graph, parent.id), ROOT_GUID, 0, { value: 100 }, graph, blobs ) const itemNc = expectDefined( changes.find((nc) => nc.name === 'Item'), 'item node change' ) expect(itemNc.transform.m02).toBe(200) expect(itemNc.transform.m12).toBe(50) }) }) // --------------------------------------------------------------------------- // Fix 2 — frameMaskDisabled always true // ---------------------------------------------------------------------------