2026-05-16 13:55:34 +00:00
|
|
|
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
2026-05-05 23:05:02 +00:00
|
|
|
import { getPageChildren } from '#tests/helpers/store'
|
2026-07-17 21:07:49 +00:00
|
|
|
import {
|
|
|
|
|
toolbarFlyoutItemTestId,
|
|
|
|
|
toolbarFlyoutTestId,
|
|
|
|
|
toolbarToolTestId
|
|
|
|
|
} from '#tests/helpers/test-ids'
|
2026-03-07 19:11:25 +00:00
|
|
|
|
2026-05-16 13:55:34 +00:00
|
|
|
const editor = useEditorSetup()
|
2026-03-07 19:11:25 +00:00
|
|
|
|
|
|
|
|
test('shapes flyout opens', async () => {
|
2026-05-16 13:55:34 +00:00
|
|
|
await editor.page.getByTestId(toolbarFlyoutTestId('RECTANGLE')).click()
|
2026-07-25 21:05:57 +00:00
|
|
|
const rectangleItem = editor.page.getByTestId(toolbarFlyoutItemTestId('RECTANGLE'))
|
|
|
|
|
const polygonItem = editor.page.getByTestId(toolbarFlyoutItemTestId('POLYGON'))
|
|
|
|
|
|
|
|
|
|
await expect(polygonItem).toBeVisible()
|
|
|
|
|
await expect(rectangleItem).toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(rectangleItem).toHaveAttribute('role', 'menuitemradio')
|
|
|
|
|
await expect(rectangleItem).toHaveAttribute('aria-checked', 'true')
|
|
|
|
|
await expect(rectangleItem.locator('[data-slot="flyout-item-indicator"] svg')).toHaveCount(1)
|
|
|
|
|
await expect(polygonItem).not.toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(polygonItem).toHaveAttribute('aria-checked', 'false')
|
2026-05-16 13:55:34 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-03-07 19:11:25 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('Polygon tool creates POLYGON node', async () => {
|
2026-05-16 13:55:34 +00:00
|
|
|
await editor.page.getByTestId(toolbarFlyoutItemTestId('POLYGON')).click()
|
2026-07-17 21:07:49 +00:00
|
|
|
await expect(editor.page.getByTestId(toolbarToolTestId('POLYGON'))).toHaveAttribute(
|
|
|
|
|
'data-active',
|
|
|
|
|
'true'
|
|
|
|
|
)
|
2026-05-16 13:55:34 +00:00
|
|
|
await editor.canvas.drag(300, 200, 400, 300)
|
|
|
|
|
await editor.canvas.waitForRender()
|
2026-03-07 19:11:25 +00:00
|
|
|
|
2026-05-16 13:55:34 +00:00
|
|
|
const children = await getPageChildren(editor.page)
|
2026-05-03 18:03:40 +00:00
|
|
|
expect(children.some((n) => n.type === 'POLYGON')).toBe(true)
|
2026-05-16 13:55:34 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-03-07 19:11:25 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('Star tool creates STAR node', async () => {
|
2026-05-16 13:55:34 +00:00
|
|
|
await editor.page.getByTestId(toolbarFlyoutTestId('RECTANGLE')).click()
|
|
|
|
|
await editor.page.getByTestId(toolbarFlyoutItemTestId('STAR')).click()
|
|
|
|
|
await editor.canvas.drag(150, 150, 250, 250)
|
|
|
|
|
await editor.canvas.waitForRender()
|
2026-03-07 19:11:25 +00:00
|
|
|
|
2026-05-16 13:55:34 +00:00
|
|
|
const children = await getPageChildren(editor.page)
|
2026-05-03 18:03:40 +00:00
|
|
|
expect(children.some((n) => n.type === 'STAR')).toBe(true)
|
2026-05-16 13:55:34 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-03-07 19:11:25 +00:00
|
|
|
})
|
|
|
|
|
|
2026-07-25 21:05:57 +00:00
|
|
|
test('shape flyout remembers its selection independently of the active tool', async () => {
|
|
|
|
|
await editor.canvas.pressKey('f')
|
|
|
|
|
await expect(editor.page.getByTestId(toolbarToolTestId('STAR'))).not.toHaveAttribute(
|
|
|
|
|
'data-active',
|
|
|
|
|
'true'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await editor.page.getByTestId(toolbarFlyoutTestId('RECTANGLE')).click()
|
|
|
|
|
const starItem = editor.page.getByTestId(toolbarFlyoutItemTestId('STAR'))
|
|
|
|
|
const rectangleItem = editor.page.getByTestId(toolbarFlyoutItemTestId('RECTANGLE'))
|
|
|
|
|
|
|
|
|
|
await expect(starItem).toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(starItem).toHaveAttribute('aria-checked', 'true')
|
|
|
|
|
await expect(starItem.locator('[data-slot="flyout-item-indicator"] svg')).toHaveCount(1)
|
|
|
|
|
await expect(rectangleItem).not.toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(rectangleItem).toHaveAttribute('aria-checked', 'false')
|
|
|
|
|
|
|
|
|
|
await rectangleItem.hover()
|
|
|
|
|
await expect(rectangleItem).toHaveAttribute('data-highlighted', '')
|
|
|
|
|
await expect(starItem).toHaveAttribute('data-active', 'true')
|
|
|
|
|
editor.canvas.assertNoErrors()
|
|
|
|
|
})
|
|
|
|
|
|
2026-03-07 19:11:25 +00:00
|
|
|
test('Pen creates VECTOR node with 3 vertices on Enter', async () => {
|
2026-05-16 13:55:34 +00:00
|
|
|
await editor.canvas.pressKey('Escape')
|
|
|
|
|
await editor.canvas.pressKey('p')
|
|
|
|
|
await editor.canvas.click(100, 400)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.click(200, 400)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.click(200, 480)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.pressKey('Enter')
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
|
|
|
|
|
const children = await getPageChildren(editor.page)
|
2026-05-03 18:03:40 +00:00
|
|
|
const vectors = children.filter((n) => n.type === 'VECTOR')
|
2026-03-07 19:11:25 +00:00
|
|
|
expect(vectors.length).toBeGreaterThan(0)
|
|
|
|
|
const last = vectors[vectors.length - 1]
|
|
|
|
|
expect(last.vectorNetwork.vertices.length).toBe(3)
|
2026-05-16 13:55:34 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-03-07 19:11:25 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('Pen Escape with 2 vertices cancels path without creating node', async () => {
|
2026-05-16 13:55:34 +00:00
|
|
|
const before = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
2026-03-07 19:11:25 +00:00
|
|
|
|
2026-05-16 13:55:34 +00:00
|
|
|
await editor.canvas.pressKey('p')
|
|
|
|
|
await editor.canvas.click(350, 400)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.click(440, 400)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.pressKey('Escape')
|
|
|
|
|
await editor.canvas.waitForRender()
|
2026-03-07 19:11:25 +00:00
|
|
|
|
2026-05-16 13:55:34 +00:00
|
|
|
const after = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
2026-03-07 19:11:25 +00:00
|
|
|
expect(after).toBe(before)
|
2026-05-16 13:55:34 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-03-07 19:11:25 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('Pen close path creates VECTOR with closed region', async () => {
|
2026-05-16 13:55:34 +00:00
|
|
|
const before = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
|
|
|
|
|
|
|
|
|
await editor.canvas.pressKey('p')
|
|
|
|
|
await editor.canvas.click(500, 200)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.click(580, 200)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.click(540, 270)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
await editor.canvas.click(500, 200)
|
|
|
|
|
await editor.canvas.waitForRender()
|
|
|
|
|
|
|
|
|
|
const after = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
2026-03-07 19:11:25 +00:00
|
|
|
expect(after).toBeGreaterThan(before)
|
|
|
|
|
|
2026-05-16 13:55:34 +00:00
|
|
|
const vectors = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR')
|
2026-03-07 19:11:25 +00:00
|
|
|
const last = vectors[vectors.length - 1]
|
|
|
|
|
expect(last.vectorNetwork.regions?.length).toBeGreaterThan(0)
|
2026-05-16 13:55:34 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-03-07 19:11:25 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('Frame flyout shows Frame and Section items', async () => {
|
2026-07-25 21:05:57 +00:00
|
|
|
await editor.canvas.pressKey('f')
|
|
|
|
|
const frameButton = editor.page.getByTestId(toolbarToolTestId('FRAME'))
|
|
|
|
|
const frameOptions = editor.page.getByTestId(toolbarFlyoutTestId('FRAME'))
|
|
|
|
|
|
|
|
|
|
await expect(frameButton).toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(frameOptions).not.toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(frameOptions).toHaveAttribute('data-state', 'closed')
|
|
|
|
|
|
|
|
|
|
await frameOptions.click()
|
|
|
|
|
await expect(frameOptions).toHaveAttribute('data-state', 'open')
|
|
|
|
|
await expect(frameOptions).not.toHaveAttribute('data-active', 'true')
|
|
|
|
|
const frameItem = editor.page.getByTestId(toolbarFlyoutItemTestId('FRAME'))
|
|
|
|
|
await expect(frameItem).toBeVisible()
|
|
|
|
|
await expect(frameItem).toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(frameItem.locator('[data-slot="flyout-item-indicator"] svg')).toHaveCount(1)
|
|
|
|
|
const sectionItem = editor.page.getByTestId(toolbarFlyoutItemTestId('SECTION'))
|
|
|
|
|
await expect(sectionItem).toBeVisible()
|
|
|
|
|
await expect(sectionItem).not.toHaveAttribute('data-active', 'true')
|
|
|
|
|
await expect(sectionItem.locator('[data-slot="flyout-item-indicator"] svg')).toHaveCount(0)
|
|
|
|
|
|
|
|
|
|
await sectionItem.hover()
|
|
|
|
|
await expect(sectionItem).toHaveAttribute('data-highlighted', '')
|
|
|
|
|
await expect(frameItem).toHaveAttribute('data-active', 'true')
|
2026-05-16 13:55:34 +00:00
|
|
|
editor.canvas.assertNoErrors()
|
2026-03-07 19:11:25 +00:00
|
|
|
})
|