Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d11ae61
extension works for MCP Bridge, phase 1 finsihed
stevez Feb 19, 2026
bfc27d9
extension: copy MCP Bridge relay pattern into our extension
stevez Feb 19, 2026
3bd6006
extension: use Playwright's CDPRelayServer + our connect.html for Pha…
stevez Feb 20, 2026
6e3312e
extension: --no-spawn mode with auto-connect relay via blank tab
stevez Feb 20, 2026
14b4e38
chore: gitignore .playwright-mcp and remove cached screenshots
stevez Feb 20, 2026
ede549b
WIP: repl-ext package using playwright-crx (side panel extension)
stevez Feb 20, 2026
e7d3847
extension v3: side panel + direct CDP, --spawn/--cdp-port flags, auto…
stevez Feb 20, 2026
d78e5a7
extension: fix tab auto-select, suppress prompt, improve response fil…
stevez Feb 20, 2026
cdebba7
test: add Playwright E2E tests for extension side panel
stevez Feb 21, 2026
0b1ead8
test: add command integration tests and restructure E2E test suite
stevez Feb 21, 2026
473e13a
fix: update panel unit tests to match current fetch API
stevez Feb 21, 2026
09e25d1
fix: drop Node 18, use 127.0.0.1 and port 0 in server tests
stevez Feb 21, 2026
86e17ef
fix: install Chromium for both playwright versions in CI
stevez Feb 21, 2026
e1dc00e
fix: use explicit playwright CLI path for command tests Chromium install
stevez Feb 21, 2026
fc7afed
extension: recording, go-back/forward fix, and CDP stability improvem…
stevez Feb 21, 2026
8d069a1
fix: resolve strict mode violations with --nth and skip non-interacti…
stevez Feb 21, 2026
0934646
test: add tab command and recording integration E2E tests
stevez Feb 21, 2026
445163f
test: add --nth and non-interactive click recording E2E tests
stevez Feb 21, 2026
252eb80
fix: use auto-retrying assertion in clear test for headed mode
stevez Feb 21, 2026
b7b7744
chore: TypeScript migration + fix --nth timeout + extension save
stevez Feb 22, 2026
b4ba29d
chore: migrate extension package from JavaScript to TypeScript
stevez Feb 22, 2026
19e641b
fix: use channel: 'chromium' for proper headless extension testing
stevez Feb 22, 2026
6a15a6e
fix: use tsc --build for correct dependency-ordered compilation
stevez Feb 22, 2026
b2a97ef
chore: TypeScript cleanup — fix types, update tsconfig, add @ts-noche…
stevez Feb 22, 2026
d7e7e01
fix: suppress snapshot output for non-snapshot commands
stevez Feb 22, 2026
83a1269
chore: remove repl-ext package + fix filterResponse tests
stevez Feb 22, 2026
bbb83c1
chore: v0.5.0 — update docs, bump versions, remove stale files
stevez Feb 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [18, 20, 22]
node-version: [20, 22]

steps:
- uses: actions/checkout@v4
Expand All @@ -25,5 +25,20 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Build and type check
run: npm run build

- name: Run tests
run: npm test
run: npm test --workspaces --if-present

- name: Lint extension
run: npm run lint
working-directory: packages/extension

- name: Install Playwright Chromium
run: npx playwright install chromium
working-directory: packages/extension

