Playwright
Playwright specs live in packages/web/tests/. Each spec boots a real web server, creates a temp Git repo with a three-way notebook conflict, and drives a Chromium page against the resolver the same way a user would.
What belongs here
Use Playwright for resolver behavior: row rendering, conflict actions, history, settings-driven UI changes, output rendering, and notebook writes that can be proven from the browser-facing flow.
If the behavior only exists when the extension host talks to VS Code APIs or the Git extension directly, move up to the VS Code host layer instead.
CLI commands
npm run test:pw # headless
npm run test:pw:headed # headed browser
npm run test:pw:ui # Playwright UI
npm run test:pw:debug # debug mode
# Direct Playwright invocation
npx playwright test <file>.spec.ts --reporter=line
Compile behavior
npm run test:pw and its variants already compile the repo through pretest:pw.
If you run npx playwright test directly, compile both layers first:
npm run compile
npm run compile-tests
Key files
| File | Purpose |
|---|---|
playwright.config.ts | Worker count, timeouts, global setup/teardown, reporters |
packages/web/tests/fixtures.ts | Shared Playwright fixtures plus notebook-on-disk assertion helpers |
packages/web/tests/globalSetup.ts | Starts the web server once per run |
packages/web/tests/globalTeardown.ts | Stops the shared web server |
test-fixtures/shared/integrationUtils.ts | Browser helpers for reading rows, editing cells, and collecting expected UI output |
packages/web/tests/*.spec.ts | Resolver UI specs |
Adding a new Playwright spec
- Add the spec under
packages/web/tests/using*.spec.ts. - Import
testandexpectfrom./fixtures. - Build a conflict repo from a fixture triplet.
- Launch a resolver session with
conflictSession. - Drive the UI and assert both browser state and notebook state on disk.
Typical shape:
import { test, expect } from './fixtures';
test('my scenario', async ({ conflictRepo, conflictSession }) => {
const workspacePath = conflictRepo({
base: 'general/conflict_0/base.ipynb',
current: 'general/conflict_0/current.ipynb',
incoming: 'general/conflict_0/incoming.ipynb',
});
const { page, conflictFile } = await conflictSession(workspacePath);
// drive UI
// read notebook from disk
});
The fixtures.ts extension gives you the pieces you usually need:
conflictRepo: creates a temp repo frombase/current/incomingnotebooksconflictSession: opens the live resolver in ChromiumapplyAndReadNotebook: clicks Apply and reads the resolved notebook from disk
Settings and disk assertions
Two repo-wide testing rules matter here:
- Respect settings explicitly.
- Tests that depend on auto-resolve or UI toggles should use
writeSettingsFile(...). - This is especially important for things like
ui.showBaseColumn,ui.hideNonConflictOutputs, and auto-resolve behavior.
- Tests that depend on auto-resolve or UI toggles should use
- Verify UI output against the notebook written to disk.
collectExpectedCellsFromUI(...)intest-fixtures/shared/integrationUtils.tsis the usual starting point.- After applying the resolution, use
assertNotebookMatches(...)against the final notebook.
Representative examples:
packages/web/tests/perCellResolution.spec.tspackages/web/tests/settingsMatrix.spec.tspackages/web/tests/reorderUnmatchApplyDisk.spec.ts
Parallel settings isolation
The Playwright fixtures provision an isolated MERGENB_CONFIG_PATH per test worker so parallel runs do not fight over one shared config file. That isolation is why settings-focused specs can safely run in parallel.