From 9be2e76bf6b329c30f38326d362f36d60405d1fc Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Tue, 15 Sep 2026 22:10:56 +0300 Subject: [PATCH] refactor: consolidate Playwright projects --- AGENTS.md | 5 +- package.json | 9 ++-- packages/docs/development/testing.md | 16 +++++- playwright.config.ts | 81 +++++++++++++++++++++------- playwright.storybook.config.ts | 20 ------- 5 files changed, 85 insertions(+), 46 deletions(-) delete mode 100644 playwright.storybook.config.ts diff --git a/AGENTS.md b/AGENTS.md index cbda5cc28..77956defc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,7 +70,8 @@ App dialogs compose the Reka-backed components under `src/components/ui/dialog/` - `bun run dev` — fixed `http://localhost:1420` server for Playwright, Tauri, and Dev Containers. - `bun run check` — complete build, lint, type, architecture, docs, package, dependency, security, tooling, and duplication gate. - `bun run format` — format and sort imports. -- `bun run test:unit` / `bun run test` — engine/unit and Playwright suites. +- `bun run test:unit` / `bun run test` — engine/unit and app Playwright suites. +- `bun run test:storybook` — the Storybook Playwright project in `playwright.config.ts`. Test scripts select their server; direct Playwright commands start both servers unless `OPENPENCIL_TEST_SERVER=app|storybook|all` is set. - `bun run tauri dev` — desktop app with hot reload. - `bun open-pencil --help` — current CLI command list. @@ -210,7 +211,7 @@ Keep responsibilities distinct: engine tests cover state contracts, Playwright b - Section/frame title text never scales — render at fixed font size, ellipsize to fit - Rulers are rendered on the canvas (not DOM), with selection range badges that don't overlap tick numbers - Remote cursors: Figma-style colored arrows with white border + name pill, rendered in screen space -- Pixel-affecting renderer features need committed visual coverage, not just mock/geometry assertions. Add or update a Playwright canvas snapshot for changes to fills, gradients, images, blend modes, masks, boolean geometry, corners, strokes, shadows, blur, text rendering, or demo showcase scenes. Use targeted snapshot updates such as `bunx playwright test tests/e2e/canvas/renderer-visuals.spec.ts --project=openpencil --update-snapshots` and then rerun the same test without `--update-snapshots`. +- Pixel-affecting renderer features need committed visual coverage, not just mock/geometry assertions. Add or update a Playwright canvas snapshot for changes to fills, gradients, images, blend modes, masks, boolean geometry, corners, strokes, shadows, blur, text rendering, or demo showcase scenes. Use targeted snapshot updates such as `bun run test tests/e2e/canvas/renderer-visuals.spec.ts --update-snapshots` and then rerun the same test without `--update-snapshots`. ## Scene graph diff --git a/package.json b/package.json index d532c19e1..68ad0e751 100644 --- a/package.json +++ b/package.json @@ -55,10 +55,11 @@ "check:packages": "bun tools/package-quality/src/cli.ts check", "check:arch": "steiger .", "check:vue": "vue-tsc --noEmit -p tsconfig.json && vue-tsc --noEmit -p packages/vue/tsconfig.json", - "test": "playwright test --project=openpencil --grep-invert @real-llm", - "test:real-llm": "playwright test --project=openpencil --grep @real-llm", - "test:update": "playwright test --project=openpencil --update-snapshots", - "test:figma": "playwright test --project=figma", + "test": "OPENPENCIL_TEST_SERVER=app playwright test --project=openpencil --grep-invert @real-llm", + "test:storybook": "OPENPENCIL_TEST_SERVER=storybook playwright test --project=storybook-chromium", + "test:real-llm": "OPENPENCIL_TEST_SERVER=app playwright test --project=openpencil --grep @real-llm", + "test:update": "OPENPENCIL_TEST_SERVER=app playwright test --project=openpencil --update-snapshots", + "test:figma": "OPENPENCIL_TEST_SERVER=app playwright test --project=figma", "figma:debug": "open -a Figma --args --remote-debugging-port=9222", "typecheck": "tsgo --noEmit && bun run check:vue", "test:unit": "bun test ./tests/engine", diff --git a/packages/docs/development/testing.md b/packages/docs/development/testing.md index eeb2b3761..0720355ab 100644 --- a/packages/docs/development/testing.md +++ b/packages/docs/development/testing.md @@ -6,6 +6,7 @@ | --------------------- | ---------- | -------------------- | --------------- | | E2E visual regression | Playwright | `bun run test` | `tests/e2e/` | | Figma CDP reference | Playwright | `bun run test:figma` | `tests/figma/` | +| Storybook components | Playwright | `bun run test:storybook` | `tests/e2e/storybook/` | | Unit tests | bun:test | `bun run test:unit` | `tests/engine/` | ## E2E Visual Regression @@ -19,13 +20,15 @@ bun run test:update # Regenerate baseline screenshots ### Server ownership and worktrees -The canonical `playwright.config.ts` starts Vite from the current checkout and waits for its HTTP URL. Vite starts and stops its MCP companion. Server reuse is off by default and always off in CI, so a test run cannot silently attach to another checkout on the default port. +The canonical `playwright.config.ts` owns app, Figma, and Storybook projects. The `test`, `test:update`, `test:real-llm`, and `test:figma` scripts select only the app server; `test:storybook` selects only Storybook on port `6017`. Direct Playwright commands start both servers by default. Set `OPENPENCIL_TEST_SERVER=app`, `storybook`, or `all` to select servers explicitly; `--project` selects tests, not servers. + +App tests start Vite from the current checkout and wait for its HTTP URL. Vite starts and stops its MCP companion. Server reuse is off by default and always off in CI, so a test run cannot silently attach to another checkout on the default port. Defaults are app port `1420` and MCP port `7600`. For concurrent worktrees, choose a free, distinct pair: ```sh OPENPENCIL_TEST_PORT=1482 OPENPENCIL_TEST_MCP_PORT=7682 \ - bunx playwright test tests/e2e/settings --project=openpencil + bun run test tests/e2e/settings ``` The configuration passes the app origin and MCP port to Vite; the companion receives matching CORS configuration and a port-specific socket/discovery directory. Port conflicts fail rather than silently selecting another endpoint. Do not reuse ports across concurrent runs. @@ -44,6 +47,15 @@ For intentional local debugging against an already-running matching server, set The editor supports a test mode that hides UI chrome (toolbar, panels) for clean screenshot capture. Activated via URL parameter. +## Storybook Component Tests + +```sh +bun run test:storybook +bun run test:storybook --list +``` + +The `storybook-chromium` project preserves its own viewport, device scale, screenshot defaults, and reduced-motion browser context. Storybook specs are excluded from the app projects. Snapshot updates use `bun run test:storybook --update-snapshots`. + ## Figma CDP Reference Tests A separate Playwright project connects to Figma via Chrome DevTools Protocol to capture reference screenshots for pixel-perfect comparison. diff --git a/playwright.config.ts b/playwright.config.ts index 3af85fec8..aa06b0141 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from '@playwright/test' +import { defineConfig, type PlaywrightTestConfig } from '@playwright/test' const appPort = process.env.OPENPENCIL_TEST_PORT ?? '1420' const mcpPort = process.env.OPENPENCIL_TEST_MCP_PORT ?? '7600' @@ -14,10 +14,16 @@ if (reuse !== undefined && reuse !== '0' && reuse !== '1') { throw new Error('OPENPENCIL_TEST_REUSE_SERVER must be 0 or 1') } -export default defineConfig({ - testDir: './tests', - timeout: 15_000, - workers: 1, +const server = process.env.OPENPENCIL_TEST_SERVER ?? 'all' +if (!['app', 'storybook', 'all'].includes(server)) { + throw new Error('OPENPENCIL_TEST_SERVER must be app, storybook, or all') +} +const storybookPort = 6017 +if (server === 'all' && [appPort, mcpPort].some((port) => Number(port) === storybookPort)) { + throw new Error('App, MCP, and Storybook test ports must differ') +} + +const appDefaults = { expect: { toHaveScreenshot: { maxDiffPixelRatio: 0.01, @@ -37,17 +43,26 @@ export default defineConfig({ launchOptions: { args: ['--enable-unsafe-swiftshader'] } - }, + } +} satisfies Pick + +export default defineConfig({ + testDir: './tests', + timeout: 15_000, + workers: 1, projects: [ { + ...appDefaults, name: 'openpencil', testDir: './tests/e2e', - testIgnore: '**/native/**', + testIgnore: ['**/native/**', '**/storybook/**'], fullyParallel: false }, { + ...appDefaults, name: 'openpencil-webkit', testDir: './tests/e2e', + testIgnore: ['**/native/**', '**/storybook/**'], testMatch: [ '**/*.webkit.spec.ts', '**/design/panel.spec.ts', @@ -55,23 +70,53 @@ export default defineConfig({ '**/fonts/settings.spec.ts' ], use: { + ...appDefaults.use, browserName: 'webkit' } }, { + ...appDefaults, name: 'figma', testDir: './tests/figma' + }, + { + name: 'storybook-chromium', + testDir: './tests/e2e/storybook', + timeout: 30_000, + use: { + baseURL: `http://localhost:${storybookPort}`, + viewport: { width: 800, height: 600 }, + deviceScaleFactor: 1, + contextOptions: { reducedMotion: 'reduce' } + } } ], - webServer: { - command: `bun run dev --port ${appPort} --strictPort`, - cwd: import.meta.dirname, - url: origin, - env: { - OPENPENCIL_DEV_ORIGIN: origin, - OPENPENCIL_DEV_MCP_PORT: mcpPort, - PORTLESS_URL: '' - }, - reuseExistingServer: !process.env.CI && reuse === '1' - } + webServer: [ + ...(server === 'storybook' + ? [] + : [ + { + command: `bun run dev --port ${appPort} --strictPort`, + cwd: import.meta.dirname, + url: origin, + env: { + OPENPENCIL_DEV_ORIGIN: origin, + OPENPENCIL_DEV_MCP_PORT: mcpPort, + PORTLESS_URL: '' + }, + reuseExistingServer: !process.env.CI && reuse === '1' + } + ]), + ...(server === 'app' + ? [] + : [ + { + command: `bun run storybook -- --port ${storybookPort} --ci --no-open`, + cwd: import.meta.dirname, + url: `http://localhost:${storybookPort}`, + reuseExistingServer: false, + timeout: 120_000 + } + ]) + ] }) diff --git a/playwright.storybook.config.ts b/playwright.storybook.config.ts deleted file mode 100644 index 859496294..000000000 --- a/playwright.storybook.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { defineConfig } from '@playwright/test' - -export default defineConfig({ - testDir: './tests/e2e/storybook', - timeout: 30_000, - workers: 1, - use: { - baseURL: 'http://localhost:6017', - viewport: { width: 800, height: 600 }, - deviceScaleFactor: 1, - reducedMotion: 'reduce' - }, - projects: [{ name: 'storybook-chromium' }], - webServer: { - command: 'bun run storybook -- --port 6017 --ci --no-open', - url: 'http://localhost:6017', - reuseExistingServer: false, - timeout: 120_000 - } -})