mit einem Klick
testing
// Guidance for writing and running tests in Simple History. Covers which framework to use, how to run existing tests, and how to create new ones (including the codegen recording workflow).
// Guidance for writing and running tests in Simple History. Covers which framework to use, how to run existing tests, and how to create new ones (including the codegen recording workflow).
Writes blog posts for simple-history.com matching the author's voice and style. Use when drafting posts, announcements, or marketing copy.
How to design and regenerate marketing screenshots for the wordpress.org plugin page (banner-1544x500.png, screenshot-1.png, etc). Covers the event mix that converts, the reproducible WordPress Playground pipeline that bakes it, and every non-obvious gotcha from previous shoots. Use when refreshing screenshot-1.png, banners, or any image showing the Simple History event log.
Query Google Analytics (GA4) for simple-history.com traffic, top pages, referrers, and the premium_* UTM campaigns that tag admin links. Use when the user asks about visits, traffic sources, which admin links lead to the site, upsell click-through, or campaign performance.
Adds changelog entries to readme.txt following keepachangelog format. Use when updating the Unreleased section or documenting changes for a release.
Surface and triage local issues that are safe and quick for an AI to implement and easy for a human to verify. Use when the user wants to "knock out issues", asks for "quick wins", "low-hanging fruit", "what's easy to do right now", or wants a batch of small tasks to work through in one session.
Guides implementation of structured action links on log events. Use when adding get_action_links() to a logger or migrating from get_log_row_details_output().
| name | testing |
| description | Guidance for writing and running tests in Simple History. Covers which framework to use, how to run existing tests, and how to create new ones (including the codegen recording workflow). |
| allowed-tools | Read, Bash, Edit, Write |
| What you're testing | Framework | Why |
|---|---|---|
| Browser UI, admin pages, visual behaviour | Playwright | Fast, visible, modern — new default for UI tests |
| PHP logic, WordPress integration, database queries | Codeception / WPUnit | Full WordPress environment loaded in PHP |
| HTTP-level WordPress behaviour | Codeception / Functional | No browser needed |
Rule of thumb: If a human would test it by clicking around in a browser, use Playwright. If it's PHP logic, use Codeception.
# Playwright (UI tests) — runs on host machine against the dev WordPress
npm run test:playwright # headless, output in playwright-report/
npm run test:playwright:ui # interactive UI mode (recommended for writing/debugging)
# Codeception (PHP tests) — runs inside Docker
npm run test:wpunit # PHP unit + WordPress integration
npm run test:functional # HTTP-level tests
npm run test:acceptance # legacy browser tests (Selenium — prefer Playwright for new ones)
# Full PHP suite (Codeception only — does NOT include Playwright)
npm test
Note: npm test runs only the Codeception suite. To get full coverage, run both npm run test:playwright and npm test separately.
playwright.config.jstests/playwright/*.spec.jstests/playwright/.auth/admin.json (gitignored) — regenerated on every run by auth.setup.js before tests execute. If auth breaks (wrong credentials, WordPress unreachable), delete tests/playwright/.auth/admin.json and re-run.http://wordpress-stable-docker-mariadb.test:8282 (override with PLAYWRIGHT_BASE_URL env var)claude / claude (override with WP_ADMIN_USER / WP_ADMIN_PASSWORD)playwright-report/ after each run — open it to debug failures# Record a test by clicking through the browser — outputs ready-to-paste code
npx playwright codegen http://wordpress-stable-docker-mariadb.test:8282/wp-admin/
# Run a single spec file (faster iteration than the full suite)
npx playwright test tests/playwright/my-feature.spec.js
See https://playwright.dev/docs/getting-started-cli for the full CLI reference.
When the user asks for help creating a new Playwright test, walk them through this flow:
1. Record by clicking through the browser:
npx playwright codegen --load-storage=tests/playwright/.auth/admin.json http://wordpress-stable-docker-mariadb.test:8282/wp-admin/
--load-storage reuses the cached admin session, so codegen lands straight in wp-admin without making the user log in again. Without it, the recorded test will include the login form fill — noise you'd delete anyway.
In the inspector toolbar, use the Pick locator (cursor icon) and assertion tools (eye / ab / form) to add expect() calls — clicking through alone produces a click log, not a test.
2. Clean up the codegen output. The raw spec looks like this:
import { test, expect } from '@playwright/test';
test.use( { storageState: 'tests/playwright/.auth/admin.json' } ); // remove
test( 'test', async ( { page } ) => {
// rename
await page.goto( 'http://wordpress-stable-docker-mariadb.test:8282/...' ); // make relative
// ...
} );
Apply these conventions to match the rest of the suite:
require() (CommonJS), not import — matches log-page.spec.js, post-logging.spec.js.test.use({ storageState }) — the chromium project in playwright.config.js already sets it./wp-admin/...) — baseURL is configured.test.describe() and share setup in beforeEach()..SimpleHistoryLogitems.is-loaded before asserting on log rows — the list renders empty first, then hydrates from the REST API.3. Save to tests/playwright/<feature-name>.spec.js — testDir picks it up automatically.
4. Run just that file while iterating:
npx playwright test tests/playwright/<feature-name>.spec.js
Or use UI mode for fast edit-and-rerun: npm run test:playwright:ui.
5. Debug failures with npx playwright show-report — frame-by-frame trace replay.
Basic test (no test data needed) — import from @playwright/test:
const { test, expect } = require( '@playwright/test' );
test( 'my test', async ( { page } ) => {
await page.goto(
'/wp-admin/admin.php?page=simple_history_admin_menu_page'
);
// Always wait for the log list to finish loading before asserting.
await page.waitForSelector( '.SimpleHistoryLogitems.is-loaded' );
await expect(
page.locator( '.SimpleHistoryLogitem__text' ).first()
).toBeVisible();
} );
Test that needs to create WordPress data — import from ./fixtures to get requestUtils:
const { test, expect } = require( './fixtures' );
test.beforeEach( async ( { requestUtils } ) => {
post = await requestUtils.createPost( {
title: 'Test post',
status: 'publish',
} );
} );
test.afterEach( async ( { requestUtils } ) => {
// Delete by ID — never use deleteAllPosts() against the live dev site (see warning below).
await requestUtils.rest( {
path: `/wp/v2/posts/${ post.id }`,
method: 'DELETE',
params: { force: true },
} );
} );
test( 'logs post creation', async ( { page } ) => {
await page.goto(
'/wp-admin/admin.php?page=simple_history_admin_menu_page'
);
await page.waitForSelector( '.SimpleHistoryLogitems.is-loaded' );
await expect(
page
.locator( '.SimpleHistoryLogitem__text', { hasText: 'Test post' } )
.first()
).toBeVisible();
} );
requestUtils uses the saved admin session (cookie auth) — no application password needed.
beforeEach via requestUtils and clean up in afterEachrequestUtils methods (selection)requestUtils.createPost( payload ); // returns post object with .id
requestUtils.createPage( payload ); // returns page object with .id
requestUtils.createUser( payload ); // returns user object
requestUtils.rest( { path, method, params, data } ); // arbitrary REST API call
Warning: Do NOT use
deleteAllPosts(),deleteAllPages(), or similar bulk-delete methods. Tests run against the live dev WordPress — bulk deletes will wipe real content. Always delete by ID usingrequestUtils.rest().
codeception.dist.yml, tests/*.suite.ymltests/wpunit/, tests/functional/, tests/acceptance/tests/.env.testingdocker compose run --rm php-cliDon't migrate proactively. When you're already working on a feature that has a Codeception acceptance test (tests/acceptance/*Cest.php), migrate it to Playwright at that point. Leave the rest as-is.