- name: Run E2E tests
run: npx playwright test
working-directory: packages/extension
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ node_modules/
.claude/
.playwright-cli/
coverage/
.playwright-mcp/
packages/*/dist/
*.tsbuildinfo
8 changes: 7 additions & 1 deletion BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- [ ] **String escaping misses newlines** — `packages/cli/src/repl.mjs`: `esc()` for verify commands doesn't escape `\n`, `\r`, `\t` — multiline user input breaks generated code
- [ ] **Ghost completion crash on empty array** — `packages/cli/src/repl.mjs`: `renderGhost(matches[0])` has no guard if `cmds` is empty

- [ ] **Auto-inject `expect` in `run-code`** — auto-prepend `const { expect } = require('@playwright/test')` in the `run-code` auto-wrap so users can write `run-code await expect(page).toHaveTitle('Todo')` without manual imports

## Medium Priority

- [ ] **Failed commands not recorded** — `packages/cli/src/repl.mjs`: `session.record(line)` only runs after success; replay files miss failed commands
Expand All @@ -15,5 +17,9 @@

## Low Priority

- [ ] **`@types/chrome` types `sendCommand` as void** — `packages/extension/background.js`: `await chrome.debugger.sendCommand(...)`, `chrome.debugger.attach()`, `chrome.debugger.detach()` are correctly awaited (MV3 returns Promises) but `@types/chrome` still types them as `void`, causing IDE "await has no effect" warnings. Revisit when `@types/chrome` updates or if migrating to TypeScript.

- [ ] **Convert to TypeScript** — migrate `.mjs` files to `.ts` across `packages/core` and `packages/cli` for type safety and better IDE support
- [ ] **Extension server (Phase 8)** — `playwright-repl --extension` starts a WebSocket server; extension connects as thin CDP relay instead of reimplementing all commands
- [x] **Extension server (Phase 8)** — `playwright-repl --extension` starts a WebSocket server; extension connects as thin CDP relay instead of reimplementing all commands
- [ ] **Improve README structure** — Consider splitting README into per-package docs (`packages/cli/README.md`, `packages/extension/README.md`) with a concise root README linking to both. Current root README covers both CLI and extension but could be better organized.
- [ ] **Restructure the extension code structure, add src folder, and add build step
36 changes: 34 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## v0.5.0 — Extension Mode & TypeScript

**2026-02-22**

### Breaking Changes

- **Requires Node.js >= 20** (was >= 18)
- **TypeScript throughout** — all three packages now compiled from TypeScript

### Features

- **Extension mode** (`--extension`): Chrome side panel extension with REPL input, script editor, visual recorder, and export to Playwright tests. Uses direct CDP connection — Engine connects to Chrome via `--remote-debugging-port`.
- **CommandServer**: HTTP server (`POST /run`, `GET /health`) relays commands from the extension panel to the Engine.
- **Recording**: Extension-side recorder captures clicks, form input, selections, checkboxes, and key presses with automatic `--nth` disambiguation for ambiguous text locators.
- **Suppress snapshot for non-snapshot commands**: `goto` now shows only URL and title instead of the full accessibility tree.
- **Text locator `--nth` support**: `click "npm" --nth 1` to target a specific match when multiple elements share the same text.

### Technical Details

- **TypeScript migration**: `packages/core` and `packages/cli` compiled via `tsc --build` with project references; `packages/extension` compiled via Vite.
- **`tsc --build`** handles dependency ordering (core before cli) automatically.
- **Module resolution**: `NodeNext` (tracks latest Node.js module behavior).
- **Testing**: 390+ unit tests (vitest) + 59 E2E tests (Playwright Test) across 3 packages.
- **Extension E2E**: Launches Chrome with the extension loaded, tests panel rendering, command execution, recording, and theme switching.

### Removed

- Stale planning docs (PLAN-CRX.md, PLAN-RECORDING.md, PLAN-TYPESCRIPT.md, MIGRATION_PLAN.md)
- Architecture diagram PNGs (outdated after extension mode redesign)
- `packages/repl-ext/` (moved to separate `playwright-repl-crx` repo)

---

## v0.4.0 — In-Process Engine & Monorepo

**2026-02-18**
Expand All @@ -12,7 +45,7 @@

### Features

- **In-process Engine** (`packages/core/src/engine.mjs`): Wraps Playwright's `BrowserServerBackend` directly — faster startup, simpler architecture, no IPC overhead.
- **In-process Engine** (`packages/core/src/engine.ts`): Wraps Playwright's `BrowserServerBackend` directly — faster startup, simpler architecture, no IPC overhead.
- **Connect mode** (`--connect [port]`): Attach to an existing Chrome instance via CDP. Start Chrome with `--remote-debugging-port=9222`, then `playwright-repl --connect`.
- **Monorepo structure**: Restructured into `packages/core` (engine + utilities) and `packages/cli` (REPL + recorder) using npm workspaces.

Expand All @@ -27,7 +60,6 @@

- Engine uses dependency injection for testability — Playwright internals loaded lazily via absolute path resolution to bypass the `exports` map
- 214 tests (147 cli + 67 core) across 10 test files
- Pure ESM JavaScript, no build step

---

Expand Down
90 changes: 69 additions & 21 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,46 @@ Think of it as a **keyword-driven test runner** (like Robot Framework) backed by
```
playwright-repl/
├── package.json # Root workspace config (npm workspaces)
├── tsconfig.base.json # Shared TypeScript compiler options
├── packages/
│ ├── core/ # Shared engine + utilities
│ ├── core/ # Shared engine + utilities (TypeScript, tsc)
│ │ ├── src/
│ │ │ ├── engine.mjs # Wraps BrowserServerBackend in-process
│ │ │ ├── parser.mjs # Command parsing + alias resolution
│ │ │ ├── page-scripts.mjs # Text locators + assertion helpers
│ │ │ ├── completion-data.mjs # Ghost completion items
│ │ │ ├── colors.mjs # ANSI color helpers
│ │ │ └── resolve.mjs # COMMANDS map, minimist re-export
│ │ │ ├── engine.ts # Wraps BrowserServerBackend in-process
│ │ │ ├── parser.ts # Command parsing + alias resolution
│ │ │ ├── page-scripts.ts # Text locators + assertion helpers
│ │ │ ├── completion-data.ts # Ghost completion items
│ │ │ ├── extension-server.ts # HTTP server for extension commands
│ │ │ ├── colors.ts # ANSI color helpers
│ │ │ └── resolve.ts # COMMANDS map, minimist re-export
│ │ ├── dist/ # Compiled output (gitignored)
│ │ └── test/
│ │
│ └── cli/ # Terminal REPL (published as "playwright-repl")
│ ├── bin/
│ │ └── playwright-repl.mjs # CLI entry point
│ ├── cli/ # Terminal REPL (published as "playwright-repl", TypeScript, tsc)
│ │ ├── src/
│ │ │ ├── playwright-repl.ts # CLI entry point (compiles to dist/)
│ │ │ ├── repl.ts # Interactive readline loop
│ │ │ ├── recorder.ts # Session recording/replay
│ │ │ └── index.ts # Public API exports
│ │ ├── dist/ # Compiled output (gitignored)
│ │ ├── test/
│ │ └── examples/ # .pw session files
│ │
│ └── extension/ # Chrome side panel extension (TypeScript, Vite)
│ ├── public/
│ │ └── manifest.json # Manifest V3 config (copied to dist/ by Vite)
│ ├── src/
│ │ ├── repl.mjs # Interactive readline loop
│ │ ├── recorder.mjs # Session recording/replay
│ │ └── index.mjs # Public API exports
│ ├── test/
│ └── examples/ # .pw session files
│ │ ├── background.ts # Side panel behavior + recording handlers
│ │ ├── panel/ # Side panel UI
│ │ │ ├── panel.html
│ │ │ ├── panel.ts
│ │ │ └── panel.css
│ │ ├── content/
│ │ │ └── recorder.ts # Event recorder injected into pages
│ │ └── lib/
│ │ └── converter.ts # .pw → Playwright test export
│ ├── dist/ # Vite build output (gitignored, loaded by Chrome)
│ ├── vite.config.ts # Vite build config (3 entry points)
│ └── e2e/ # Playwright E2E tests
```

## Architecture
Expand Down Expand Up @@ -75,7 +95,7 @@ browser: locator.click()
Chrome: actual DOM click event
```

### Engine (packages/core/src/engine.mjs)
### Engine (packages/core/src/engine.ts)

The `Engine` class wraps Playwright's `BrowserServerBackend` in-process:

Expand All @@ -90,6 +110,7 @@ await engine.close();
Three connection modes via `start(opts)`:
- **launch** (default): `contextFactory(config)` → new browser
- **connect**: `opts.connect = 9222` → `cdpEndpoint` → `connectOverCDP()`
- **extension**: `opts.extension = true` → starts `CommandServer`, Chrome launched with `--remote-debugging-port`, side panel sends commands via HTTP
- Dependency injection: constructor accepts `deps` for testing

Key Playwright internals used (via `createRequire`):
Expand All @@ -99,6 +120,31 @@ Key Playwright internals used (via `createRequire`):
- `playwright/lib/mcp/terminal/commands` → `commands` map
- `playwright/lib/mcp/terminal/command` → `parseCommand`

### CommandServer (packages/core/src/extension-server.ts)

When `--extension` mode is used, `CommandServer` starts an HTTP server:

```
┌──────────────────────────────────────────────┐
│ Chrome Extension (Side Panel) │
│ panel.js ─── fetch POST /run ───────────┐ │
│ ▲ │ │
│ │ JSON response │ │
└─────┼────────────────────────────────────┼───┘
│ │
│ ▼
┌─────────────────────────────────────────────────────┐
│ CommandServer (HTTP :3000) │
│ ├── POST /run ← panel sends commands here │
│ └── GET /health ← panel checks server status │
│ Engine → connectOverCDP → CDP :3001 → Chrome │
└─────────────────────────────────────────────────────┘
```

- **Direct CDP**: Engine connects to Chrome via `--remote-debugging-port` (no relay)
- **Command channel**: panel sends commands via `fetch()` → CommandServer → `Engine.run()` → results back
- **Recording**: extension-side (inject recorder.js via `chrome.scripting.executeScript`)

### Element Refs (e1, e5, etc.)

When you run `snapshot`, Playwright walks the page's accessibility tree via CDP, assigns short refs like `e1`, `e2`, `e5` to interactive elements. When you later say `click e5`, it resolves back via the backend's internal ref tracking.
Expand Down Expand Up @@ -137,17 +183,19 @@ async function processQueue() {

## Tech Stack

- **Runtime**: Node.js (ESM modules, `.mjs`)
- **Runtime**: Node.js (ESM modules)
- **Language**: TypeScript throughout — `packages/core` and `packages/cli` compiled via `tsc`; `packages/extension` compiled via Vite
- **Build**: `tsc --build packages/core packages/cli` (project references) + Vite for extension. Run `npm run build` at root.
- **Dependencies**: `minimist` (command parsing), `playwright@>=1.59.0-alpha` (browser engine)
- **Monorepo**: npm workspaces (`packages/core`, `packages/cli`)
- **Testing**: vitest
- **Monorepo**: npm workspaces (`packages/core`, `packages/cli`, `packages/extension`)
- **Testing**: vitest (unit tests), Playwright Test (extension E2E)
- **Key insight**: `playwright@1.59.0-alpha` includes `lib/mcp/browser/` (BrowserServerBackend, contextFactory).
The stable `playwright@1.58` does NOT. Once 1.59 goes stable, the alpha pin can be removed.
- No build step — plain ESM JavaScript

## Code Style

- ESM imports (`import ... from`)
- TypeScript with `"module": "NodeNext"` — relative imports in core/cli use `.js` extensions (resolved to `.ts` at compile time)
- Extension uses Vite — standard `.ts` imports (no `.js` extension needed)
- Async/await throughout
- No TypeScript (keep it simple, scripting-oriented)
- Sections separated by `// ─── Section Name ───` comments
Loading