openpencil/tests/e2e/canvas/drop-files.spec.ts
Danila Poyarkov bb8c5c18d9
fix(editor): commit vector drags on pointer release (#586)
* fix(editor): commit vector drags on pointer release

- Commit vertex and Bézier handle edits immediately on release\n- Rebase active vector-edit state after graph commits\n- Use vector-specific input paths and cover Pen-created Bézier drags

* test: reduce visual snapshot scope

- Replace behavior screenshots with scene assertions\n- Capture renderer fixtures from bounded canvas regions\n- Remove obsolete full-viewport snapshots

* fix(vector): harden edit cancellation and endpoint remapping

- Restore temporary vector geometry on cancelled pointer drags\n- Commit Bézier handle releases consistently and guard stale handle state\n- Preserve valid endpoint indices when connecting vector vertices

* fix(vector): cancel bend drags and satisfy formatting

* fix(vector): guard stale handle references

* fix(vector): guard stale overlay handle indices

* fix(vector): validate overlay handle keys

* refactor(vector): use typed handle selection ids

* test(vector): align typed handle selection coverage
2026-08-24 16:02:27 +03:00

99 lines
3.2 KiB
TypeScript

import { expect, test, useEditorSetupWithClear } from '#tests/e2e/fixtures'
const editor = useEditorSetupWithClear('/?test&no-chrome&no-rulers')
async function dispatchMixedFileDrop() {
return editor.page.getByTestId('canvas-element').evaluate(async (canvas) => {
const bounds = canvas.getBoundingClientRect()
const svg = new File(
[
'<svg width="40" height="20" viewBox="0 0 40 20"><rect width="20" height="20" fill="#f00"/><rect x="20" width="20" height="20" fill="#0c8"/></svg>'
],
'mark.svg'
)
const rasterCanvas = document.createElement('canvas')
rasterCanvas.width = 20
rasterCanvas.height = 10
const context = rasterCanvas.getContext('2d')
if (!context) throw new Error('Canvas context unavailable')
context.fillStyle = '#00f'
context.fillRect(0, 0, 20, 10)
const blob = await new Promise<Blob>((resolve, reject) => {
rasterCanvas.toBlob((value) => {
if (value) resolve(value)
else reject(new Error('PNG failed'))
})
})
const image = new File([blob], 'photo.png', { type: 'image/png' })
const transfer = new DataTransfer()
transfer.items.add(svg)
transfer.items.add(image)
const eventOptions = {
bubbles: true,
cancelable: true,
clientX: bounds.left + 300,
clientY: bounds.top + 200,
dataTransfer: transfer
}
const dragoverAccepted = !canvas.dispatchEvent(new DragEvent('dragover', eventOptions))
canvas.dispatchEvent(new DragEvent('drop', eventOptions))
return dragoverAccepted
})
}
test('mixed SVG and raster drops share placement, selection, and undo', async () => {
expect(await dispatchMixedFileDrop()).toBe(true)
await expect
.poll(() =>
editor.page.evaluate(() => {
const store = window.openPencil?.getStore?.()
return store?.graph.getChildren(store.state.currentPageId).length ?? 0
})
)
.toBe(2)
const placed = await editor.page.evaluate(() => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
return {
nodes: store.graph
.getChildren(store.state.currentPageId)
.map((node) => ({ name: node.name, type: node.type })),
selected: store.state.selectedIds.size,
undoLabel: store.undo.undoLabel
}
})
expect(placed).toEqual({
nodes: [
{ name: 'mark', type: 'FRAME' },
{ name: 'photo', type: 'RECTANGLE' }
],
selected: 2,
undoLabel: 'Place files'
})
await editor.canvas.waitForRender()
expect(await editor.canvas.screenshotCanvasRegion()).toMatchSnapshot('dropped-svg-multicolor.png')
await editor.page.evaluate(() => window.openPencil?.getStore?.().undoAction())
await expect
.poll(() =>
editor.page.evaluate(() => {
const store = window.openPencil?.getStore?.()
return store?.graph.getChildren(store.state.currentPageId).length ?? 0
})
)
.toBe(0)
await editor.page.evaluate(() => window.openPencil?.getStore?.().redoAction())
await expect
.poll(() =>
editor.page.evaluate(() => {
const store = window.openPencil?.getStore?.()
return store?.graph.getChildren(store.state.currentPageId).length ?? 0
})
)
.toBe(2)
editor.canvas.assertNoErrors()
})