Guide

E2E Testing

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

CommandDescription
npm run test:e2eRun all E2E tests (headless)
npm run test:e2e:headedRun with a visible browser window
npm run test:e2e:debugRun in Playwright debug mode (step-through)
npm run test:e2e:uiOpen the Playwright UI for interactive runs

Environment Commands

CommandDescription
npm run env:start:e2eStart the Docker environment (reuses existing)
npm run env:reset:e2eStart and update the environment
npm run env:stop:e2eStop the Docker containers
npm run env:clean:e2eDestroy and remove all environment data

Architecture

How It Works

  1. wp-env reads .wp-env.json at the repo root and spins up WordPress + MySQL in Docker.
  2. The plugin directory is bind-mounted into the container so file changes are reflected immediately.
  3. playwright.config.js extends the @wordpress/scripts base config, sets the test directory to tests/e2e/specs/, and points to the Docker environment on port 8877.
  4. tests/e2e/global-setup.js runs before all tests — it authenticates via the REST API, activates the plugin, and cleans up stale posts/pages.
  5. Each spec file uses helpers from @wordpress/e2e-test-utils-playwright (admin, editor, page, requestUtils).

Key Files

FilePurpose
.wp-env.jsonWordPress environment config (plugins, themes, ports, PHP version)
playwright.config.jsPlaywright config (test dir, base URL, web server command)
tests/e2e/global-setup.jsPre-test setup (auth, plugin activation, cleanup)
tests/e2e/specs/*.spec.jsTest specifications
bin/e2e-env.mjsHelper script to run wp-env with consistent port/runtime settings

Port Allocation

PortService
8877WordPress development site (E2E tests hit this)
8889WordPress 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/pages
  • editor — insert blocks, get content, interact with the block editor
  • page — Playwright Page object for direct DOM interaction
  • requestUtils — 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 chromium runs before the test step.
  • Check that block assets are built (cd blocks && npm run build).
  • Verify the WP_BASE_URL environment variable matches the port in .wp-env.json.

Was this helpful?