Implement a small set of high-signal, low-flake Playwright tests to run in CI.
These tests are intended to catch regressions in the “core shell” of the app (navigation, dialogs, prompt UX, file viewer, terminal), without relying on model output.
Add 6 smoke tests to packages/app/e2e/:
/open opens the file picker dialog@<file> inserts a file pill tokenpackages/app/e2e/settings.spec.ts)/open opens file picker (packages/app/e2e/prompt-slash-open.spec.ts)packages/app/e2e/prompt-mention.spec.ts)packages/app/e2e/model-picker.spec.ts)packages/app/e2e/file-viewer.spec.ts)packages/app/e2e/terminal-init.spec.ts)packages/app/script/e2e-local.ts).Existing tests in packages/app/e2e/ already cover:
/sessionWe want to add a focused smoke layer that increases coverage of the most regression-prone UI paths.
All tests should use the shared fixtures in:
packages/app/e2e/fixtures.ts (for sdk, directory, gotoSession)packages/app/e2e/utils.ts (for modKey, promptSelector, terminalToggleKey)Prefer creating new spec files rather than overloading existing ones, so it’s easy to run these tests as a group via grep.
Suggested file layout:
packages/app/e2e/settings.spec.tspackages/app/e2e/prompt-slash-open.spec.tspackages/app/e2e/prompt-mention.spec.tspackages/app/e2e/model-picker.spec.tspackages/app/e2e/file-viewer.spec.tspackages/app/e2e/terminal-init.spec.tsName each test with a “smoke” prefix so CI can run only this suite if needed.
Purpose: catch regressions in dialog infra, settings rendering, tabs.
Steps:
await gotoSession().await page.keyboard.press(${modKey}+Comma).page.getByRole('dialog')).tab, name "Shortcuts").Escape and assert dialog removed.Notes:
Meta+Comma / Control+Comma key name is flaky, fall back to clicking the sidebar settings icon.Escape doesn’t dismiss reliably (tooltips can intercept), fall back to clicking the dialog overlay.Implementation: packages/app/e2e/settings.spec.ts
Acceptance criteria:
/open opens file pickerPurpose: validate contenteditable parsing + slash popover + builtin command dispatch (distinct from mod+p).
Steps:
await gotoSession().promptSelector)./open.Enter (while slash popover is active).Escape.Acceptance criteria:
/open triggers file.open and opens DialogSelectFile.Purpose: validate the most fragile prompt behavior: structured tokens inside contenteditable.
Steps:
await gotoSession().@packages/app/package.json.Tab to accept the active @mention suggestion.page.locator('[data-component="prompt-input"] [data-type="file"][data-path="packages/app/package.json"]') exists.Acceptance criteria:
data-* attributes.Purpose: validate model list rendering, selection wiring, and prompt footer updating.
Implementation approach:
/model to open the model selection dialog (builtin command).Steps:
await gotoSession()./model, press Enter.data-key to avoid time-based model visibility drift).Acceptance criteria:
Purpose: ensure file search + open + file.read + code viewer render all work.
Steps:
await gotoSession().mod+p or /open).packages/app/package.json.package.json tab if needed so the viewer mounts)."name": "@opencode-ai/app".Acceptance criteria:
Purpose: ensure terminal isn’t only “visible”, but actually mounted and functional.
Steps:
await gotoSession().terminalToggleKey (currently Control+Backquote).[data-component="terminal"].[data-component="terminal"] textarea.terminal.new is ctrl+alt+t).Acceptance criteria:
These tests run with fullyParallel: true in packages/app/playwright.config.ts. Keep them isolated and deterministic.
packages/app/package.json rather than bare package.json (multiple hits possible)data-key insteadgetByRole('dialog'), getByRole('textbox'), getByRole('tab')promptSelector, [data-component="terminal"]page.waitForTimeout; use expect(...).toBeVisible() and expect.poll when neededpage.on('pageerror') and fail test if any are emittedpage.on('console', ...)) and fail on type==='error'finallyRun locally:
cd packages/appbun run test:e2e:local -- --grep smokeVerify:
packages/app/e2e/utils.ts for “type into prompt contenteditable” to reduce duplication?@smoke naming convention (or test.describe('smoke', ...)) so CI can target them explicitly?