End-to-end tests use Playwright through @wordpress/scripts test-playwright and run against a Docker-based WordPress environment managed by wp-env.
Based on Introducing e2e testing to WordPress block development by Aki Hamano.
Prerequisites
- Node.js 20+
- Docker (Desktop or Engine) running and available on your PATH
- Root and
blocks/dependencies installed
Quick Start
# 1. Install dependencies (if not already done)
npm install
cd blocks && npm install && cd ..
# 2. Build both the root plugin and block assets
npm run build
cd blocks && npm run build && cd ..
# 3. Start the Docker-based WordPress environment
npx wp-env start --runtime=docker --update
# 4. Run the tests
WP_BASE_URL=http://localhost:8877 npx wp-scripts test-playwright --project=chromium
Test Commands
| Command | Description |
|---|---|
npm run test:e2e | Run all E2E tests (headless) |
npm run test:e2e:headed | Run with a visible browser window |
npm run test:e2e:debug | Run in Playwright debug mode (step-through) |
npm run test:e2e:ui | Open the Playwright UI for interactive runs |
Environment Commands
| Command | Description |
|---|---|
npm run env:start:e2e | Start the Docker environment (reuses existing) |
npm run env:reset:e2e | Start and update the environment |
npm run env:stop:e2e | Stop the Docker containers |
npm run env:clean:e2e | Destroy and remove all environment data |
Architecture
How It Works
wp-envreads.wp-env.jsonat the repo root and spins up WordPress + MySQL in Docker.- The plugin directory is bind-mounted into the container so file changes are reflected immediately.
playwright.config.jsextends the@wordpress/scriptsbase config, sets the test directory totests/e2e/specs/, and points to the Docker environment on port 8877.tests/e2e/global-setup.jsruns before all tests — it authenticates via the REST API, activates the plugin, and cleans up stale posts/pages.- Each spec file uses helpers from
@wordpress/e2e-test-utils-playwright(admin,editor,page,requestUtils).
Key Files
| File | Purpose |
|---|---|
.wp-env.json | WordPress environment config (plugins, themes, ports, PHP version) |
playwright.config.js | Playwright config (test dir, base URL, web server command) |
tests/e2e/global-setup.js | Pre-test setup (auth, plugin activation, cleanup) |
tests/e2e/specs/*.spec.js | Test specifications |
bin/e2e-env.mjs | Helper script to run wp-env with consistent port/runtime settings |
Port Allocation
| Port | Service |
|---|---|
| 8877 | WordPress development site (E2E tests hit this) |
| 8889 | WordPress test site |
Port 8877 is used to avoid conflicts with Local by Flywheel or other local WordPress tools that commonly use 8888.
Current Test Specs
install-activate.spec.js
Navigates to the plugins admin page and verifies that the Linchpin Block Library plugin is active (showing the "Deactivate" link). Takes a screenshot of the plugins page.
insert-block.spec.js
Creates a new page, programmatically inserts a linchpin/accordion block with an accordion-pane inner block containing a paragraph, then asserts:
- Exactly one block exists on the page.
- The block name is
linchpin/accordion. - The serialized post content contains the expected block comments, title, and paragraph text.
- Screenshots are taken at each stage: empty editor, block inserted, assertions verified.
Writing New Tests
Tests live in tests/e2e/specs/ and follow the WordPress E2E conventions:
const {
expect,
test,
} = require( '@wordpress/e2e-test-utils-playwright' );
test.describe( 'My Feature', () => {
test.beforeEach( async ( { admin } ) => {
await admin.createNewPost( { postType: 'page' } );
} );
test( 'should do something', async ( { editor, page }, testInfo ) => {
await editor.insertBlock( { name: 'linchpin/my-block' } );
const blocks = await editor.getBlocks();
expect( blocks ).toHaveLength( 1 );
await page.screenshot( {
path: testInfo.outputPath( 'result.png' ),
fullPage: true,
} );
} );
} );
Available Fixtures
The @wordpress/e2e-test-utils-playwright package provides these test fixtures:
admin— navigate to admin pages, create posts/pageseditor— insert blocks, get content, interact with the block editorpage— Playwright Page object for direct DOM interactionrequestUtils— REST API helpers (create posts, manage plugins, upload media)
Plugin Slug Note
When calling requestUtils.activatePlugin(), use the slug derived from the Plugin Name header ('linchpin-block-library'), not the directory name. The utility converts plugin names to param-case via the change-case library.
Screenshots & Artifacts
Tests capture screenshots into testInfo.outputPath(). When running in CI, all artifacts (screenshots, videos, traces) are uploaded to GitHub Actions as build artifacts.
To view a Playwright trace locally:
npx playwright show-trace artifacts/test-results/<test-folder>/trace.zip
Troubleshooting
"The plugin isn't installed"
The activatePlugin() utility maps plugins by their display name, not directory. Use 'linchpin-block-library' as the slug (see Plugin Slug Note above).
Port 8877 already in use
Another wp-env instance or service is using the port. Stop it first:
npx wp-env stop
# or find and kill the process
lsof -i :8877
Docker database connection errors
Stale Docker volumes can cause this. Clean up and restart:
npx wp-env stop
npx wp-env clean all
npx wp-env start --runtime=docker --update
Tests pass locally but fail in CI
- Ensure
npx playwright install --with-deps chromiumruns before the test step. - Check that block assets are built (
cd blocks && npm run build). - Verify the
WP_BASE_URLenvironment variable matches the port in.wp-env.json.