This document covers the Playwright E2E testing setup for the MCP-B NPM packages monorepo.
For test-layer definitions, mocking policy, and coverage expectations, see TESTING_PHILOSOPHY.md.
# Install dependencies (from repo root)
pnpm install
# Build packages
pnpm build
# Install Playwright browsers
pnpm --filter mcp-e2e-tests exec playwright install chromium
# Run E2E tests
pnpm test# Run all E2E tests (headless)
pnpm test
pnpm test:e2e
# Run specific test suites
pnpm --filter mcp-e2e-tests test # Tab transport tests
pnpm --filter mcp-e2e-tests test:react-webmcp # React WebMCP tests
pnpm --filter mcp-e2e-tests test:native-parity # Default Chromium + Chrome Beta parity check
pnpm --filter mcp-e2e-tests test:native-parity:default # Default browser parity lane only
pnpm --filter mcp-e2e-tests test:native-parity:beta # Chrome Beta parity lane only
pnpm test:e2e:tarball:global # Install packed @mcp-b/global into test app and run tab-transport E2E
# Run with Playwright UI (recommended for development)
pnpm test:e2e:ui
# Run in headed mode (see the browser)
pnpm test:e2e:headed
# Debug mode (step through tests)
pnpm test:e2e:debug
# View test report
pnpm --filter mcp-e2e-tests test:reportRun the test apps manually to experiment:
# Start test app dev server
pnpm --filter mcp-tab-transport-test-app dev
# Open http://localhost:5173 in browserThen use the UI to:
- Start MCP Server
- Connect MCP Client
- List available tools
- Execute counter operations
- Monitor the event log
# Start React test app
pnpm --filter react-webmcp-test-app dev
# Open http://localhost:5174 in browserTest React integration:
- See tools registered via
useWebMCP - Connect client to consume tools
- Call tools through MCP protocol
- Observe tool state management
The Playwright test suite validates:
Basic Functionality
- ✅ Application loads correctly
- ✅ MCP server starts successfully
- ✅ MCP client connects to server
- ✅ Tools can be listed via MCP protocol
Tool Execution
- ✅ Increment counter via MCP tool call
- ✅ Decrement counter via MCP tool call
- ✅ Reset counter via MCP tool call
- ✅ Get counter value via MCP tool call
Advanced Scenarios
- ✅ Multiple rapid tool calls (concurrency)
- ✅ Client disconnect and reconnect
- ✅ Server stop and restart
- ✅ Programmatic API usage
Transport Validation
- ✅ TabServerTransport accepts connections
- ✅ TabClientTransport establishes connections
- ✅ postMessage communication works
- ✅ JSON-RPC messages flow correctly
- ✅ State persistence across reconnections
Hook Integration
- ✅
useWebMCPregisters tools on mount - ✅ Tools appear in
navigator.modelContext - ✅ Tools unregister on component unmount
- ✅ React StrictMode compatibility (double mounting)
Tool Registration
- ✅ Multiple tools registered successfully
- ✅ Input schema validation with Zod
- ✅ Tool execution state tracking
- ✅ Error handling in tool handlers
Client Consumption
- ✅
McpClientProviderconnects to server - ✅
useMcpClientlists available tools - ✅ Tools can be called from client
- ✅ Tool responses propagate correctly
State Management
- ✅ Tool execution state (isExecuting, lastResult, error)
- ✅ State updates during tool execution
- ✅ Error state management
- ✅ Execution count tracking
These tests validate that core navigator.modelContextTesting behavior remains stable across:
- Default Playwright Chromium lane (
test:native-parity:default) - Chrome Beta + WebMCP flags lane (
test:native-parity:beta)
This is the required parity gate for changes that affect:
@mcp-b/global@mcp-b/webmcp-polyfill@mcp-b/webmcp-ts-sdk@mcp-b/transports- E2E runtime apps and bridge wiring
Tests run automatically in GitHub Actions:
Workflow: .github/workflows/e2e.yml
Triggers:
- Pull requests to
main - Pushes to
main
Steps:
- Install dependencies
- Build all packages
- Install Playwright browsers
- Run E2E tests
- Run native API parity matrix:
- Default Chromium parity lane
- Chrome Beta parity lane
- Run tarball validation (
@mcp-b/global) against the real test app - Upload test reports and screenshots
Artifacts:
- Playwright HTML report (30 days retention)
- Test results and screenshots (7 days retention)
import { test, expect } from '@playwright/test';
test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
// Setup code
});
test('should do something', async ({ page }) => {
// Arrange
await page.click('#start-server');
// Act
await page.click('#some-action');
// Assert
await expect(page.locator('#result')).toHaveText('Expected');
});
});- Use Data Attributes: The test app uses
data-status,data-counter, etc. for reliable selectors - Wait for State: Use
await expect().toHaveText()with timeouts instead of arbitrary waits - Test User Flows: Write tests from the user's perspective, not implementation details
- Check Logs: Verify operations appear in the event log
- Parallel Safe: Ensure tests don't depend on shared state
For Chrome Extension transports, you'll need to:
- Create a test extension directory
- Use Playwright's
chromium.launchPersistentContext()with extension support - Test cross-extension communication
Example (future implementation):
test('should connect via ExtensionTransport', async () => {
const context = await chromium.launchPersistentContext('', {
args: [
`--disable-extensions-except=/path/to/test-extension`,
`--load-extension=/path/to/test-extension`,
],
});
const page = await context.newPage();
// Test extension transport
});pnpm test:e2e:uiFeatures:
- Visual test execution
- Time travel debugging
- DOM snapshots at each step
- Network activity logs
- Console output
- Pick locator tool
pnpm test:e2e:debugFeatures:
- Playwright Inspector
- Step through tests line by line
- Set breakpoints
- Inspect page state
- Test selector playground
Failed tests automatically capture:
- Screenshots: Saved in
e2e/test-results/ - Traces: Viewable in Playwright UI or via
npx playwright show-trace trace.zip
To always capture traces:
// In playwright.config.ts
use: {
trace: 'on', // 'on' | 'off' | 'on-first-retry' | 'retain-on-failure'
}- Typical test suite runtime: 30-60 seconds
- Test parallelization: Enabled by default (can be configured in
playwright.config.ts) - CI optimization: Uses single worker on CI for stability
If browser installation fails:
# Install browsers with system dependencies
pnpm --filter mcp-e2e-tests exec playwright install --with-deps
# Or install specific browser
pnpm --filter mcp-e2e-tests exec playwright install chromiumPlaywright tab-transport runs default to PLAYWRIGHT_TAB_TRANSPORT_PORT=4173
and only reuse existing servers when PLAYWRIGHT_REUSE_SERVER=1.
If the configured port is in use:
# Find and kill process
lsof -ti:4173 | xargs kill
# Or change port in playwright.config.ts and vite.config.tsIncrease timeout in test or config:
// In test
test('slow test', async ({ page }) => {
test.setTimeout(60000); // 60 seconds
});
// In config
export default defineConfig({
timeout: 60000,
});Common CI issues:
- Missing browsers: Ensure
playwright installruns before tests - Race conditions: Use
expect().toHaveText()instead ofwaitForTimeout() - Flaky tests: Add retry logic or fix timing issues
- Chrome Beta lane failures: Verify
google-chrome-betacan be installed and that the beta lane runs with:--enable-experimental-web-platform-features--enable-features=WebMCPTesting