openpencil/tests/e2e/text-editing.spec.ts
Danila Poyarkov b818ddeff0 Enter key opens text editing with select-all on TEXT nodes
Matches Figma behavior: pressing Enter when a single TEXT node is
selected starts text editing mode and selects all text. Uses rAF
to defer text editing start so the Enter keydown event completes
before the textarea is created and focused, preventing the newline
from being inserted into the selected text.

Closes #181
2026-04-06 15:40:55 +03:00

170 lines
4.5 KiB
TypeScript

import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../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()
})
function getSelectedNode() {
return page.evaluate(() => {
const store = window.__OPEN_PENCIL_STORE__!
const id = [...store.state.selectedIds][0]
if (!id) return null
const n = store.graph.getNode(id)
if (!n) return null
return {
id: n.id,
type: n.type,
name: n.name,
text: n.text,
fontSize: n.fontSize,
fontFamily: n.fontFamily,
fontWeight: n.fontWeight,
width: n.width,
height: n.height
}
})
}
function getPageChildren() {
return page.evaluate(() => {
const store = window.__OPEN_PENCIL_STORE__!
return store.graph.getChildren(store.state.currentPageId).map((n) => ({
type: n.type,
name: n.name,
text: n.text
}))
})
}
test('pressing T activates text tool', async () => {
await page.keyboard.press('t')
const tool = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.activeTool)
expect(tool).toBe('TEXT')
})
test('clicking with text tool creates a text node', async () => {
await canvas.click(200, 200)
await canvas.waitForRender()
// Wait a tick for text editing mode to activate
await page.waitForTimeout(200)
const children = await getPageChildren()
const textNode = children.find((c) => c.type === 'TEXT')
expect(textNode).toBeTruthy()
})
test('text node is selected after creation', async () => {
const node = await getSelectedNode()
expect(node).toBeTruthy()
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()
// Re-select the text node
await canvas.click(200, 200)
await canvas.waitForRender()
const typoSection = page.locator('[data-test-id="typography-section"]')
await expect(typoSection).toBeVisible()
})
test('text node has default font properties', async () => {
const node = await getSelectedNode()
expect(node).toBeTruthy()
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 page.evaluate(() => {
const store = window.__OPEN_PENCIL_STORE__!
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()
const node = await getSelectedNode()
expect(node!.text).toBe('Hello World')
expect(node!.fontSize).toBe(24)
})
test('undo removes text node', async () => {
const beforeCount = (await getPageChildren()).length
await canvas.undo()
await 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()
const node = await getSelectedNode()
expect(node!.type).toBe('FRAME')
expect(node!.width).toBeGreaterThan(0)
expect(node!.height).toBeGreaterThan(0)
canvas.assertNoErrors()
})
test('Enter key opens text editing and selects all without erasing', async () => {
await page.keyboard.press('Escape')
await canvas.waitForRender()
const textId = await page.evaluate(() => {
const store = window.__OPEN_PENCIL_STORE__!
const id = store.createShape('TEXT', 300, 300, 200, 30)
store.graph.updateNode(id, { text: 'Keep this text' })
store.select([id])
store.requestRender()
return id
})
await canvas.waitForRender()
const before = await getSelectedNode()
expect(before!.text).toBe('Keep this text')
expect(before!.type).toBe('TEXT')
await page.keyboard.press('Enter')
await page.waitForTimeout(200)
const editing = await page.evaluate(() => window.__OPEN_PENCIL_STORE__!.state.editingTextId)
expect(editing).toBe(textId)
const after = await page.evaluate(() => {
const store = window.__OPEN_PENCIL_STORE__!
const id = store.state.editingTextId
if (!id) return null
return store.graph.getNode(id)?.text ?? null
})
expect(after).toBe('Keep this text')
})