openpencil/tests/engine/vue/layer-tree/model.test.ts
Danila Poyarkov c96f87bf9b feat(editor): add shared style bindings
- Model imported fill, stroke, text, effect, and grid style definitions and references

- Add SDK and app selectors with mixed-selection batching, detach-on-edit, and undo restoration

- Preserve style definitions through .fig export and extracted subgraphs

- Document the shared-style composable and update compatibility coverage
2026-07-17 20:28:22 +03:00

107 lines
3.4 KiB
TypeScript

import { describe, expect, test } from 'bun:test'
import {
buildLayerTreeModel,
layerSelectionForTarget,
patchLayerNode,
visibleLayerRows
} from '@open-pencil/vue'
import { createRect, firstPageId, makeSceneGraph } from '#tests/helpers/scene'
describe('layer tree model', () => {
test('builds indexed nested items in scene order', () => {
const graph = makeSceneGraph()
const pageId = firstPageId(graph)
const frame = graph.createNode('FRAME', pageId, { name: 'Frame' })
const child = createRect(graph, frame.id, { name: 'Child' })
const sibling = createRect(graph, pageId, { name: 'Sibling' })
const internal = graph.createNode('RECTANGLE', pageId, {
name: 'Internal style',
internalOnly: true
})
const model = buildLayerTreeModel(graph, pageId)
expect(model.items.map((node) => node.id)).toEqual([frame.id, sibling.id])
expect(model.items[0]?.children?.map((node) => node.id)).toEqual([child.id])
expect(model.byId.get(child.id)?.name).toBe('Child')
expect(model.byId.has(internal.id)).toBe(false)
})
test('derives only rows made visible by expansion', () => {
const graph = makeSceneGraph()
const pageId = firstPageId(graph)
const frame = graph.createNode('FRAME', pageId, { name: 'Frame' })
const nested = graph.createNode('FRAME', frame.id, { name: 'Nested' })
const child = createRect(graph, nested.id, { name: 'Child' })
const model = buildLayerTreeModel(graph, pageId)
expect(visibleLayerRows(model.items, new Set()).map((row) => row.node.id)).toEqual([frame.id])
expect(
visibleLayerRows(model.items, new Set([frame.id, nested.id])).map((row) => [
row.node.id,
row.level
])
).toEqual([
[frame.id, 1],
[nested.id, 2],
[child.id, 3]
])
})
test('patches an indexed node without replacing its identity', () => {
const graph = makeSceneGraph()
const pageId = firstPageId(graph)
const sceneNode = createRect(graph, pageId, { name: 'Before' })
const model = buildLayerTreeModel(graph, pageId)
const layerNode = model.byId.get(sceneNode.id)
expect(layerNode).toBeDefined()
if (!layerNode) return
graph.updateNode(sceneNode.id, { name: 'After', visible: false })
const updated = graph.getNode(sceneNode.id)
expect(updated).toBeDefined()
if (!updated) return
expect(patchLayerNode(layerNode, updated)).toBe(true)
expect(model.items[0]).toBe(layerNode)
expect(layerNode).toMatchObject({ name: 'After', visible: false })
expect(patchLayerNode(layerNode, updated)).toBe(false)
})
})
describe('layer tree selection', () => {
const rows = ['a', 'b', 'c', 'd']
test('replaces, toggles, and ranges from the anchor', () => {
expect(
layerSelectionForTarget(rows, new Set(['a']), 'a', 'c', {
additive: false,
range: true
})
).toEqual(new Set(['a', 'b', 'c']))
expect(
layerSelectionForTarget(rows, new Set(['a']), 'a', 'c', {
additive: true,
range: true
})
).toEqual(new Set(['a', 'b', 'c']))
expect(
layerSelectionForTarget(rows, new Set(['a', 'b']), 'a', 'b', {
additive: true,
range: false
})
).toEqual(new Set(['a']))
})
test('falls back to the target when the anchor is not visible', () => {
expect(
layerSelectionForTarget(rows, new Set(['a']), 'missing', 'd', {
additive: true,
range: true
})
).toEqual(new Set(['d']))
})
})