test(layout): place auto-layout reflow cases by domain

This commit is contained in:
Danila Poyarkov 2026-07-02 10:16:08 +03:00
parent d522dc0529
commit ddb46af32f
5 changed files with 96 additions and 94 deletions

View file

@ -0,0 +1,31 @@
import { describe, expect, test } from 'bun:test'
import { createEditor } from '@open-pencil/core/editor'
import { computeAllLayouts } from '@open-pencil/core/layout'
import { getNodeOrThrow } from '#tests/helpers/assert'
import { autoFrame, rect } from '#tests/helpers/layout'
describe('deleteSelected', () => {
test('reflows fixed vertical auto-layout siblings', () => {
const editor = createEditor()
const page = editor.state.currentPageId
const frame = autoFrame(editor.graph, page, {
layoutMode: 'VERTICAL',
width: 300,
height: 300,
itemSpacing: 0
})
rect(editor.graph, frame.id, 300, 40)
const body = rect(editor.graph, frame.id, 300, 100, { layoutGrow: 1 })
const footerA = rect(editor.graph, frame.id, 300, 40)
const footerB = rect(editor.graph, frame.id, 300, 40)
computeAllLayouts(editor.graph, page)
editor.select([footerB.id])
editor.deleteSelected()
expect(getNodeOrThrow(editor.graph, body.id).height).toBe(220)
expect(getNodeOrThrow(editor.graph, footerA.id).y).toBe(260)
})
})

View file

@ -4,31 +4,9 @@ import { createEditor } from '@open-pencil/core/editor'
import { computeAllLayouts } from '@open-pencil/core/layout'
import { getNodeOrThrow } from '#tests/helpers/assert'
import { autoFrame, rect } from '#tests/helpers/layout'
describe('editor auto-layout reflow', () => {
test('deleteSelected reflows fixed vertical auto-layout siblings', () => {
const editor = createEditor()
const page = editor.state.currentPageId
const frame = autoFrame(editor.graph, page, {
layoutMode: 'VERTICAL',
width: 300,
height: 300,
itemSpacing: 0
})
rect(editor.graph, frame.id, 300, 40)
const body = rect(editor.graph, frame.id, 300, 100, { layoutGrow: 1 })
const footerA = rect(editor.graph, frame.id, 300, 40)
const footerB = rect(editor.graph, frame.id, 300, 40)
computeAllLayouts(editor.graph, page)
editor.select([footerB.id])
editor.deleteSelected()
expect(getNodeOrThrow(editor.graph, body.id).height).toBe(220)
expect(getNodeOrThrow(editor.graph, footerA.id).y).toBe(260)
})
import { rect } from '#tests/helpers/layout'
describe('structure state actions', () => {
test('toggleNodeVisibility reflows HUG auto-layout instance slots', () => {
const editor = createEditor()
const page = editor.state.currentPageId

View file

@ -1,69 +0,0 @@
import { describe, expect, test } from 'bun:test'
import { computeAllLayouts, SceneGraph } from '@open-pencil/core'
import { getNodeOrThrow } from '#tests/helpers/assert'
import { autoFrame, pageId, rect } from '#tests/helpers/layout'
describe('Figma auto-layout oracle regressions', () => {
test('deleting a trailing child reflows fill siblings in fixed vertical auto-layout', () => {
const graph = new SceneGraph()
const page = pageId(graph)
const frame = autoFrame(graph, page, {
layoutMode: 'VERTICAL',
width: 300,
height: 300,
itemSpacing: 0
})
rect(graph, frame.id, 300, 40, { name: 'header h40' })
const body = rect(graph, frame.id, 300, 100, { name: 'body fill', layoutGrow: 1 })
const footerA = rect(graph, frame.id, 300, 40, { name: 'footerA h40' })
const footerB = rect(graph, frame.id, 300, 40, { name: 'footerB h40' })
computeAllLayouts(graph, page)
expect(getNodeOrThrow(graph, body.id).height).toBe(180)
expect(getNodeOrThrow(graph, footerA.id).y).toBe(220)
graph.deleteNode(footerB.id)
computeAllLayouts(graph, page)
expect(getNodeOrThrow(graph, body.id).height).toBe(220)
expect(getNodeOrThrow(graph, footerA.id).y).toBe(260)
expect(getNodeOrThrow(graph, frame.id).height).toBe(300)
})
test('hidden child in HUG auto-layout instance is excluded from layout', () => {
const graph = new SceneGraph()
const page = pageId(graph)
const component = graph.createNode('COMPONENT', page, {
name: 'Optional slot component',
layoutMode: 'VERTICAL',
primaryAxisSizing: 'HUG',
counterAxisSizing: 'FIXED',
width: 300,
height: 1,
itemSpacing: 0
})
rect(graph, component.id, 300, 40, { name: 'Slot / Pinned at Top' })
rect(graph, component.id, 300, 74, { name: 'Slot / Content' })
rect(graph, component.id, 300, 40, { name: 'Slot / Pinned at Bottom' })
const instance = graph.createInstance(component.id, page, { x: 360, y: 0 })
if (!instance) throw new Error('Expected instance to be created')
computeAllLayouts(graph, page)
expect(getNodeOrThrow(graph, instance.id).height).toBe(154)
const content = graph.getChildren(instance.id).find((child) => child.name === 'Slot / Content')
const bottom = graph
.getChildren(instance.id)
.find((child) => child.name === 'Slot / Pinned at Bottom')
if (!content || !bottom) throw new Error('Expected instance slot children')
expect(getNodeOrThrow(graph, bottom.id).y).toBe(114)
graph.updateNode(content.id, { visible: false })
computeAllLayouts(graph, page)
expect(getNodeOrThrow(graph, instance.id).height).toBe(80)
expect(getNodeOrThrow(graph, bottom.id).y).toBe(40)
expect(getNodeOrThrow(graph, content.id).height).toBe(74)
})
})

View file

@ -87,4 +87,39 @@ describe('hidden children', () => {
expect(innerNode.height).toBe(50)
expect(children[1].x).toBe(0)
})
test('hidden children in HUG auto-layout instances collapse layout', () => {
const graph = new SceneGraph()
const page = pageId(graph)
const component = graph.createNode('COMPONENT', page, {
name: 'Optional slot component',
layoutMode: 'VERTICAL',
primaryAxisSizing: 'HUG',
counterAxisSizing: 'FIXED',
width: 300,
height: 1,
itemSpacing: 0
})
rect(graph, component.id, 300, 40, { name: 'Slot / Pinned at Top' })
rect(graph, component.id, 300, 74, { name: 'Slot / Content' })
rect(graph, component.id, 300, 40, { name: 'Slot / Pinned at Bottom' })
const instance = graph.createInstance(component.id, page, { x: 360, y: 0 })
if (!instance) throw new Error('Expected instance to be created')
computeLayout(graph, instance.id)
expect(getNodeOrThrow(graph, instance.id).height).toBe(154)
const content = graph.getChildren(instance.id).find((child) => child.name === 'Slot / Content')
const bottom = graph
.getChildren(instance.id)
.find((child) => child.name === 'Slot / Pinned at Bottom')
if (!content || !bottom) throw new Error('Expected instance slot children')
expect(getNodeOrThrow(graph, bottom.id).y).toBe(114)
graph.updateNode(content.id, { visible: false })
computeLayout(graph, instance.id)
expect(getNodeOrThrow(graph, instance.id).height).toBe(80)
expect(getNodeOrThrow(graph, bottom.id).y).toBe(40)
expect(getNodeOrThrow(graph, content.id).height).toBe(74)
})
})

View file

@ -1,7 +1,8 @@
import { describe, expect, test } from 'bun:test'
import { computeLayout, SceneGraph } from '@open-pencil/core'
import { computeAllLayouts, computeLayout, SceneGraph } from '@open-pencil/core'
import { getNodeOrThrow } from '#tests/helpers/assert'
import { autoFrame, pageId, rect } from '#tests/helpers/layout'
describe('vertical basic', () => {
@ -43,4 +44,30 @@ describe('vertical basic', () => {
expect(children[1].y).toBe(56)
expect(children[2].y).toBe(112)
})
test('deleting a trailing child reflows fill siblings in fixed vertical auto-layout', () => {
const graph = new SceneGraph()
const page = pageId(graph)
const frame = autoFrame(graph, page, {
layoutMode: 'VERTICAL',
width: 300,
height: 300,
itemSpacing: 0
})
rect(graph, frame.id, 300, 40, { name: 'header h40' })
const body = rect(graph, frame.id, 300, 100, { name: 'body fill', layoutGrow: 1 })
const footerA = rect(graph, frame.id, 300, 40, { name: 'footerA h40' })
const footerB = rect(graph, frame.id, 300, 40, { name: 'footerB h40' })
computeAllLayouts(graph, page)
expect(getNodeOrThrow(graph, body.id).height).toBe(180)
expect(getNodeOrThrow(graph, footerA.id).y).toBe(220)
graph.deleteNode(footerB.id)
computeAllLayouts(graph, page)
expect(getNodeOrThrow(graph, body.id).height).toBe(220)
expect(getNodeOrThrow(graph, footerA.id).y).toBe(260)
expect(getNodeOrThrow(graph, frame.id).height).toBe(300)
})
})