claude-plugin-wordpress-manager 1.5.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +97 -0
  3. package/README.md +27 -13
  4. package/agents/wp-accessibility-auditor.md +206 -0
  5. package/agents/wp-content-strategist.md +18 -0
  6. package/agents/wp-deployment-engineer.md +34 -2
  7. package/agents/wp-performance-optimizer.md +12 -0
  8. package/agents/wp-security-auditor.md +20 -0
  9. package/agents/wp-security-hardener.md +266 -0
  10. package/agents/wp-site-manager.md +14 -0
  11. package/agents/wp-test-engineer.md +207 -0
  12. package/docs/guides/INDEX.md +46 -0
  13. package/docs/guides/wp-blog.md +590 -0
  14. package/docs/guides/wp-design-system.md +976 -0
  15. package/docs/guides/wp-ecommerce.md +786 -0
  16. package/docs/guides/wp-landing-page.md +762 -0
  17. package/docs/guides/wp-portfolio.md +713 -0
  18. package/docs/plans/2026-02-27-design-system-guide-design.md +30 -0
  19. package/docs/plans/2026-02-27-site-type-guides-design.md +44 -0
  20. package/package.json +2 -2
  21. package/skills/wordpress-router/references/decision-tree.md +12 -2
  22. package/skills/wp-accessibility/SKILL.md +170 -0
  23. package/skills/wp-accessibility/references/a11y-audit-tools.md +248 -0
  24. package/skills/wp-accessibility/references/a11y-testing.md +222 -0
  25. package/skills/wp-accessibility/references/block-a11y.md +247 -0
  26. package/skills/wp-accessibility/references/interactive-a11y.md +272 -0
  27. package/skills/wp-accessibility/references/media-a11y.md +254 -0
  28. package/skills/wp-accessibility/references/theme-a11y.md +309 -0
  29. package/skills/wp-audit/SKILL.md +4 -0
  30. package/skills/wp-block-development/SKILL.md +5 -0
  31. package/skills/wp-block-themes/SKILL.md +4 -0
  32. package/skills/wp-e2e-testing/SKILL.md +186 -0
  33. package/skills/wp-e2e-testing/references/ci-integration.md +174 -0
  34. package/skills/wp-e2e-testing/references/jest-wordpress.md +114 -0
  35. package/skills/wp-e2e-testing/references/phpunit-wordpress.md +141 -0
  36. package/skills/wp-e2e-testing/references/playwright-wordpress.md +108 -0
  37. package/skills/wp-e2e-testing/references/test-data-generation.md +127 -0
  38. package/skills/wp-e2e-testing/references/visual-regression.md +107 -0
  39. package/skills/wp-e2e-testing/references/wp-env-setup.md +97 -0
  40. package/skills/wp-e2e-testing/scripts/test_inspect.mjs +375 -0
  41. package/skills/wp-headless/SKILL.md +168 -0
  42. package/skills/wp-headless/references/api-layer-choice.md +160 -0
  43. package/skills/wp-headless/references/cors-config.md +245 -0
  44. package/skills/wp-headless/references/frontend-integration.md +331 -0
  45. package/skills/wp-headless/references/headless-auth.md +286 -0
  46. package/skills/wp-headless/references/webhooks.md +277 -0
  47. package/skills/wp-headless/references/wpgraphql.md +331 -0
  48. package/skills/wp-headless/scripts/headless_inspect.mjs +321 -0
  49. package/skills/wp-i18n/SKILL.md +170 -0
  50. package/skills/wp-i18n/references/js-i18n.md +201 -0
  51. package/skills/wp-i18n/references/multilingual-setup.md +219 -0
  52. package/skills/wp-i18n/references/php-i18n.md +196 -0
  53. package/skills/wp-i18n/references/rtl-support.md +206 -0
  54. package/skills/wp-i18n/references/translation-workflow.md +178 -0
  55. package/skills/wp-i18n/references/wpcli-i18n.md +177 -0
  56. package/skills/wp-i18n/scripts/i18n_inspect.mjs +330 -0
  57. package/skills/wp-interactivity-api/SKILL.md +4 -0
  58. package/skills/wp-plugin-development/SKILL.md +6 -0
  59. package/skills/wp-rest-api/SKILL.md +4 -0
  60. package/skills/wp-security/SKILL.md +179 -0
  61. package/skills/wp-security/references/api-restriction.md +147 -0
  62. package/skills/wp-security/references/authentication-hardening.md +105 -0
  63. package/skills/wp-security/references/filesystem-hardening.md +105 -0
  64. package/skills/wp-security/references/http-headers.md +105 -0
  65. package/skills/wp-security/references/incident-response.md +144 -0
  66. package/skills/wp-security/references/user-capabilities.md +115 -0
  67. package/skills/wp-security/references/wp-config-security.md +129 -0
  68. package/skills/wp-security/scripts/security_inspect.mjs +393 -0
