-
diff --git a/tests/e2e/components.spec.ts b/tests/e2e/components.spec.ts
index 99fa7c56d..6626561dc 100644
--- a/tests/e2e/components.spec.ts
+++ b/tests/e2e/components.spec.ts
@@ -106,13 +106,11 @@ test('instance shows INSTANCE type in design panel', async () => {
})
test('instance has "Go to Main Component" button', async () => {
- const goToBtn = editor.page.getByTestId('design-go-to-component')
- await expect(goToBtn).toBeVisible()
+ await expect(editor.page.getByRole('button', { name: 'Go to Main Component' })).toBeVisible()
})
test('instance has "Detach" button', async () => {
- const detachBtn = editor.page.getByTestId('design-detach-instance')
- await expect(detachBtn).toBeVisible()
+ await expect(editor.page.getByRole('button', { name: 'Detach Instance' })).toBeVisible()
})
test('modifying component propagates to instance', async () => {
diff --git a/tests/e2e/components/assets-panel.spec.ts b/tests/e2e/components/assets-panel.spec.ts
index f920cced2..575b261fb 100644
--- a/tests/e2e/components/assets-panel.spec.ts
+++ b/tests/e2e/components/assets-panel.spec.ts
@@ -173,9 +173,10 @@ test('assets panel groups component sets and inserts the default variant', async
expect(inserted?.width).toBe(132)
expect(inserted?.childTexts).toEqual(['Secondary'])
- await expect(page.getByTestId('variant-section')).toBeVisible()
+ const variantSection = page.getByRole('region', { name: 'Variants' })
+ await expect(variantSection).toBeVisible()
- await page.getByTestId('variant-section').getByTestId('app-select-trigger').click()
+ await variantSection.getByRole('combobox', { name: 'Type' }).click()
await page.getByRole('option', { name: 'Primary' }).click()
expectDefined(inserted?.id, 'inserted instance id')
diff --git a/tests/e2e/design/panel.spec.ts b/tests/e2e/design/panel.spec.ts
index 7c9198869..b39cc292c 100644
--- a/tests/e2e/design/panel.spec.ts
+++ b/tests/e2e/design/panel.spec.ts
@@ -257,15 +257,16 @@ test('mask action toggles mask section and mask type control', async () => {
const maskAction = editor.page.getByTestId('selection-toggle-mask')
await expect(maskAction).toBeVisible()
- await expect(editor.page.getByTestId('mask-section')).toHaveCount(0)
+ await expect(propertySection(editor.page, 'Mask')).toHaveCount(0)
await maskAction.click()
await editor.canvas.waitForRender()
let node = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(node, 'node').isMask).toBe(true)
- await expect(editor.page.getByTestId('mask-section')).toBeVisible()
+ const maskSection = propertySection(editor.page, 'Mask')
+ await expect(maskSection).toBeVisible()
- const maskTypeSelect = editor.page.getByTestId('mask-type-select')
+ const maskTypeSelect = maskSection.getByRole('combobox', { name: 'Mask type' })
await maskTypeSelect.click()
await editor.page.getByRole('option', { name: 'Luminance' }).click()
await editor.canvas.waitForRender()
@@ -277,7 +278,7 @@ test('mask action toggles mask section and mask type control', async () => {
await editor.canvas.waitForRender()
node = await getNode(expectDefined(id, 'selected id'))
expect(expectDefined(node, 'node').isMask).toBe(false)
- await expect(editor.page.getByTestId('mask-section')).toHaveCount(0)
+ await expect(propertySection(editor.page, 'Mask')).toHaveCount(0)
})
test('visibility toggle in appearance section works', async () => {
diff --git a/tests/e2e/fonts/settings.spec.ts b/tests/e2e/fonts/settings.spec.ts
index e6ea54b40..3bcc35848 100644
--- a/tests/e2e/fonts/settings.spec.ts
+++ b/tests/e2e/fonts/settings.spec.ts
@@ -13,11 +13,18 @@ test('font settings popover exposes web font access without desktop-only cache a
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
const id = store.createShape('TEXT', 120, 120, 240, 40)
- store.updateNode(id, { characters: 'Font settings smoke' })
+ store.updateNode(id, {
+ characters: 'Font settings smoke',
+ fontFamily: 'Missing Test Sans'
+ })
store.select([id])
})
- await expect(page.getByTestId('typography-section')).toBeVisible()
+ const typography = page.getByRole('region', { name: 'Typography' })
+ await expect(typography).toBeVisible()
+ await expect(
+ typography.getByRole('img', { name: /Missing font: Missing Test Sans/ })
+ ).toBeVisible()
const fontSettings = page.getByTestId('font-settings-trigger')
await fontSettings.hover()
await expect(page.locator('[role=tooltip]').filter({ hasText: 'Font settings' })).toBeVisible()
diff --git a/tests/e2e/properties/page-section.spec.ts b/tests/e2e/properties/page-section.spec.ts
new file mode 100644
index 000000000..9b01c74bf
--- /dev/null
+++ b/tests/e2e/properties/page-section.spec.ts
@@ -0,0 +1,37 @@
+import { expect, test, useEditorSetup } from '#tests/e2e/fixtures'
+import { propertySection } from '#tests/helpers/properties'
+
+const editor = useEditorSetup()
+
+function pageColor() {
+ return editor.page.evaluate(() => {
+ const store = window.openPencil?.getStore?.()
+ if (!store) throw new Error('OpenPencil store not initialized')
+ return store.state.pageColor
+ })
+}
+
+test('page background uses the shared paint field for hex and alpha', async () => {
+ const pageSection = propertySection(editor.page, 'Page')
+ await expect(pageSection).toBeVisible()
+
+ const hex = pageSection.getByRole('textbox', { name: 'Page background' })
+ await hex.fill('336699')
+ await hex.press('Enter')
+ await editor.canvas.waitForRender()
+
+ let color = await pageColor()
+ expect(color.r).toBeCloseTo(0.2, 2)
+ expect(color.g).toBeCloseTo(0.4, 2)
+ expect(color.b).toBeCloseTo(0.6, 2)
+
+ const opacity = pageSection.getByRole('spinbutton', { name: 'Opacity' })
+ await opacity.click()
+ await opacity.fill('50')
+ await opacity.press('Enter')
+ await editor.canvas.waitForRender()
+
+ color = await pageColor()
+ expect(color.a).toBeCloseTo(0.5, 2)
+ editor.canvas.assertNoErrors()
+})
diff --git a/tests/e2e/properties/visibility.spec.ts b/tests/e2e/properties/visibility.spec.ts
index 96d5b48e0..3a7b65b28 100644
--- a/tests/e2e/properties/visibility.spec.ts
+++ b/tests/e2e/properties/visibility.spec.ts
@@ -40,8 +40,17 @@ test('fill visibility supports repeat click and undo redo', async () => {
)
})
+test('empty list sections expose semantic empty state', async () => {
+ await expect(propertySection(editor.page, 'Fill')).not.toHaveAttribute('data-empty')
+ await expect(propertySection(editor.page, 'Stroke')).toHaveAttribute('data-empty', '')
+ await expect(propertySection(editor.page, 'Effects')).toHaveAttribute('data-empty', '')
+ await expect(propertySection(editor.page, 'Export')).toHaveAttribute('data-empty', '')
+})
+
test('stroke visibility supports repeat click and undo redo', async () => {
- await propertySection(editor.page, 'Stroke').getByRole('button', { name: 'Add stroke' }).click()
+ const strokeSection = propertySection(editor.page, 'Stroke')
+ await strokeSection.getByRole('button', { name: 'Add stroke' }).click()
+ await expect(strokeSection).not.toHaveAttribute('data-empty')
await editor.canvas.waitForRender()
const strokeButton = propertyItems(editor.page, 'strokes')
diff --git a/tests/e2e/text/editing.spec.ts b/tests/e2e/text/editing.spec.ts
index 5ce6575f9..6674c7c23 100644
--- a/tests/e2e/text/editing.spec.ts
+++ b/tests/e2e/text/editing.spec.ts
@@ -53,7 +53,7 @@ test('typography section appears for text node', async () => {
await editor.canvas.click(200, 200)
await editor.canvas.waitForRender()
- const typoSection = editor.page.getByTestId('typography-section')
+ const typoSection = editor.page.getByRole('region', { name: 'Typography' })
await expect(typoSection).toBeVisible()
})
diff --git a/tests/e2e/text/formatting.spec.ts b/tests/e2e/text/formatting.spec.ts
index 75584f4e6..b6eed7399 100644
--- a/tests/e2e/text/formatting.spec.ts
+++ b/tests/e2e/text/formatting.spec.ts
@@ -44,6 +44,34 @@ test('double-click enters text edit mode', async () => {
canvas.assertNoErrors()
})
+test('typography controls use compact labeled anatomy', async () => {
+ await canvas.pressKey('Escape')
+ await canvas.waitForRender()
+ await canvas.click(275, 215)
+ await canvas.waitForRender()
+
+ const typography = page.getByRole('region', { name: 'Typography' })
+ await expect(typography.getByRole('button', { name: 'Font family' })).toBeVisible()
+ await expect(typography.getByRole('combobox', { name: 'Font weight' })).toBeVisible()
+ await expect(typography.getByRole('spinbutton', { name: 'Font size' })).toBeVisible()
+ await expect(typography.getByRole('spinbutton', { name: 'Line height' })).toBeVisible()
+ await expect(typography.getByRole('spinbutton', { name: 'Letter spacing' })).toBeVisible()
+ await expect(typography.getByRole('combobox', { name: 'Direction' })).toBeVisible()
+
+ const alignment = typography.getByRole('group', { name: 'Text alignment' })
+ await expect(alignment.getByRole('button', { name: 'Align left' })).toHaveAttribute(
+ 'aria-pressed',
+ 'true'
+ )
+ await alignment.getByRole('button', { name: 'Align center horizontally' }).click()
+ await canvas.waitForRender()
+ await expect(
+ alignment.getByRole('button', { name: 'Align center horizontally' })
+ ).toHaveAttribute('aria-pressed', 'true')
+
+ await expect(typography).toHaveScreenshot('typography-panel.png')
+})
+
test('bold button toggles fontWeight to 700 then back to 400', async () => {
await canvas.pressKey('Escape')
await canvas.waitForRender()
@@ -62,7 +90,9 @@ test('bold button toggles fontWeight to 700 then back to 400', async () => {
}, nodeId)
await canvas.waitForRender()
- const boldBtn = page.getByTestId('typography-bold-button')
+ const boldBtn = page.getByRole('toolbar', { name: 'Text formatting' }).getByRole('button', {
+ name: /^Bold/
+ })
await expect(boldBtn).toBeVisible({ timeout: 3000 })
await page.waitForTimeout(200)
await boldBtn.click()
@@ -72,7 +102,7 @@ test('bold button toggles fontWeight to 700 then back to 400', async () => {
const bold = await getNodeById(page, nodeId)
expect(bold?.fontWeight).toBe(700)
- await page.getByTestId('typography-bold-button').click()
+ await boldBtn.click()
await page.waitForTimeout(500)
await canvas.waitForRender()
diff --git a/tests/e2e/text/formatting.spec.ts-snapshots/typography-panel-openpencil-darwin.png b/tests/e2e/text/formatting.spec.ts-snapshots/typography-panel-openpencil-darwin.png
new file mode 100644
index 000000000..a4b2a126f
Binary files /dev/null and b/tests/e2e/text/formatting.spec.ts-snapshots/typography-panel-openpencil-darwin.png differ
diff --git a/tests/e2e/variables/dialog.spec.ts b/tests/e2e/variables/dialog.spec.ts
index 3cf2998b8..e8f503d7e 100644
--- a/tests/e2e/variables/dialog.spec.ts
+++ b/tests/e2e/variables/dialog.spec.ts
@@ -19,10 +19,16 @@ function variableRows() {
return editor.page.getByTestId('variable-row')
}
+function openVariables() {
+ return editor.page
+ .getByRole('region', { name: 'Variables' })
+ .getByRole('button', { name: 'Open variables' })
+}
+
test('variables dialog opens', async () => {
await createColorVariable('primary-color')
- await editor.page.getByTestId('variables-section-open').click()
+ await openVariables().click()
await expect(editor.page.getByTestId('variables-dialog')).toBeVisible()
editor.canvas.assertNoErrors()
})
@@ -87,7 +93,7 @@ test('color swatch opens color picker', async () => {
// close dialog if open from previous test
await editor.page.keyboard.press('Escape')
await editor.page.waitForTimeout(200)
- await editor.page.getByTestId('variables-section-open').click()
+ await openVariables().click()
await expect(editor.page.getByTestId('variables-dialog')).toBeVisible({ timeout: 3000 })
const swatch = editor.page