test(e2e): extract shared editor setup and store helpers
- useEditorSetup() / useEditorSetupWithClear() in tests/e2e/fixtures.ts - Migrated 15 simple specs to shared fixture - Moved getSelectedNode/getSelectedNodes to tests/helpers/store.ts - Replaced inline getSelectedNode in 4 specs - Reverted 8 specs with custom beforeAll setup (not fixture-compatible) - Test duplication in tests/: 329 → 307 clones (10.12% → 8.95%)
This commit is contained in:
parent
11e38b626a
commit
680350cdcb
|
|
@ -1,26 +1,11 @@
|
|||
import { expect, test, type Page } from '@playwright/test'
|
||||
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
||||
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
import { getSelectedNodes } from '#tests/helpers/store'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
await page.goto('/')
|
||||
canvas = new CanvasHelper(page)
|
||||
await canvas.waitForInit()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
const editor = useEditorSetup()
|
||||
|
||||
function getPageChildCount() {
|
||||
return page.evaluate(() => {
|
||||
return editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).length
|
||||
|
|
@ -28,7 +13,7 @@ function getPageChildCount() {
|
|||
}
|
||||
|
||||
function getSelectedCount() {
|
||||
return page.evaluate(() => {
|
||||
return editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.state.selectedIds.size
|
||||
|
|
@ -37,12 +22,12 @@ function getSelectedCount() {
|
|||
|
||||
|
||||
test('copy + paste via store duplicates a shape', async () => {
|
||||
await canvas.drawRect(100, 100, 120, 80)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.drawRect(100, 100, 120, 80)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const countBefore = await getPageChildCount()
|
||||
|
||||
await page.evaluate(async () => {
|
||||
await editor.page.evaluate(async () => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const data = new DataTransfer()
|
||||
|
|
@ -50,14 +35,14 @@ test('copy + paste via store duplicates a shape', async () => {
|
|||
const html = data.getData('text/html')
|
||||
if (html) await store.pasteFromHTML(html)
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const countAfter = await getPageChildCount()
|
||||
expect(countAfter).toBe(countBefore + 1)
|
||||
})
|
||||
|
||||
test('pasted node is offset from original', async () => {
|
||||
const nodes = await getSelectedNodes(page)
|
||||
const nodes = await getSelectedNodes(editor.page)
|
||||
expect(nodes).toHaveLength(1)
|
||||
|
||||
const pasted = nodes[0]
|
||||
|
|
@ -66,12 +51,12 @@ test('pasted node is offset from original', async () => {
|
|||
|
||||
test('⌘D duplicates in place', async () => {
|
||||
// Clear canvas and start fresh
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(200, 200, 100, 80)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 100, 80)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const countBefore = await getPageChildCount()
|
||||
await canvas.duplicate()
|
||||
await editor.canvas.duplicate()
|
||||
|
||||
const countAfter = await getPageChildCount()
|
||||
expect(countAfter).toBe(countBefore + 1)
|
||||
|
|
@ -80,7 +65,7 @@ test('⌘D duplicates in place', async () => {
|
|||
|
||||
test('duplicate preserves fills', async () => {
|
||||
// Set a custom fill on the selected node
|
||||
await page.evaluate(() => {
|
||||
await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
|
|
@ -100,72 +85,72 @@ test('duplicate preserves fills', async () => {
|
|||
'Set fill'
|
||||
)
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await canvas.duplicate()
|
||||
await editor.canvas.duplicate()
|
||||
|
||||
const nodes = await getSelectedNodes(page)
|
||||
const nodes = await getSelectedNodes(editor.page)
|
||||
expect(nodes[0].fills[0].color.b).toBeCloseTo(1, 1)
|
||||
})
|
||||
|
||||
test('cut removes original', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(100, 100, 80, 60)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(100, 100, 80, 60)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
expect(await getPageChildCount()).toBe(1)
|
||||
|
||||
// Cut via store
|
||||
await page.evaluate(() => {
|
||||
await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const data = new DataTransfer()
|
||||
store.writeCopyData(data)
|
||||
store.deleteSelected()
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
expect(await getPageChildCount()).toBe(0)
|
||||
})
|
||||
|
||||
test('multiple pastes can all be undone', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(100, 100, 80, 60)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(100, 100, 80, 60)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
expect(await getPageChildCount()).toBe(1)
|
||||
|
||||
// Duplicate 3 times via Cmd+D (synchronous, no clipboard issues)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await canvas.duplicate()
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.duplicate()
|
||||
await editor.canvas.waitForRender()
|
||||
}
|
||||
|
||||
expect(await getPageChildCount()).toBe(4) // 1 original + 3 duplicates
|
||||
|
||||
// Undo all 3 duplicates
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await canvas.undo()
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.undo()
|
||||
await editor.canvas.waitForRender()
|
||||
}
|
||||
|
||||
expect(await getPageChildCount()).toBe(1) // back to original only
|
||||
})
|
||||
|
||||
test('multi-select duplicate creates copies of all', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(100, 100, 60, 60)
|
||||
await canvas.drawRect(200, 100, 60, 60)
|
||||
await canvas.selectAll()
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(100, 100, 60, 60)
|
||||
await editor.canvas.drawRect(200, 100, 60, 60)
|
||||
await editor.canvas.selectAll()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
expect(await getSelectedCount()).toBe(2)
|
||||
|
||||
const countBefore = await getPageChildCount()
|
||||
await canvas.duplicate()
|
||||
await editor.canvas.duplicate()
|
||||
|
||||
const countAfter = await getPageChildCount()
|
||||
expect(countAfter).toBe(countBefore + 2)
|
||||
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,9 +1,25 @@
|
|||
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
||||
import { expect, test, type Page } from '@playwright/test'
|
||||
|
||||
const editor = useEditorSetup()
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
await page.goto('/')
|
||||
canvas = new CanvasHelper(page)
|
||||
await canvas.waitForInit()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
|
||||
async function getSelectedFill() {
|
||||
return editor.page.evaluate(() => {
|
||||
return page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
|
|
@ -14,36 +30,36 @@ async function getSelectedFill() {
|
|||
}
|
||||
|
||||
async function openFillPicker() {
|
||||
const solidTab = editor.page.getByTestId('fill-picker-tab-solid')
|
||||
const solidTab = page.getByTestId('fill-picker-tab-solid')
|
||||
if (await solidTab.isVisible().catch(() => false)) return
|
||||
const swatch = editor.page.getByTestId('fill-picker-swatch').first()
|
||||
const swatch = page.getByTestId('fill-picker-swatch').first()
|
||||
await swatch.click()
|
||||
await expect(solidTab).toBeVisible()
|
||||
}
|
||||
|
||||
async function chooseFormat(label: 'RGB' | 'HSL' | 'HSB' | 'OkHCL') {
|
||||
await editor.page.getByTestId('color-format-select').click()
|
||||
await editor.page.getByRole('option', { name: label, exact: true }).click()
|
||||
await page.getByTestId('color-format-select').click()
|
||||
await page.getByRole('option', { name: label, exact: true }).click()
|
||||
}
|
||||
|
||||
async function dragSlider(testId: string, ratio: number) {
|
||||
const slider = editor.page.getByTestId(testId).locator('input[type="range"]')
|
||||
const slider = page.getByTestId(testId).locator('input[type="range"]')
|
||||
const box = await slider.boundingBox()
|
||||
if (!box) throw new Error(`Missing slider: ${testId}`)
|
||||
const y = box.y + box.height / 2
|
||||
await editor.page.mouse.move(box.x + 2, y)
|
||||
await editor.page.mouse.down()
|
||||
await editor.page.mouse.move(box.x + Math.max(2, Math.min(box.width - 2, box.width * ratio)), y, {
|
||||
await page.mouse.move(box.x + 2, y)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(box.x + Math.max(2, Math.min(box.width - 2, box.width * ratio)), y, {
|
||||
steps: 20
|
||||
})
|
||||
await editor.page.mouse.up()
|
||||
await editor.canvas.waitForRender()
|
||||
await page.mouse.up()
|
||||
await canvas.waitForRender()
|
||||
}
|
||||
|
||||
test('rgb hue slider updates selected fill color', async () => {
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(100, 100, 160, 120)
|
||||
await editor.canvas.waitForRender()
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(100, 100, 160, 120)
|
||||
await canvas.waitForRender()
|
||||
|
||||
await openFillPicker()
|
||||
const before = await getSelectedFill()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { expect, test, type Page } from '@playwright/test'
|
||||
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
import { getSelectedNode } from '#tests/helpers/store'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
|
@ -19,6 +18,21 @@ test.afterAll(async () => {
|
|||
await page.close()
|
||||
})
|
||||
|
||||
async function getSelectedNodeFlags() {
|
||||
return page.evaluate(() => {
|
||||
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 n = store.graph.getNode(id)
|
||||
if (!n) return null
|
||||
return {
|
||||
type: n.type,
|
||||
independentCorners: n.independentCorners,
|
||||
independentStrokeWeights: n.independentStrokeWeights
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function drawFrame(x: number, y: number, w: number, h: number) {
|
||||
await canvas.pressKey('f')
|
||||
|
|
@ -30,7 +44,7 @@ test('independent corners toggle shows per-corner inputs', async () => {
|
|||
await drawFrame(120, 120, 120, 80)
|
||||
await canvas.waitForRender()
|
||||
|
||||
const flags = await getSelectedNode(page)
|
||||
const flags = await getSelectedNodeFlags()
|
||||
expect(flags?.type).toBe('FRAME')
|
||||
expect(flags?.independentCorners).toBe(false)
|
||||
|
||||
|
|
@ -40,7 +54,7 @@ test('independent corners toggle shows per-corner inputs', async () => {
|
|||
await toggle.click()
|
||||
await canvas.waitForRender()
|
||||
|
||||
expect((await getSelectedNode(page))?.independentCorners).toBe(true)
|
||||
expect((await getSelectedNodeFlags())?.independentCorners).toBe(true)
|
||||
const grid = page.getByTestId('independent-corners-grid')
|
||||
await expect(grid).toBeVisible()
|
||||
const cornerInputs = grid.getByTestId('scrub-input')
|
||||
|
|
|
|||
|
|
@ -1,99 +1,84 @@
|
|||
import { expect, test, type Page } from '@playwright/test'
|
||||
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
import { getPageChildren, getSelectedNode } from '#tests/helpers/store'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
await page.goto('/')
|
||||
canvas = new CanvasHelper(page)
|
||||
await canvas.waitForInit()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
const editor = useEditorSetup()
|
||||
|
||||
test('ScrubInput drag changes X position', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(100, 100, 80, 80)
|
||||
const before = await getSelectedNode(page)
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(100, 100, 80, 80)
|
||||
const before = await getSelectedNode(editor.page)
|
||||
const initialX = expectDefined(before, 'selected rectangle before drag').x
|
||||
|
||||
const xScrub = page
|
||||
const xScrub = editor.page
|
||||
.getByTestId('position-section').getByTestId('scrub-input')
|
||||
.first()
|
||||
await canvas.dragScrubInput(xScrub, 50)
|
||||
await editor.canvas.dragScrubInput(xScrub, 50)
|
||||
|
||||
const after = await getSelectedNode(page)
|
||||
const after = await getSelectedNode(editor.page)
|
||||
expect(after?.x).not.toBe(initialX)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('corner radius uniform sets cornerRadius', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(200, 200, 80, 80)
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
const scrubContainer = page.getByTestId('corner-radius-input')
|
||||
const scrubContainer = editor.page.getByTestId('corner-radius-input')
|
||||
await scrubContainer.click()
|
||||
await canvas.waitForRender()
|
||||
const input = page.getByTestId('corner-radius-input').getByTestId('scrub-input-field')
|
||||
await editor.canvas.waitForRender()
|
||||
const input = editor.page.getByTestId('corner-radius-input').getByTestId('scrub-input-field')
|
||||
await input.fill('12')
|
||||
await input.press('Enter')
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const node = await getSelectedNode(page)
|
||||
const node = await getSelectedNode(editor.page)
|
||||
expect(node?.cornerRadius).toBe(12)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('independent corners toggle shows four corner inputs', async () => {
|
||||
await page.getByTestId('independent-corners-toggle').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('independent-corners-toggle').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await expect(page.getByTestId('corner-tl-input')).toBeVisible()
|
||||
await expect(page.getByTestId('corner-tr-input')).toBeVisible()
|
||||
await expect(page.getByTestId('corner-br-input')).toBeVisible()
|
||||
await expect(page.getByTestId('corner-bl-input')).toBeVisible()
|
||||
canvas.assertNoErrors()
|
||||
await expect(editor.page.getByTestId('corner-tl-input')).toBeVisible()
|
||||
await expect(editor.page.getByTestId('corner-tr-input')).toBeVisible()
|
||||
await expect(editor.page.getByTestId('corner-br-input')).toBeVisible()
|
||||
await expect(editor.page.getByTestId('corner-bl-input')).toBeVisible()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('fill gradient switch changes fill type', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.pressKey('Escape')
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.pressKey('Escape')
|
||||
await editor.canvas.waitForRender()
|
||||
// fresh rect with default solid fill
|
||||
await canvas.drawRect(300, 300, 80, 80)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.drawRect(300, 300, 80, 80)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await expect(page.getByTestId('fill-section')).toBeVisible({ timeout: 5000 })
|
||||
await expect(editor.page.getByTestId('fill-section')).toBeVisible({ timeout: 5000 })
|
||||
|
||||
const fillItem = page.getByTestId('fill-item').first()
|
||||
const fillItem = editor.page.getByTestId('fill-item').first()
|
||||
await expect(fillItem).toBeVisible({ timeout: 5000 })
|
||||
const fillSwatch = fillItem.getByTestId('fill-picker-swatch')
|
||||
await expect(fillSwatch).toBeVisible({ timeout: 5000 })
|
||||
await fillSwatch.click()
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await page.getByTestId('fill-picker-tab-gradient').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('fill-picker-tab-gradient').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const node = expectDefined(await getSelectedNode(page), 'gradient-filled node')
|
||||
const node = expectDefined(await getSelectedNode(editor.page), 'gradient-filled node')
|
||||
expect(node.fills[0]?.type).toBe('GRADIENT_LINEAR')
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('variable bind badge appears on fill', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(200, 200, 80, 80)
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
await page.evaluate(() => {
|
||||
await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const col = store.graph.createCollection('Colors')
|
||||
|
|
@ -103,17 +88,17 @@ test('variable bind badge appears on fill', async () => {
|
|||
store.graph.bindVariable(id, 'fills/0/color', v.id)
|
||||
store.state.sceneVersion++
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await expect(page.getByTestId('fill-unbind-variable')).toBeVisible()
|
||||
canvas.assertNoErrors()
|
||||
await expect(editor.page.getByTestId('fill-unbind-variable')).toBeVisible()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('fill color can bind an existing variable', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(200, 200, 80, 80)
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
await page.evaluate(() => {
|
||||
await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const col = store.graph.createCollection('Colors')
|
||||
|
|
@ -126,45 +111,45 @@ test('fill color can bind an existing variable', async () => {
|
|||
store.state.sceneVersion++
|
||||
return variable.id
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await page.getByTestId('fill-apply-variable-0').click()
|
||||
await page.getByText('test-brand-red', { exact: true }).click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('fill-apply-variable-0').click()
|
||||
await editor.page.getByText('test-brand-red', { exact: true }).click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await expect(page.getByTestId('fill-unbind-variable')).toBeVisible()
|
||||
const fillSwatch = page.getByTestId('fill-picker-swatch')
|
||||
await expect(editor.page.getByTestId('fill-unbind-variable')).toBeVisible()
|
||||
const fillSwatch = editor.page.getByTestId('fill-picker-swatch')
|
||||
await expect(fillSwatch).toHaveCSS('background-color', 'rgb(255, 0, 0)')
|
||||
await fillSwatch.click()
|
||||
const colorInputs = page.locator('[role="dialog"] input[type="number"]:not(.hidden)')
|
||||
const colorInputs = editor.page.locator('[role="dialog"] input[type="number"]:not(.hidden)')
|
||||
await expect(colorInputs.first()).toHaveValue('255')
|
||||
await colorInputs.first().fill('0')
|
||||
await colorInputs.first().press('Enter')
|
||||
await canvas.waitForRender()
|
||||
await expect(page.getByTestId('fill-unbind-variable')).toBeHidden()
|
||||
const boundVariableId = await page.evaluate(() => {
|
||||
await editor.canvas.waitForRender()
|
||||
await expect(editor.page.getByTestId('fill-unbind-variable')).toBeHidden()
|
||||
const boundVariableId = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
return id ? (store.getNode(id)?.boundVariables['fills/0/color'] ?? null) : null
|
||||
})
|
||||
expect(boundVariableId).toBeNull()
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('fill color can create and bind a variable', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(200, 200, 80, 80)
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
await page.getByTestId('fill-apply-variable-0').click()
|
||||
await expect(page.getByText(/Create color variable from #?[0-9A-F]{6}/)).toBeVisible()
|
||||
await page.getByTestId('fill-apply-variable-0-create').click()
|
||||
await page.getByPlaceholder('Variable name').fill('Surface/default')
|
||||
await page.getByTestId('fill-apply-variable-0-create').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('fill-apply-variable-0').click()
|
||||
await expect(editor.page.getByText(/Create color variable from #?[0-9A-F]{6}/)).toBeVisible()
|
||||
await editor.page.getByTestId('fill-apply-variable-0-create').click()
|
||||
await editor.page.getByPlaceholder('Variable name').fill('Surface/default')
|
||||
await editor.page.getByTestId('fill-apply-variable-0-create').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await expect(page.getByTestId('fill-unbind-variable')).toBeVisible()
|
||||
const boundVariable = await page.evaluate(() => {
|
||||
await expect(editor.page.getByTestId('fill-unbind-variable')).toBeVisible()
|
||||
const boundVariable = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
|
|
@ -174,23 +159,23 @@ test('fill color can create and bind a variable', async () => {
|
|||
return variableId ? store.getVariable(variableId)?.name : null
|
||||
})
|
||||
expect(boundVariable).toBe('Surface/default')
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('width can create, bind, and detach a number variable', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(200, 200, 80, 80)
|
||||
await page.getByTestId('layout-height-input').click()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 80, 80)
|
||||
await editor.page.getByTestId('layout-height-input').click()
|
||||
|
||||
await page.getByTestId('layout-width-apply-variable').click()
|
||||
await expect(page.getByText('Create number variable from 80')).toBeVisible()
|
||||
await page.getByTestId('layout-width-apply-variable-create').click()
|
||||
await page.getByPlaceholder('Variable name').fill('Card/width')
|
||||
await page.getByTestId('layout-width-apply-variable-create').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('layout-width-apply-variable').click()
|
||||
await expect(editor.page.getByText('Create number variable from 80')).toBeVisible()
|
||||
await editor.page.getByTestId('layout-width-apply-variable-create').click()
|
||||
await editor.page.getByPlaceholder('Variable name').fill('Card/width')
|
||||
await editor.page.getByTestId('layout-width-apply-variable-create').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await expect(page.getByTestId('layout-width-unbind-variable')).toBeVisible()
|
||||
const boundVariable = await page.evaluate(() => {
|
||||
await expect(editor.page.getByTestId('layout-width-unbind-variable')).toBeVisible()
|
||||
const boundVariable = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
|
|
@ -201,15 +186,15 @@ test('width can create, bind, and detach a number variable', async () => {
|
|||
})
|
||||
expect(boundVariable).toBe('Card/width')
|
||||
|
||||
const widthField = page.getByTestId('layout-width-input')
|
||||
const widthField = editor.page.getByTestId('layout-width-input')
|
||||
await widthField.click()
|
||||
const widthInput = widthField.getByTestId('scrub-input-field')
|
||||
await widthInput.fill('120')
|
||||
await widthInput.press('Enter')
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await expect(page.getByTestId('layout-width-unbind-variable')).toBeHidden()
|
||||
const directWidth = await page.evaluate(() => {
|
||||
await expect(editor.page.getByTestId('layout-width-unbind-variable')).toBeHidden()
|
||||
const directWidth = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = [...store.state.selectedIds][0]
|
||||
|
|
@ -217,54 +202,54 @@ test('width can create, bind, and detach a number variable', async () => {
|
|||
return node ? { width: node.width, binding: node.boundVariables.width ?? null } : null
|
||||
})
|
||||
expect(directWidth).toEqual({ width: 120, binding: null })
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('alignment buttons align nodes to same X', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(50, 200, 60, 60)
|
||||
await canvas.drawRect(250, 200, 60, 60)
|
||||
await canvas.pressKey('Meta+a')
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(50, 200, 60, 60)
|
||||
await editor.canvas.drawRect(250, 200, 60, 60)
|
||||
await editor.canvas.pressKey('Meta+a')
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await page.getByTestId('position-align-left').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('position-align-left').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const children = await getPageChildren(page)
|
||||
const children = await getPageChildren(editor.page)
|
||||
expect(children.length).toBe(2)
|
||||
expect(children[0].x).toBe(children[1].x)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('flip horizontal sets flipX', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.drawRect(200, 200, 80, 80)
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.drawRect(200, 200, 80, 80)
|
||||
|
||||
await page.getByTestId('position-flip-horizontal').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('position-flip-horizontal').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const node = await getSelectedNode(page)
|
||||
const node = await getSelectedNode(editor.page)
|
||||
expect(node?.flipX).toBe(true)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('clip content checkbox toggles clipsContent', async () => {
|
||||
await canvas.clearCanvas()
|
||||
await canvas.pressKey('f')
|
||||
await canvas.drag(100, 100, 300, 300)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.clearCanvas()
|
||||
await editor.canvas.pressKey('f')
|
||||
await editor.canvas.drag(100, 100, 300, 300)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
// Enable auto-layout so the clip-content checkbox is visible
|
||||
await canvas.pressKey('Shift+a')
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.pressKey('Shift+a')
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const before = expectDefined(await getSelectedNode(page), 'selected frame before clipping')
|
||||
const before = expectDefined(await getSelectedNode(editor.page), 'selected frame before clipping')
|
||||
const initialValue = before.clipsContent
|
||||
|
||||
await page.getByTestId('clip-content-checkbox').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('clip-content-checkbox').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const after = await getSelectedNode(page)
|
||||
const after = await getSelectedNode(editor.page)
|
||||
expect(after?.clipsContent).toBe(!initialValue)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,88 +1,73 @@
|
|||
import { expect, test, type Page } from '@playwright/test'
|
||||
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
import { getSelectedNode } from '#tests/helpers/store'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
await page.goto('/')
|
||||
canvas = new CanvasHelper(page)
|
||||
await canvas.waitForInit()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
const editor = useEditorSetup()
|
||||
|
||||
|
||||
test('fill visibility supports repeat click and undo redo', async () => {
|
||||
await canvas.drawRect(120, 120, 120, 80)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.drawRect(120, 120, 120, 80)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const fillButton = page.getByTestId('fill-visibility-0')
|
||||
const fillButton = editor.page.getByTestId('fill-visibility-0')
|
||||
await expect(fillButton).toBeVisible()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').fills[0]?.visible).toBe(true)
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(true)
|
||||
|
||||
await fillButton.click()
|
||||
await canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').fills[0]?.visible).toBe(false)
|
||||
await editor.canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(false)
|
||||
|
||||
await fillButton.click()
|
||||
await canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').fills[0]?.visible).toBe(true)
|
||||
await editor.canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(true)
|
||||
|
||||
await canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').fills[0]?.visible).toBe(false)
|
||||
await editor.canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(false)
|
||||
|
||||
await canvas.redo()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').fills[0]?.visible).toBe(true)
|
||||
await editor.canvas.redo()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(true)
|
||||
})
|
||||
|
||||
test('stroke visibility supports repeat click and undo redo', async () => {
|
||||
await page.getByTestId('stroke-section-add').click()
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('stroke-section-add').click()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const strokeButton = page.getByTestId('stroke-visibility-0')
|
||||
const strokeButton = editor.page.getByTestId('stroke-visibility-0')
|
||||
await expect(strokeButton).toBeVisible()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').strokes[0]?.visible).toBe(true)
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(true)
|
||||
|
||||
await strokeButton.click()
|
||||
await canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').strokes[0]?.visible).toBe(false)
|
||||
await editor.canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(false)
|
||||
|
||||
await strokeButton.click()
|
||||
await canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').strokes[0]?.visible).toBe(true)
|
||||
await editor.canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(true)
|
||||
|
||||
await canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').strokes[0]?.visible).toBe(false)
|
||||
await editor.canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(false)
|
||||
|
||||
await canvas.redo()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').strokes[0]?.visible).toBe(true)
|
||||
await editor.canvas.redo()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(true)
|
||||
})
|
||||
|
||||
test('appearance visibility supports repeat click and undo redo in one step', async () => {
|
||||
const visibilityButton = page.getByTestId('appearance-visibility')
|
||||
const visibilityButton = editor.page.getByTestId('appearance-visibility')
|
||||
await expect(visibilityButton).toBeVisible()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').visible).toBe(true)
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').visible).toBe(true)
|
||||
|
||||
await visibilityButton.click()
|
||||
await canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').visible).toBe(false)
|
||||
await editor.canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').visible).toBe(false)
|
||||
|
||||
await visibilityButton.click()
|
||||
await canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').visible).toBe(true)
|
||||
await editor.canvas.waitForRender()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').visible).toBe(true)
|
||||
|
||||
await canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').visible).toBe(false)
|
||||
await editor.canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').visible).toBe(false)
|
||||
|
||||
await canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(page), 'selected node').visible).toBe(true)
|
||||
await editor.canvas.undo()
|
||||
expect(expectDefined(await getSelectedNode(editor.page), 'selected node').visible).toBe(true)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,28 +1,13 @@
|
|||
import { expect, test, type Page } from '@playwright/test'
|
||||
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
||||
|
||||
import { expectDefined } from '#tests/helpers/assert'
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
import { getSelectedNode } from '#tests/helpers/store'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
await page.goto('/')
|
||||
canvas = new CanvasHelper(page)
|
||||
await canvas.waitForInit()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
const editor = useEditorSetup()
|
||||
|
||||
|
||||
function getPageChildren() {
|
||||
return page.evaluate(() => {
|
||||
return editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.graph.getChildren(store.state.currentPageId).map((n) => ({
|
||||
|
|
@ -34,9 +19,9 @@ function getPageChildren() {
|
|||
}
|
||||
|
||||
test('pressing T activates text tool', async () => {
|
||||
await page.keyboard.press('t')
|
||||
await editor.page.keyboard.press('t')
|
||||
|
||||
const tool = await page.evaluate(() => {
|
||||
const tool = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.state.activeTool
|
||||
|
|
@ -45,11 +30,11 @@ test('pressing T activates text tool', async () => {
|
|||
})
|
||||
|
||||
test('clicking with text tool creates a text node', async () => {
|
||||
await canvas.click(200, 200)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.click(200, 200)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
// Wait a tick for text editing mode to activate
|
||||
await page.waitForTimeout(200)
|
||||
await editor.page.waitForTimeout(200)
|
||||
|
||||
const children = await getPageChildren()
|
||||
const textNode = children.find((c) => c.type === 'TEXT')
|
||||
|
|
@ -57,43 +42,43 @@ test('clicking with text tool creates a text node', async () => {
|
|||
})
|
||||
|
||||
test('text node is selected after creation', async () => {
|
||||
const node = await getSelectedNode(page)
|
||||
const node = await getSelectedNode(editor.page)
|
||||
expect(node?.type).toBe('TEXT')
|
||||
})
|
||||
|
||||
test('typography section appears for text node', async () => {
|
||||
// Exit text editing mode
|
||||
await page.keyboard.press('Escape')
|
||||
await canvas.waitForRender()
|
||||
await editor.page.keyboard.press('Escape')
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
// Re-select the text node
|
||||
await canvas.click(200, 200)
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.click(200, 200)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const typoSection = page.getByTestId('typography-section')
|
||||
const typoSection = editor.page.getByTestId('typography-section')
|
||||
await expect(typoSection).toBeVisible()
|
||||
})
|
||||
|
||||
test('text node has default font properties', async () => {
|
||||
const node = await getSelectedNode(page)
|
||||
const node = await getSelectedNode(editor.page)
|
||||
expect(node?.fontSize).toBeGreaterThan(0)
|
||||
expect(node?.fontFamily).toBeTruthy()
|
||||
})
|
||||
|
||||
test('creating text via store works', async () => {
|
||||
await page.keyboard.press('Escape')
|
||||
await canvas.waitForRender()
|
||||
await editor.page.keyboard.press('Escape')
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await page.evaluate(() => {
|
||||
await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.createShape('TEXT', 100, 300, 200, 30)
|
||||
store.graph.updateNode(id, { text: 'Hello World', fontSize: 24, fontFamily: 'Inter' })
|
||||
store.select([id])
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const node = await getSelectedNode(page)
|
||||
const node = await getSelectedNode(editor.page)
|
||||
expect(node?.text).toBe('Hello World')
|
||||
expect(node?.fontSize).toBe(24)
|
||||
})
|
||||
|
|
@ -101,31 +86,31 @@ test('creating text via store works', async () => {
|
|||
test('undo removes text node', async () => {
|
||||
const beforeCount = (await getPageChildren()).length
|
||||
|
||||
await canvas.undo()
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.undo()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const afterCount = (await getPageChildren()).length
|
||||
expect(afterCount).toBe(beforeCount - 1)
|
||||
})
|
||||
|
||||
test('frame tool creates FRAME node', async () => {
|
||||
await page.keyboard.press('f')
|
||||
await canvas.drag(400, 100, 600, 250)
|
||||
await canvas.waitForRender()
|
||||
await editor.page.keyboard.press('f')
|
||||
await editor.canvas.drag(400, 100, 600, 250)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const node = expectDefined(await getSelectedNode(page), 'selected frame')
|
||||
const node = expectDefined(await getSelectedNode(editor.page), 'selected frame')
|
||||
expect(node.type).toBe('FRAME')
|
||||
expect(node.width).toBeGreaterThan(0)
|
||||
expect(node.height).toBeGreaterThan(0)
|
||||
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('Enter key opens text editing and selects all without erasing', async () => {
|
||||
await page.keyboard.press('Escape')
|
||||
await canvas.waitForRender()
|
||||
await editor.page.keyboard.press('Escape')
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const textId = await page.evaluate(() => {
|
||||
const textId = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.createShape('TEXT', 300, 300, 200, 30)
|
||||
|
|
@ -134,23 +119,23 @@ test('Enter key opens text editing and selects all without erasing', async () =>
|
|||
store.requestRender()
|
||||
return id
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const before = await getSelectedNode(page)
|
||||
const before = await getSelectedNode(editor.page)
|
||||
expect(before?.text).toBe('Keep this text')
|
||||
expect(before?.type).toBe('TEXT')
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await page.waitForTimeout(200)
|
||||
await editor.page.keyboard.press('Enter')
|
||||
await editor.page.waitForTimeout(200)
|
||||
|
||||
const editing = await page.evaluate(() => {
|
||||
const editing = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
return store.state.editingTextId
|
||||
})
|
||||
expect(editing).toBe(textId)
|
||||
|
||||
const after = await page.evaluate(() => {
|
||||
const after = await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const id = store.state.editingTextId
|
||||
|
|
|
|||
|
|
@ -1,113 +1,98 @@
|
|||
import { test, expect, type Page } from '@playwright/test'
|
||||
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
||||
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
import { getPageChildren } from '#tests/helpers/store'
|
||||
import { toolbarFlyoutItemTestId, toolbarFlyoutTestId } from '#tests/helpers/test-ids'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
await page.goto('/')
|
||||
canvas = new CanvasHelper(page)
|
||||
await canvas.waitForInit()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
const editor = useEditorSetup()
|
||||
|
||||
test('shapes flyout opens', async () => {
|
||||
await page.getByTestId(toolbarFlyoutTestId('RECTANGLE')).click()
|
||||
await expect(page.getByTestId(toolbarFlyoutItemTestId('POLYGON'))).toBeVisible()
|
||||
canvas.assertNoErrors()
|
||||
await editor.page.getByTestId(toolbarFlyoutTestId('RECTANGLE')).click()
|
||||
await expect(editor.page.getByTestId(toolbarFlyoutItemTestId('POLYGON'))).toBeVisible()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('Polygon tool creates POLYGON node', async () => {
|
||||
await page.getByTestId(toolbarFlyoutItemTestId('POLYGON')).click()
|
||||
await canvas.drag(300, 200, 400, 300)
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId(toolbarFlyoutItemTestId('POLYGON')).click()
|
||||
await editor.canvas.drag(300, 200, 400, 300)
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const children = await getPageChildren(page)
|
||||
const children = await getPageChildren(editor.page)
|
||||
expect(children.some((n) => n.type === 'POLYGON')).toBe(true)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('Star tool creates STAR node', async () => {
|
||||
await page.getByTestId(toolbarFlyoutTestId('RECTANGLE')).click()
|
||||
await page.getByTestId(toolbarFlyoutItemTestId('STAR')).click()
|
||||
await canvas.drag(150, 150, 250, 250)
|
||||
await canvas.waitForRender()
|
||||
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()
|
||||
|
||||
const children = await getPageChildren(page)
|
||||
const children = await getPageChildren(editor.page)
|
||||
expect(children.some((n) => n.type === 'STAR')).toBe(true)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('Pen creates VECTOR node with 3 vertices on Enter', async () => {
|
||||
await canvas.pressKey('Escape')
|
||||
await canvas.pressKey('p')
|
||||
await canvas.click(100, 400)
|
||||
await canvas.waitForRender()
|
||||
await canvas.click(200, 400)
|
||||
await canvas.waitForRender()
|
||||
await canvas.click(200, 480)
|
||||
await canvas.waitForRender()
|
||||
await canvas.pressKey('Enter')
|
||||
await canvas.waitForRender()
|
||||
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(page)
|
||||
const children = await getPageChildren(editor.page)
|
||||
const vectors = children.filter((n) => n.type === 'VECTOR')
|
||||
expect(vectors.length).toBeGreaterThan(0)
|
||||
const last = vectors[vectors.length - 1]
|
||||
expect(last.vectorNetwork.vertices.length).toBe(3)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('Pen Escape with 2 vertices cancels path without creating node', async () => {
|
||||
const before = (await getPageChildren(page)).filter((n) => n.type === 'VECTOR').length
|
||||
const before = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
||||
|
||||
await canvas.pressKey('p')
|
||||
await canvas.click(350, 400)
|
||||
await canvas.waitForRender()
|
||||
await canvas.click(440, 400)
|
||||
await canvas.waitForRender()
|
||||
await canvas.pressKey('Escape')
|
||||
await canvas.waitForRender()
|
||||
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()
|
||||
|
||||
const after = (await getPageChildren(page)).filter((n) => n.type === 'VECTOR').length
|
||||
const after = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
||||
expect(after).toBe(before)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('Pen close path creates VECTOR with closed region', async () => {
|
||||
const before = (await getPageChildren(page)).filter((n) => n.type === 'VECTOR').length
|
||||
const before = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
||||
|
||||
await canvas.pressKey('p')
|
||||
await canvas.click(500, 200)
|
||||
await canvas.waitForRender()
|
||||
await canvas.click(580, 200)
|
||||
await canvas.waitForRender()
|
||||
await canvas.click(540, 270)
|
||||
await canvas.waitForRender()
|
||||
await canvas.click(500, 200)
|
||||
await canvas.waitForRender()
|
||||
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(page)).filter((n) => n.type === 'VECTOR').length
|
||||
const after = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR').length
|
||||
expect(after).toBeGreaterThan(before)
|
||||
|
||||
const vectors = (await getPageChildren(page)).filter((n) => n.type === 'VECTOR')
|
||||
const vectors = (await getPageChildren(editor.page)).filter((n) => n.type === 'VECTOR')
|
||||
const last = vectors[vectors.length - 1]
|
||||
expect(last.vectorNetwork.regions?.length).toBeGreaterThan(0)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('Frame flyout shows Frame and Section items', async () => {
|
||||
await page.getByTestId(toolbarFlyoutTestId('FRAME')).click()
|
||||
await expect(page.getByTestId(toolbarFlyoutItemTestId('FRAME'))).toBeVisible()
|
||||
await expect(page.getByTestId(toolbarFlyoutItemTestId('SECTION'))).toBeVisible()
|
||||
canvas.assertNoErrors()
|
||||
await editor.page.getByTestId(toolbarFlyoutTestId('FRAME')).click()
|
||||
await expect(editor.page.getByTestId(toolbarFlyoutItemTestId('FRAME'))).toBeVisible()
|
||||
await expect(editor.page.getByTestId(toolbarFlyoutItemTestId('SECTION'))).toBeVisible()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,26 +1,11 @@
|
|||
import { test, expect, type Page } from '@playwright/test'
|
||||
import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
|
||||
|
||||
import { CanvasHelper } from '#tests/helpers/canvas'
|
||||
import { variablesAddTestId } from '#tests/helpers/test-ids'
|
||||
|
||||
let page: Page
|
||||
let canvas: CanvasHelper
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage()
|
||||
await page.goto('/')
|
||||
canvas = new CanvasHelper(page)
|
||||
await canvas.waitForInit()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close()
|
||||
})
|
||||
const editor = useEditorSetup()
|
||||
|
||||
async function createColorVariable(name: string) {
|
||||
return page.evaluate((varName: string) => {
|
||||
return editor.page.evaluate((varName: string) => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const existing = [...store.graph.variableCollections.values()]
|
||||
|
|
@ -32,86 +17,86 @@ async function createColorVariable(name: string) {
|
|||
}
|
||||
|
||||
function variableRows() {
|
||||
return page.getByTestId('variable-row')
|
||||
return editor.page.getByTestId('variable-row')
|
||||
}
|
||||
|
||||
test('variables dialog opens', async () => {
|
||||
await createColorVariable('primary-color')
|
||||
|
||||
await page.getByTestId('variables-section-open').click()
|
||||
await expect(page.getByTestId('variables-dialog')).toBeVisible()
|
||||
canvas.assertNoErrors()
|
||||
await editor.page.getByTestId('variables-section-open').click()
|
||||
await expect(editor.page.getByTestId('variables-dialog')).toBeVisible()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('search filters variable rows', async () => {
|
||||
await page.evaluate(() => {
|
||||
await editor.page.evaluate(() => {
|
||||
const store = window.openPencil?.getStore?.()
|
||||
if (!store) throw new Error('OpenPencil store not initialized')
|
||||
const col = [...store.graph.variableCollections.values()][0]
|
||||
store.graph.createVariable('beta-spacing', 'FLOAT', col.id, 8)
|
||||
store.state.sceneVersion++
|
||||
})
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const searchInput = page.getByTestId('variables-search-input')
|
||||
const searchInput = editor.page.getByTestId('variables-search-input')
|
||||
await searchInput.fill('primary')
|
||||
|
||||
await expect(variableRows()).toHaveCount(1, { timeout: 3000 })
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('add variable menu creates non-color variable types', async () => {
|
||||
await page.getByTestId('variables-search-input').fill('')
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('variables-search-input').fill('')
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
await page.getByTestId('variables-add-variable').click()
|
||||
await page.getByTestId(variablesAddTestId('FLOAT')).click()
|
||||
await editor.page.getByTestId('variables-add-variable').click()
|
||||
await editor.page.getByTestId(variablesAddTestId('FLOAT')).click()
|
||||
await expect(
|
||||
page.getByTestId('variable-row').filter({ hasText: 'New number' })
|
||||
editor.page.getByTestId('variable-row').filter({ hasText: 'New number' })
|
||||
).toHaveCount(1)
|
||||
|
||||
await page.getByTestId('variables-add-variable').click()
|
||||
await page.getByTestId(variablesAddTestId('STRING')).click()
|
||||
await editor.page.getByTestId('variables-add-variable').click()
|
||||
await editor.page.getByTestId(variablesAddTestId('STRING')).click()
|
||||
await expect(
|
||||
page.getByTestId('variable-row').filter({ hasText: 'New text' })
|
||||
editor.page.getByTestId('variable-row').filter({ hasText: 'New text' })
|
||||
).toHaveCount(1)
|
||||
|
||||
await page.getByTestId('variables-add-variable').click()
|
||||
await page.getByTestId(variablesAddTestId('BOOLEAN')).click()
|
||||
await editor.page.getByTestId('variables-add-variable').click()
|
||||
await editor.page.getByTestId(variablesAddTestId('BOOLEAN')).click()
|
||||
await expect(
|
||||
page.getByTestId('variable-row').filter({ hasText: 'New boolean' })
|
||||
editor.page.getByTestId('variable-row').filter({ hasText: 'New boolean' })
|
||||
).toHaveCount(1)
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('click name cell activates editable input', async () => {
|
||||
await page.getByTestId('variables-search-input').fill('')
|
||||
await canvas.waitForRender()
|
||||
await editor.page.getByTestId('variables-search-input').fill('')
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const firstRow = variableRows().first()
|
||||
const nameCell = firstRow.locator('td').first()
|
||||
await nameCell.click()
|
||||
await canvas.waitForRender()
|
||||
await editor.canvas.waitForRender()
|
||||
|
||||
const editableInput = nameCell.locator('input, [contenteditable]').first()
|
||||
await expect(editableInput).toBeFocused()
|
||||
canvas.assertNoErrors()
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
||||
test('color swatch opens color picker', async () => {
|
||||
await createColorVariable('SwatchVar')
|
||||
// close dialog if open from previous test
|
||||
await page.keyboard.press('Escape')
|
||||
await page.waitForTimeout(200)
|
||||
await page.getByTestId('variables-section-open').click()
|
||||
await expect(page.getByTestId('variables-dialog')).toBeVisible({ timeout: 3000 })
|
||||
await editor.page.keyboard.press('Escape')
|
||||
await editor.page.waitForTimeout(200)
|
||||
await editor.page.getByTestId('variables-section-open').click()
|
||||
await expect(editor.page.getByTestId('variables-dialog')).toBeVisible({ timeout: 3000 })
|
||||
|
||||
const swatch = page
|
||||
const swatch = editor.page
|
||||
.getByTestId('variable-row')
|
||||
.first()
|
||||
.getByTestId('color-picker-swatch')
|
||||
await expect(swatch).toBeVisible({ timeout: 3000 })
|
||||
await swatch.click()
|
||||
await expect(page.getByTestId('color-picker-popover')).toBeVisible({ timeout: 5000 })
|
||||
canvas.assertNoErrors()
|
||||
await expect(editor.page.getByTestId('color-picker-popover')).toBeVisible({ timeout: 5000 })
|
||||
editor.canvas.assertNoErrors()
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue