2026-05-16 13:19:03 +00:00
|
|
|
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
2026-05-07 10:56:30 +00:00
|
|
|
|
2026-05-16 13:19:03 +00:00
|
|
|
const editor = useEditorSetup()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
|
|
|
|
function getNodeChildren(nodeId: string) {
|
2026-05-16 13:19:03 +00:00
|
|
|
return editor.page.evaluate((id) => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
return store.graph.getChildren(id).map((n) => ({
|
|
|
|
|
id: n.id,
|
|
|
|
|
name: n.name,
|
|
|
|
|
type: n.type,
|
|
|
|
|
parentId: n.parentId
|
|
|
|
|
}))
|
|
|
|
|
}, nodeId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getSelectedParent() {
|
2026-05-16 13:19:03 +00:00
|
|
|
return editor.page.evaluate(() => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
const id = [...store.state.selectedIds][0]
|
|
|
|
|
if (!id) return null
|
|
|
|
|
const node = store.graph.getNode(id)
|
|
|
|
|
return node?.parentId ?? null
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createFrameWithChild() {
|
2026-05-16 13:19:03 +00:00
|
|
|
return editor.page.evaluate(() => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
const pageId = store.state.currentPageId
|
|
|
|
|
const frameId = store.createShape('FRAME', 50, 50, 300, 200, pageId)
|
|
|
|
|
store.graph.updateNode(frameId, { name: 'Container' })
|
|
|
|
|
const childId = store.createShape('RECTANGLE', 10, 10, 80, 60, frameId)
|
|
|
|
|
store.graph.updateNode(childId, { name: 'Inner Rect' })
|
|
|
|
|
store.requestRender()
|
|
|
|
|
return { frameId, childId, pageId }
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function selectNode(nodeId: string) {
|
2026-05-16 13:19:03 +00:00
|
|
|
return editor.page.evaluate((id) => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
store.select([id])
|
|
|
|
|
store.requestRender()
|
|
|
|
|
}, nodeId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function copyAndPaste() {
|
2026-05-16 13:19:03 +00:00
|
|
|
return editor.page.evaluate(async () => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
const data = new DataTransfer()
|
|
|
|
|
await store.writeCopyData(data)
|
|
|
|
|
const html = data.getData('text/html')
|
|
|
|
|
if (html) await store.pasteFromHTML(html)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test('paste into selected frame places node as child', async () => {
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.clearCanvas()
|
2026-05-07 10:56:30 +00:00
|
|
|
const { frameId } = await createFrameWithChild()
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.drawRect(400, 50, 60, 60)
|
|
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
|
|
|
|
await copyAndPaste()
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
|
|
|
|
await selectNode(frameId)
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.page.evaluate(async () => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
const rect = [...store.graph.nodes.values()].find(
|
|
|
|
|
(n) => n.name === 'Rectangle' && n.type === 'RECTANGLE'
|
|
|
|
|
)
|
|
|
|
|
if (!rect) throw new Error('Rectangle not found')
|
|
|
|
|
store.select([rect.id])
|
|
|
|
|
|
|
|
|
|
const data = new DataTransfer()
|
|
|
|
|
await store.writeCopyData(data)
|
|
|
|
|
const html = data.getData('text/html')
|
|
|
|
|
|
|
|
|
|
const frame = [...store.graph.nodes.values()].find((n) => n.name === 'Container')
|
|
|
|
|
if (!frame) throw new Error('Container not found')
|
|
|
|
|
store.select([frame.id])
|
|
|
|
|
|
|
|
|
|
if (html) await store.pasteFromHTML(html)
|
|
|
|
|
})
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
|
|
|
|
const children = await getNodeChildren(frameId)
|
|
|
|
|
const pastedChild = children.find((c) => c.name === 'Rectangle')
|
|
|
|
|
expect(pastedChild).toBeDefined()
|
|
|
|
|
expect(pastedChild?.parentId).toBe(frameId)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('paste with child selected places node as sibling in parent frame', async () => {
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.clearCanvas()
|
2026-05-07 10:56:30 +00:00
|
|
|
const { frameId, childId } = await createFrameWithChild()
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.drawRect(400, 50, 60, 60)
|
|
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.page.evaluate(
|
2026-05-07 10:56:30 +00:00
|
|
|
async ({ childId: cid }) => {
|
|
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
const rect = [...store.graph.nodes.values()].find(
|
|
|
|
|
(n) => n.name === 'Rectangle' && n.type === 'RECTANGLE'
|
|
|
|
|
)
|
|
|
|
|
if (!rect) throw new Error('Rectangle not found')
|
|
|
|
|
store.select([rect.id])
|
|
|
|
|
|
|
|
|
|
const data = new DataTransfer()
|
|
|
|
|
await store.writeCopyData(data)
|
|
|
|
|
const html = data.getData('text/html')
|
|
|
|
|
|
|
|
|
|
store.select([cid])
|
|
|
|
|
if (html) await store.pasteFromHTML(html)
|
|
|
|
|
},
|
|
|
|
|
{ childId }
|
|
|
|
|
)
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
|
|
|
|
const parent = await getSelectedParent()
|
|
|
|
|
expect(parent).toBe(frameId)
|
|
|
|
|
|
|
|
|
|
const children = await getNodeChildren(frameId)
|
|
|
|
|
expect(children.length).toBeGreaterThanOrEqual(2)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('paste with no selection places on page', async () => {
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.clearCanvas()
|
|
|
|
|
await editor.canvas.drawRect(100, 100, 60, 60)
|
|
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.page.evaluate(async () => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
const data = new DataTransfer()
|
|
|
|
|
await store.writeCopyData(data)
|
|
|
|
|
const html = data.getData('text/html')
|
|
|
|
|
store.clearSelection()
|
|
|
|
|
if (html) await store.pasteFromHTML(html)
|
|
|
|
|
})
|
2026-05-16 13:19:03 +00:00
|
|
|
await editor.canvas.waitForRender()
|
2026-05-07 10:56:30 +00:00
|
|
|
|
|
|
|
|
const parent = await getSelectedParent()
|
2026-05-16 13:19:03 +00:00
|
|
|
const pageId = await editor.page.evaluate(() => {
|
2026-05-07 10:56:30 +00:00
|
|
|
const store = window.openPencil?.getStore?.()
|
|
|
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
|
return store.state.currentPageId
|
|
|
|
|
})
|
|
|
|
|
expect(parent).toBe(pageId)
|
|
|
|
|
|
2026-05-16 13:19:03 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-05-07 10:56:30 +00:00
|
|
|
})
|