@@ -0,0 +1,114 @@
1
+ # Jest for WordPress JavaScript Testing
2
+
3
+ Use this file when writing or configuring Jest unit tests for WordPress JavaScript code.
4
+
5
+ ## Setup with @wordpress/scripts
6
+
7
+ `@wordpress/scripts` includes a preconfigured Jest setup. No separate Jest config needed:
8
+
9
+ ```bash
10
+ npm install -D @wordpress/scripts
11
+ ```
12
+
13
+ Add to `package.json`:
14
+ ```json
15
+ {
16
+ "scripts": {
17
+ "test:unit": "wp-scripts test-unit-js",
18
+ "test:unit:watch": "wp-scripts test-unit-js --watch"
19
+ }
20
+ }
21
+ ```
22
+
23
+ ## Test file location
24
+
25
+ By convention, tests go in:
26
+ - `src/component/__tests__/component.test.js` — colocated with source
27
+ - `src/component/test/component.test.js` — `test/` subdirectory
28
+ - `tests/js/` — separate test directory (configure in `jest.config.js`)
29
+
30
+ ## Writing tests
31
+
32
+ ```js
33
+ import { render, screen } from '@testing-library/react';
34
+ import MyBlock from '../edit';
35
+
36
+ describe('MyBlock', () => {
37
+ it('renders the block content', () => {
38
+ const attributes = { content: 'Hello World' };
39
+ render(<MyBlock attributes={attributes} />);
40
+ expect(screen.getByText('Hello World')).toBeInTheDocument();
41
+ });
42
+ });
43
+ ```
44
+
45
+ ## Mocking WordPress globals
46
+
47
+ Create `tests/js/setup-globals.js`:
48
+
49
+ ```js
50
+ // Mock wp global
51
+ global.wp = {
52
+ element: require('@wordpress/element'),
53
+ data: require('@wordpress/data'),
54
+ i18n: { __: (str) => str, _n: (s, p, n) => (n === 1 ? s : p) },
55
+ };
56
+
57
+ // Mock jQuery if needed
58
+ global.jQuery = jest.fn(() => ({
59
+ on: jest.fn(),
60
+ off: jest.fn(),
61
+ trigger: jest.fn(),
62
+ ready: jest.fn((fn) => fn()),
63
+ }));
64
+ ```
65
+
66
+ Reference in `jest.config.js`:
67
+ ```js
68
+ module.exports = {
69
+ ...require('@wordpress/scripts/config/jest-unit.config'),
70
+ setupFiles: ['./tests/js/setup-globals.js'],
71
+ };
72
+ ```
73
+
74
+ ## Testing @wordpress/data stores
75
+
76
+ ```js
77
+ import { createRegistry } from '@wordpress/data';
78
+ import { store as myStore } from '../store';
79
+
80
+ describe('My Store', () => {
81
+ let registry;
82
+
83
+ beforeEach(() => {
84
+ registry = createRegistry();
85
+ registry.register(myStore);
86
+ });
87
+
88
+ it('returns default state', () => {
89
+ const result = registry.select(myStore).getItems();
90
+ expect(result).toEqual([]);
91
+ });
92
+
93
+ it('adds an item via action', () => {
94
+ registry.dispatch(myStore).addItem({ id: 1, title: 'Test' });
95
+ const result = registry.select(myStore).getItems();
96
+ expect(result).toHaveLength(1);
97
+ });
98
+ });
99
+ ```
100
+
101
+ ## Running
102
+
103
+ ```bash
104
+ npx wp-scripts test-unit-js # Run all tests
105
+ npx wp-scripts test-unit-js --watch # Watch mode
106
+ npx wp-scripts test-unit-js --coverage # With coverage report
107
+ npx wp-scripts test-unit-js -- path/to/test # Run specific test
108
+ ```
109
+
110
+ ## Common issues
111
+
112
+ - **"Cannot find module @wordpress/..."**: ensure the package is in dependencies; `@wordpress/scripts` provides many but not all
113
+ - **JSX transform errors**: `@wordpress/scripts` handles this; don't add a separate Babel config unless needed
114
+ - **Snapshot mismatches after update**: review changes, then `npx wp-scripts test-unit-js --updateSnapshot`
@@ -0,0 +1,141 @@
1
+ # PHPUnit for WordPress
2
+
3
+ Use this file when writing or configuring PHPUnit tests for WordPress PHP code.
4
+
5
+ ## Scaffolding with WP-CLI
6
+
7
+ ```bash
8
+ wp scaffold plugin-tests my-plugin
9
+ ```
10
+
11
+ This creates:
12
+ - `phpunit.xml.dist` — PHPUnit configuration
13
+ - `tests/bootstrap.php` — WordPress test library loader
14
+ - `tests/test-sample.php` — Example test
15
+ - `bin/install-wp-tests.sh` — Script to install WP test library
16
+
17
+ ## Running with wp-env (recommended)
18
+
19
+ wp-env includes the WordPress test suite out of the box:
20
+
21
+ ```bash
22
+ npx wp-env run tests-cli --env-cwd=wp-content/plugins/my-plugin phpunit
23
+ npx wp-env run tests-cli --env-cwd=wp-content/plugins/my-plugin phpunit -- --filter=test_activation
24
+ ```
25
+
26
+ ## Writing tests
27
+
28
+ ```php
29
+ class Test_My_Plugin extends WP_UnitTestCase {
30
+
31
+ public function set_up(): void {
32
+ parent::set_up();
33
+ // Runs before each test
34
+ }
35
+
36
+ public function tear_down(): void {
37
+ // Runs after each test
38
+ parent::tear_down();
39
+ }
40
+
41
+ public function test_plugin_activates(): void {
42
+ $this->assertTrue( is_plugin_active( 'my-plugin/my-plugin.php' ) );
43
+ }
44
+
45
+ public function test_custom_post_type_registered(): void {
46
+ $this->assertTrue( post_type_exists( 'book' ) );
47
+ }
48
+
49
+ public function test_hook_fires(): void {
50
+ $fired = false;
51
+ add_action( 'my_plugin_init', function() use ( &$fired ) {
52
+ $fired = true;
53
+ });
54
+ do_action( 'my_plugin_init' );
55
+ $this->assertTrue( $fired );
56
+ }
57
+
58
+ public function test_filter_modifies_value(): void {
59
+ add_filter( 'my_plugin_title', function( $title ) {
60
+ return 'Modified: ' . $title;
61
+ });
62
+ $result = apply_filters( 'my_plugin_title', 'Original' );
63
+ $this->assertSame( 'Modified: Original', $result );
64
+ }
65
+ }
66
+ ```
67
+
68
+ ## Testing REST API endpoints
69
+
70
+ ```php
71
+ class Test_REST_API extends WP_UnitTestCase {
72
+
73
+ public function set_up(): void {
74
+ parent::set_up();
75
+ do_action( 'rest_api_init' );
76
+ }
77
+
78
+ public function test_endpoint_registered(): void {
79
+ $routes = rest_get_server()->get_routes();
80
+ $this->assertArrayHasKey( '/my-plugin/v1/items', $routes );
81
+ }
82
+
83
+ public function test_get_items(): void {
84
+ $request = new WP_REST_Request( 'GET', '/my-plugin/v1/items' );
85
+ $response = rest_get_server()->dispatch( $request );
86
+ $this->assertSame( 200, $response->get_status() );
87
+ }
88
+
89
+ public function test_unauthorized_access(): void {
90
+ $request = new WP_REST_Request( 'POST', '/my-plugin/v1/items' );
91
+ $response = rest_get_server()->dispatch( $request );
92
+ $this->assertSame( 401, $response->get_status() );
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## Test factories
98
+
99
+ ```php
100
+ public function test_post_with_meta(): void {
101
+ $post_id = self::factory()->post->create([
102
+ 'post_title' => 'Test Post',
103
+ 'post_status' => 'publish',
104
+ ]);
105
+ update_post_meta( $post_id, 'my_key', 'my_value' );
106
+
107
+ $this->assertSame( 'my_value', get_post_meta( $post_id, 'my_key', true ) );
108
+ }
109
+
110
+ public function test_user_with_role(): void {
111
+ $user_id = self::factory()->user->create([ 'role' => 'editor' ]);
112
+ wp_set_current_user( $user_id );
113
+
114
+ $this->assertTrue( current_user_can( 'edit_posts' ) );
115
+ $this->assertFalse( current_user_can( 'manage_options' ) );
116
+ }
117
+ ```
118
+
119
+ ## phpunit.xml.dist configuration
120
+
121
+ ```xml
122
+ <phpunit bootstrap="tests/bootstrap.php" colors="true">
123
+ <testsuites>
124
+ <testsuite name="My Plugin">
125
+ <directory suffix=".php">./tests/</directory>
126
+ </testsuite>
127
+ </testsuites>
128
+ <coverage>
129
+ <include>
130
+ <directory suffix=".php">./includes/</directory>
131
+ </include>
132
+ </coverage>
133
+ </phpunit>
134
+ ```
135
+
136
+ ## Common issues
137
+
138
+ - **"Class WP_UnitTestCase not found"**: bootstrap not loading; use wp-env or re-run `bin/install-wp-tests.sh`
139
+ - **"No tests executed"**: test methods must start with `test_`; class must extend `WP_UnitTestCase`
140
+ - **Database errors**: each test runs in a transaction that rolls back; avoid `dbDelta()` in tests
141
+ - **`set_up()` not `setUp()`**: WordPress renamed the method in WP 5.9 for snake_case consistency
@@ -0,0 +1,108 @@
1
+ # Playwright for WordPress E2E Testing
2
+
3
+ Use this file when writing or configuring Playwright E2E tests for WordPress projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -D @playwright/test @wordpress/e2e-test-utils-playwright
9
+ npx playwright install chromium
10
+ ```
11
+
12
+ ## Configuration (`playwright.config.ts`)
13
+
14
+ ```ts
15
+ import { defineConfig } from '@playwright/test';
16
+
17
+ export default defineConfig({
18
+ testDir: './tests/e2e',
19
+ outputDir: './tests/e2e/artifacts',
20
+ fullyParallel: false, // WordPress state can conflict in parallel
21
+ retries: process.env.CI ? 2 : 0,
22
+ use: {
23
+ baseURL: process.env.WP_BASE_URL || 'http://localhost:8888',
24
+ storageState: process.env.STORAGE_STATE_PATH,
25
+ trace: 'retain-on-failure',
26
+ screenshot: 'only-on-failure',
27
+ },
28
+ webServer: {
29
+ command: 'npx wp-env start',
30
+ url: 'http://localhost:8888',
31
+ reuseExistingServer: true,
32
+ timeout: 120000,
33
+ },
34
+ });
35
+ ```
36
+
37
+ ## WordPress-specific fixtures
38
+
39
+ `@wordpress/e2e-test-utils-playwright` provides fixtures:
40
+
41
+ ```ts
42
+ import { test, expect } from '@wordpress/e2e-test-utils-playwright';
43
+
44
+ test('can create a post', async ({ admin, editor, page }) => {
45
+ await admin.visitAdminPage('post-new.php');
46
+ await editor.canvas.locator('[data-type="core/paragraph"]').click();
47
+ await page.keyboard.type('Hello from Playwright');
48
+ await editor.publishPost();
49
+ await expect(page.locator('.components-snackbar')).toContainText('published');
50
+ });
51
+ ```
52
+
53
+ Key fixtures:
54
+ - `admin` — navigate to admin pages, authenticated as admin
55
+ - `editor` — block editor helpers (canvas, inserter, publish)
56
+ - `requestUtils` — REST API helpers for test data setup
57
+ - `page` — standard Playwright page object
58
+
59
+ ## Authentication setup
60
+
61
+ Create a global setup that stores auth state:
62
+
63
+ ```ts
64
+ // tests/e2e/global-setup.ts
65
+ import { request } from '@playwright/test';
66
+
67
+ export default async function globalSetup() {
68
+ const api = await request.newContext({ baseURL: 'http://localhost:8888' });
69
+ await api.post('/wp-login.php', {
70
+ form: { log: 'admin', pwd: 'password', rememberme: 'forever' },
71
+ });
72
+ await api.storageState({ path: './tests/e2e/.auth/admin.json' });
73
+ await api.dispose();
74
+ }
75
+ ```
76
+
77
+ ## Seeding test data via REST
78
+
79
+ ```ts
80
+ test.beforeAll(async ({ requestUtils }) => {
81
+ await requestUtils.createPost({
82
+ title: 'Test Post',
83
+ content: '<!-- wp:paragraph --><p>Content</p><!-- /wp:paragraph -->',
84
+ status: 'publish',
85
+ });
86
+ });
87
+
88
+ test.afterAll(async ({ requestUtils }) => {
89
+ await requestUtils.deleteAllPosts();
90
+ });
91
+ ```
92
+
93
+ ## Running tests
94
+
95
+ ```bash
96
+ npx wp-scripts test-playwright # Using @wordpress/scripts
97
+ npx playwright test # Direct Playwright
98
+ npx playwright test --ui # Interactive UI mode
99
+ npx playwright test --grep "block editor" # Filter by title
100
+ ```
101
+
102
+ ## Best practices
103
+
104
+ - Use `test.describe.serial()` for tests that depend on order
105
+ - Clean up test data in `afterAll` to prevent state leakage
106
+ - Use `page.waitForLoadState('networkidle')` sparingly — prefer specific selectors
107
+ - Store authentication state to avoid login in every test
108
+ - Use `test.slow()` for tests involving complex editor operations
@@ -0,0 +1,127 @@
1
+ # Test Data Generation
2
+
3
+ Use this file when creating test data and fixtures for WordPress tests.
4
+
5
+ ## PHPUnit: WP test factories
6
+
7
+ `WP_UnitTestCase` provides factory methods for creating test data:
8
+
9
+ ```php
10
+ // Posts
11
+ $post_id = self::factory()->post->create([
12
+ 'post_title' => 'Test Post',
13
+ 'post_status' => 'publish',
14
+ 'post_type' => 'post',
15
+ ]);
16
+
17
+ // Multiple posts
18
+ $post_ids = self::factory()->post->create_many(10, [
19
+ 'post_status' => 'publish',
20
+ ]);
21
+
22
+ // Users
23
+ $user_id = self::factory()->user->create([
24
+ 'role' => 'editor',
25
+ 'user_login' => 'testeditor',
26
+ ]);
27
+
28
+ // Terms
29
+ $term_id = self::factory()->term->create([
30
+ 'taxonomy' => 'category',
31
+ 'name' => 'Test Category',
32
+ ]);
33
+
34
+ // Comments
35
+ $comment_id = self::factory()->comment->create([
36
+ 'comment_post_ID' => $post_id,
37
+ 'comment_content' => 'Test comment',
38
+ ]);
39
+
40
+ // Attachments
41
+ $attachment_id = self::factory()->attachment->create_upload_object(
42
+ DIR_TESTDATA . '/images/canola.jpg',
43
+ $post_id
44
+ );
45
+ ```
46
+
47
+ ## Playwright E2E: requestUtils
48
+
49
+ `@wordpress/e2e-test-utils-playwright` provides REST API-based data creation:
50
+
51
+ ```ts
52
+ test.beforeAll(async ({ requestUtils }) => {
53
+ await requestUtils.createPost({
54
+ title: 'E2E Test Post',
55
+ content: '<!-- wp:paragraph --><p>Test content</p><!-- /wp:paragraph -->',
56
+ status: 'publish',
57
+ });
58
+
59
+ await requestUtils.createPage({
60
+ title: 'Test Page',
61
+ status: 'publish',
62
+ });
63
+ });
64
+ ```
65
+
66
+ ## WP-CLI bulk data generation
67
+
68
+ ```bash
69
+ # Generate posts
70
+ npx wp-env run cli wp post generate --count=50 --post_type=post --post_status=publish
71
+
72
+ # Generate users
73
+ npx wp-env run cli wp user generate --count=10 --role=subscriber
74
+
75
+ # Import theme unit test data (standard WP test content)
76
+ npx wp-env run cli wp import /tmp/theme-unit-test-data.xml --authors=create
77
+ ```
78
+
79
+ ## Cleanup patterns
80
+
81
+ ### PHPUnit (automatic)
82
+
83
+ Each PHPUnit test runs in a database transaction that rolls back automatically. Factory-created data is cleaned up without explicit teardown.
84
+
85
+ ### Playwright (manual cleanup)
86
+
87
+ ```ts
88
+ test.afterAll(async ({ requestUtils }) => {
89
+ await requestUtils.deleteAllPosts();
90
+ await requestUtils.deleteAllPages();
91
+ });
92
+ ```
93
+
94
+ ### Between test runs
95
+
96
+ ```bash
97
+ npx wp-env clean all # Reset both databases to initial state
98
+ ```
99
+
100
+ ## Fixtures for complex scenarios
101
+
102
+ Create a reusable fixture file:
103
+
104
+ ```php
105
+ // tests/fixtures/class-test-fixtures.php
106
+ class Test_Fixtures {
107
+ public static function create_sample_store(): array {
108
+ $category = self::factory()->term->create(['taxonomy' => 'product_cat', 'name' => 'Widgets']);
109
+ $products = self::factory()->post->create_many(5, [
110
+ 'post_type' => 'product',
111
+ 'post_status' => 'publish',
112
+ ]);
113
+ foreach ($products as $id) {
114
+ wp_set_object_terms($id, $category, 'product_cat');
115
+ }
116
+ return compact('category', 'products');
117
+ }
118
+ }
119
+ ```
120
+
121
+ ## Best practices
122
+
123
+ - Create the minimum data needed for each test
124
+ - Use factories over raw SQL or direct `wp_insert_post()` calls
125
+ - Name test data clearly to distinguish from real content
126
+ - Clean up E2E test data in `afterAll` hooks
127
+ - Avoid relying on auto-increment IDs; query by known attributes
@@ -0,0 +1,107 @@
1
+ # Visual Regression Testing
2
+
3
+ Use this file when adding screenshot-based visual regression tests to a WordPress project.
4
+
5
+ ## Playwright built-in approach (recommended)
6
+
7
+ Playwright includes `toHaveScreenshot()` for visual comparison:
8
+
9
+ ```ts
10
+ import { test, expect } from '@wordpress/e2e-test-utils-playwright';
11
+
12
+ test('homepage matches visual baseline', async ({ page }) => {
13
+ await page.goto('/');
14
+ await expect(page).toHaveScreenshot('homepage.png', {
15
+ maxDiffPixelRatio: 0.01, // Allow 1% pixel difference
16
+ });
17
+ });
18
+
19
+ test('block renders correctly in editor', async ({ admin, editor, page }) => {
20
+ await admin.visitAdminPage('post-new.php');
21
+ await editor.insertBlock({ name: 'my-plugin/my-block' });
22
+ const block = editor.canvas.locator('[data-type="my-plugin/my-block"]');
23
+ await expect(block).toHaveScreenshot('my-block-editor.png');
24
+ });
25
+ ```
26
+
27
+ ## Generating baselines
28
+
29
+ ```bash
30
+ # First run: creates baseline screenshots
31
+ npx playwright test --update-snapshots
32
+
33
+ # Subsequent runs: compare against baselines
34
+ npx playwright test
35
+ ```
36
+
37
+ Baselines are stored in `tests/e2e/__snapshots__/` by default.
38
+
39
+ ## Handling dynamic content
40
+
41
+ Mask elements that change between runs:
42
+
43
+ ```ts
44
+ await expect(page).toHaveScreenshot('dashboard.png', {
45
+ mask: [
46
+ page.locator('.current-time'),
47
+ page.locator('.random-ad'),
48
+ page.locator('#wpadminbar'), // Admin bar may show user-specific data
49
+ ],
50
+ });
51
+ ```
52
+
53
+ ## Disable animations
54
+
55
+ Add to `playwright.config.ts` to prevent animation-related flakiness:
56
+
57
+ ```ts
58
+ use: {
59
+ // ...
60
+ contextOptions: {
61
+ reducedMotion: 'reduce',
62
+ },
63
+ },
64
+ ```
65
+
66
+ ## Consistent viewport
67
+
68
+ ```ts
69
+ use: {
70
+ viewport: { width: 1280, height: 720 },
71
+ },
72
+ ```
73
+
74
+ ## CI integration
75
+
76
+ Playwright stores screenshots as test artifacts. In GitHub Actions:
77
+
78
+ ```yaml
79
+ - uses: actions/upload-artifact@v4
80
+ if: failure()
81
+ with:
82
+ name: visual-regression-diffs
83
+ path: tests/e2e/artifacts/
84
+ retention-days: 7
85
+ ```
86
+
87
+ ## Updating baselines after intentional changes
88
+
89
+ ```bash
90
+ npx playwright test --update-snapshots
91
+ git add tests/e2e/__snapshots__/
92
+ git commit -m "Update visual regression baselines"
93
+ ```
94
+
95
+ ## Threshold tuning
96
+
97
+ - `maxDiffPixelRatio: 0.01` — 1% tolerance (good default)
98
+ - `maxDiffPixels: 100` — absolute pixel count tolerance
99
+ - `threshold: 0.2` — per-pixel color sensitivity (0-1, lower = stricter)
100
+
101
+ Use higher thresholds for pages with web fonts (rendering varies across OS).
102
+
103
+ ## Common issues
104
+
105
+ - **False positives on CI**: OS font rendering differs; consider running in Docker or using consistent font stacks
106
+ - **Flaky screenshots**: add `await page.waitForLoadState('networkidle')` before screenshots; mask dynamic elements
107
+ - **Large snapshot files**: use PNG compression; store in Git LFS for large projects
@@ -0,0 +1,97 @@
1
+ # wp-env Test Environment Setup
2
+
3
+ Use this file when setting up or configuring wp-env as a test environment for WordPress development.
4
+
5
+ ## Minimal `.wp-env.json` for testing
6
+
7
+ ```json
8
+ {
9
+ "core": null,
10
+ "phpVersion": "8.2",
11
+ "plugins": ["./"],
12
+ "config": {
13
+ "WP_DEBUG": true,
14
+ "SCRIPT_DEBUG": true
15
+ },
16
+ "port": 8888,
17
+ "testsPort": 8889
18
+ }
19
+ ```
20
+
21
+ For a theme project, replace `"plugins": ["./"]` with `"themes": ["./"]`.
22
+
23
+ ## Starting and managing
24
+
25
+ ```bash
26
+ npx wp-env start # Start both dev and test environments
27
+ npx wp-env start --update # Start and pull latest images
28
+ npx wp-env stop # Stop containers (preserves data)
29
+ npx wp-env destroy # Remove containers and data
30
+ npx wp-env clean all # Reset databases only
31
+ ```
32
+
33
+ ## Running commands inside wp-env
34
+
35
+ ```bash
36
+ # WP-CLI in development environment
37
+ npx wp-env run cli wp plugin list
38
+
39
+ # WP-CLI in tests environment
40
+ npx wp-env run tests-cli wp option get siteurl
41
+
42
+ # PHPUnit in tests container
43
+ npx wp-env run tests-cli --env-cwd=wp-content/plugins/my-plugin phpunit
44
+
45
+ # Arbitrary bash
46
+ npx wp-env run cli bash -c "cat wp-config.php | grep WP_DEBUG"
47
+ ```
48
+
49
+ ## Default credentials
50
+
51
+ - **Development**: `http://localhost:8888` — admin / password
52
+ - **Tests**: `http://localhost:8889` — admin / password
53
+
54
+ ## Custom PHP and WP versions
55
+
56
+ ```json
57
+ {
58
+ "core": "WordPress/WordPress#6.8",
59
+ "phpVersion": "8.1"
60
+ }
61
+ ```
62
+
63
+ This is useful for testing compatibility matrices. Each CI job can override these values.
64
+
65
+ ## Mounting additional plugins/themes
66
+
67
+ ```json
68
+ {
69
+ "plugins": [
70
+ "./",
71
+ "https://downloads.wordpress.org/plugin/gutenberg.latest-stable.zip"
72
+ ],
73
+ "mappings": {
74
+ "wp-content/mu-plugins": "./test-utils/mu-plugins"
75
+ }
76
+ }
77
+ ```
78
+
79
+ ## Override file for local settings
80
+
81
+ Create `.wp-env.override.json` (gitignored) for developer-specific settings:
82
+
83
+ ```json
84
+ {
85
+ "port": 9999,
86
+ "config": {
87
+ "WP_DEBUG_LOG": true
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## Common issues
93
+
94
+ - **Port conflict**: change `port`/`testsPort` or stop the conflicting service
95
+ - **Docker not running**: `docker info` must succeed; start Docker Desktop or daemon
96
+ - **Stale containers**: `npx wp-env destroy && npx wp-env start` for a clean slate
97
+ - **Plugin not activated**: run `npx wp-env run cli wp plugin activate my-plugin`