openpencil/tests/e2e/design/panel.spec.ts
2026-06-06 11:31:24 +03:00

326 lines
11 KiB
TypeScript

import { expect, expectInViewport, test, useEditorSetup } from '#tests/e2e/fixtures'
import { expectDefined } from '#tests/helpers/assert'
const editor = useEditorSetup()
function designPanel() {
return editor.page.getByTestId('design-panel-single')
}
function nodeHeader() {
return editor.page.getByTestId('design-node-header')
}
function fillSection() {
return editor.page.getByTestId('fill-section')
}
function strokeSection() {
return editor.page.getByTestId('stroke-section')
}
function positionSection() {
return editor.page.getByTestId('position-section')
}
function effectsSection() {
return editor.page.getByTestId('effects-section')
}
function getNode(id: string) {
return editor.page.evaluate((nodeId) => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
const n = store.graph.getNode(nodeId)
if (!n) return null
return {
fills: n.fills,
strokes: n.strokes,
effects: n.effects,
opacity: n.opacity,
visible: n.visible,
x: n.x,
y: n.y,
width: n.width,
height: n.height,
rotation: n.rotation
}
}, id)
}
function getSelectedId() {
return editor.page.evaluate(() => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
return [...store.state.selectedIds][0] ?? null
})
}
test('selecting a rectangle shows design panel with type and name', async () => {
await editor.canvas.drawRect(100, 100, 120, 80)
await editor.canvas.waitForRender()
await expect(designPanel()).toBeVisible()
await expect(nodeHeader()).toContainText('RECTANGLE')
await expect(nodeHeader()).toContainText('Rectangle')
})
test('position section shows X, Y, rotation inputs', async () => {
await expect(positionSection()).toBeVisible()
const inputs = positionSection().getByTestId('scrub-input')
const count = await inputs.count()
expect(count).toBeGreaterThanOrEqual(3)
})
test('fill section appears with default fill', async () => {
await expect(fillSection()).toBeVisible()
const fillItems = fillSection().getByTestId('fill-item')
await expect(fillItems.first()).toBeVisible()
})
test('fill item shows color swatch', async () => {
const swatch = fillSection().getByTestId('fill-picker-swatch').first()
await expect(swatch).toBeVisible()
})
test('clicking color area changes fill color', async () => {
const id = await getSelectedId()
const before = await getNode(expectDefined(id, 'selected id'))
const swatch = fillSection().getByTestId('fill-picker-swatch').first()
await swatch.click()
const colorArea = editor.page.locator('.cursor-crosshair').first()
await expect(colorArea).toBeVisible({ timeout: 5000 })
const box = await colorArea.boundingBox()
await editor.page.mouse.click(
expectDefined(box, 'color area bounds').x + expectDefined(box, 'color area bounds').width - 10,
expectDefined(box, 'color area bounds').y + 10
)
await editor.canvas.waitForRender()
await editor.page.waitForTimeout(100)
const after = await getNode(expectDefined(id, 'selected id'))
const c1 = expectDefined(before, 'before node').fills[0].color
const c2 = expectDefined(after, 'after node').fills[0].color
expect(c1.r !== c2.r || c1.g !== c2.g || c1.b !== c2.b).toBe(true)
// Close popover — click the swatch again to toggle it off
await swatch.click()
await editor.canvas.waitForRender()
})
test('adding a stroke creates stroke section item', async () => {
const addBtn = strokeSection().getByTestId('stroke-section-add')
await addBtn.click()
await editor.canvas.waitForRender()
const strokeItems = strokeSection().getByTestId('stroke-item')
await expect(strokeItems.first()).toBeVisible()
const id = await getSelectedId()
const node = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(node, 'node node').strokes.length).toBe(1)
})
test('adding an effect creates effect item', async () => {
const addBtn = effectsSection().getByTestId('effects-section-add')
await addBtn.click()
await editor.canvas.waitForRender()
const effectItems = effectsSection().getByTestId('effect-item')
await expect(effectItems.first()).toBeVisible()
const id = await getSelectedId()
const node = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(node, 'node node').effects.length).toBe(1)
})
test('adding a second fill shows two fill items', async () => {
const addBtn = fillSection().getByTestId('fill-section-add')
await addBtn.click()
await editor.canvas.waitForRender()
const fillItems = fillSection().getByTestId('fill-item')
expect(await fillItems.count()).toBe(2)
const id = await getSelectedId()
const node = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(node, 'node node').fills.length).toBe(2)
})
test('visibility toggle in appearance section works', async () => {
const visBtn = editor.page.getByTestId('appearance-visibility')
await expect(visBtn).toBeVisible()
const id = await getSelectedId()
const before = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(before, 'before node').visible).toBe(true)
await visBtn.click()
await editor.canvas.waitForRender()
const after = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(after, 'after node').visible).toBe(false)
await visBtn.click()
await editor.canvas.waitForRender()
const restored = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(restored, 'restored node').visible).toBe(true)
})
test('fill stroke and effect visibility toggles update on repeated clicks and support undo redo', async () => {
const id = await getSelectedId()
expect(id).toBeTruthy()
const fillButton = editor.page.getByTestId('fill-visibility-0')
await expect(fillButton).toBeVisible()
const initial = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(initial, 'initial node').fills[0]?.visible).toBe(true)
await fillButton.click()
await editor.canvas.waitForRender()
await expect(fillButton).toHaveAttribute('data-visible', 'false')
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').fills[0]
?.visible
).toBe(false)
await fillButton.click()
await editor.canvas.waitForRender()
await expect(fillButton).toHaveAttribute('data-visible', 'true')
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').fills[0]
?.visible
).toBe(true)
await editor.canvas.undo()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').fills[0]
?.visible
).toBe(false)
await editor.canvas.redo()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').fills[0]
?.visible
).toBe(true)
const strokeAddButton = strokeSection().getByTestId('stroke-section-add')
await strokeAddButton.click()
await editor.canvas.waitForRender()
const strokeButton = editor.page.getByTestId('stroke-visibility-0')
await expect(strokeButton).toBeVisible()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').strokes[0]
?.visible
).toBe(true)
await strokeButton.click()
await editor.canvas.waitForRender()
await expect(strokeButton).toHaveAttribute('data-visible', 'false')
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').strokes[0]
?.visible
).toBe(false)
await strokeButton.click()
await editor.canvas.waitForRender()
await expect(strokeButton).toHaveAttribute('data-visible', 'true')
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').strokes[0]
?.visible
).toBe(true)
await editor.canvas.undo()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').strokes[0]
?.visible
).toBe(false)
await editor.canvas.redo()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').strokes[0]
?.visible
).toBe(true)
const effectAddButton = effectsSection().getByTestId('effects-section-add')
await effectAddButton.click()
await editor.canvas.waitForRender()
const effectButton = editor.page.getByTestId('effect-visibility-0')
await expect(effectButton).toBeVisible()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').effects[0]
?.visible
).toBe(true)
await effectButton.click()
await editor.canvas.waitForRender()
await expect(effectButton).toHaveAttribute('data-visible', 'false')
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').effects[0]
?.visible
).toBe(false)
await effectButton.click()
await editor.canvas.waitForRender()
await expect(effectButton).toHaveAttribute('data-visible', 'true')
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').effects[0]
?.visible
).toBe(true)
await editor.canvas.undo()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').effects[0]
?.visible
).toBe(false)
await editor.canvas.redo()
expect(
expectDefined(await getNode(expectDefined(id, 'selected id')), 'selected node').effects[0]
?.visible
).toBe(true)
})
test('deselecting shows empty design panel', async () => {
await editor.page.keyboard.press('Escape')
await editor.canvas.waitForRender()
await expect(editor.page.getByTestId('design-panel-empty')).toBeVisible()
})
test('multi-select shows mixed header and boolean operations', async () => {
await editor.canvas.drawRect(300, 100, 60, 60)
await editor.canvas.drawRect(400, 100, 60, 60)
await editor.canvas.selectAll()
await editor.canvas.waitForRender()
const multiHeader = editor.page.getByTestId('design-multi-header')
await expect(multiHeader).toBeVisible()
await expect(multiHeader).toContainText('Mixed')
await expect(multiHeader).toContainText('layers')
const booleanOperations = editor.page.getByTestId('boolean-operations-trigger')
await booleanOperations.hover()
await expect(
editor.page.locator('[role=tooltip]').filter({ hasText: 'Boolean operations' })
).toBeVisible()
await booleanOperations.click()
await expect(
editor.page.locator('[role=tooltip]').filter({ hasText: 'Boolean operations' })
).toHaveCount(0)
const booleanUnion = editor.page.getByTestId('boolean-operation-booleanUnion')
await expect(booleanUnion).toBeVisible()
await expectInViewport(editor.page, booleanUnion)
await expect(editor.page.getByTestId('boolean-operation-booleanSubtract')).toBeVisible()
await expect(editor.page.getByTestId('boolean-operation-booleanIntersect')).toBeVisible()
await expect(editor.page.getByTestId('boolean-operation-booleanExclude')).toBeVisible()
await expect(editor.page.getByTestId('boolean-operation-flatten')).toBeVisible()
})