claude-plugin-wordpress-manager 1.4.0 → 1.7.0

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 (81) hide show
  1. package/.claude-plugin/plugin.json +7 -3
  2. package/CHANGELOG.md +111 -0
  3. package/README.md +10 -3
  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/GUIDE.md +68 -15
  13. package/docs/guides/INDEX.md +46 -0
  14. package/docs/guides/wp-blog.md +590 -0
  15. package/docs/guides/wp-design-system.md +976 -0
  16. package/docs/guides/wp-ecommerce.md +786 -0
  17. package/docs/guides/wp-landing-page.md +762 -0
  18. package/docs/guides/wp-portfolio.md +713 -0
  19. package/docs/plans/2026-02-27-design-system-guide-design.md +30 -0
  20. package/docs/plans/2026-02-27-local-dev-tools-assessment.md +332 -0
  21. package/docs/plans/2026-02-27-local-env-design.md +179 -0
  22. package/docs/plans/2026-02-27-site-type-guides-design.md +44 -0
  23. package/package.json +7 -3
  24. package/skills/wordpress-router/SKILL.md +25 -5
  25. package/skills/wordpress-router/references/decision-tree.md +59 -3
  26. package/skills/wp-accessibility/SKILL.md +170 -0
  27. package/skills/wp-accessibility/references/a11y-audit-tools.md +248 -0
  28. package/skills/wp-accessibility/references/a11y-testing.md +222 -0
  29. package/skills/wp-accessibility/references/block-a11y.md +247 -0
  30. package/skills/wp-accessibility/references/interactive-a11y.md +272 -0
  31. package/skills/wp-accessibility/references/media-a11y.md +254 -0
  32. package/skills/wp-accessibility/references/theme-a11y.md +309 -0
  33. package/skills/wp-audit/SKILL.md +4 -0
  34. package/skills/wp-block-development/SKILL.md +5 -0
  35. package/skills/wp-block-themes/SKILL.md +4 -0
  36. package/skills/wp-deploy/SKILL.md +12 -0
  37. package/skills/wp-e2e-testing/SKILL.md +186 -0
  38. package/skills/wp-e2e-testing/references/ci-integration.md +174 -0
  39. package/skills/wp-e2e-testing/references/jest-wordpress.md +114 -0
  40. package/skills/wp-e2e-testing/references/phpunit-wordpress.md +141 -0
  41. package/skills/wp-e2e-testing/references/playwright-wordpress.md +108 -0
  42. package/skills/wp-e2e-testing/references/test-data-generation.md +127 -0
  43. package/skills/wp-e2e-testing/references/visual-regression.md +107 -0
  44. package/skills/wp-e2e-testing/references/wp-env-setup.md +97 -0
  45. package/skills/wp-e2e-testing/scripts/test_inspect.mjs +375 -0
  46. package/skills/wp-headless/SKILL.md +168 -0
  47. package/skills/wp-headless/references/api-layer-choice.md +160 -0
  48. package/skills/wp-headless/references/cors-config.md +245 -0
  49. package/skills/wp-headless/references/frontend-integration.md +331 -0
  50. package/skills/wp-headless/references/headless-auth.md +286 -0
  51. package/skills/wp-headless/references/webhooks.md +277 -0
  52. package/skills/wp-headless/references/wpgraphql.md +331 -0
  53. package/skills/wp-headless/scripts/headless_inspect.mjs +321 -0
  54. package/skills/wp-i18n/SKILL.md +170 -0
  55. package/skills/wp-i18n/references/js-i18n.md +201 -0
  56. package/skills/wp-i18n/references/multilingual-setup.md +219 -0
  57. package/skills/wp-i18n/references/php-i18n.md +196 -0
  58. package/skills/wp-i18n/references/rtl-support.md +206 -0
  59. package/skills/wp-i18n/references/translation-workflow.md +178 -0
  60. package/skills/wp-i18n/references/wpcli-i18n.md +177 -0
  61. package/skills/wp-i18n/scripts/i18n_inspect.mjs +330 -0
  62. package/skills/wp-interactivity-api/SKILL.md +4 -0
  63. package/skills/wp-local-env/SKILL.md +233 -0
  64. package/skills/wp-local-env/references/localwp-adapter.md +156 -0
  65. package/skills/wp-local-env/references/mcp-adapter-setup.md +153 -0
  66. package/skills/wp-local-env/references/studio-adapter.md +127 -0
  67. package/skills/wp-local-env/references/wpenv-adapter.md +121 -0
  68. package/skills/wp-local-env/scripts/detect_local_env.mjs +404 -0
  69. package/skills/wp-playground/SKILL.md +13 -1
  70. package/skills/wp-plugin-development/SKILL.md +6 -0
  71. package/skills/wp-rest-api/SKILL.md +4 -0
  72. package/skills/wp-security/SKILL.md +179 -0
  73. package/skills/wp-security/references/api-restriction.md +147 -0
  74. package/skills/wp-security/references/authentication-hardening.md +105 -0
  75. package/skills/wp-security/references/filesystem-hardening.md +105 -0
  76. package/skills/wp-security/references/http-headers.md +105 -0
  77. package/skills/wp-security/references/incident-response.md +144 -0
  78. package/skills/wp-security/references/user-capabilities.md +115 -0
  79. package/skills/wp-security/references/wp-config-security.md +129 -0
  80. package/skills/wp-security/scripts/security_inspect.mjs +393 -0
  81. package/skills/wp-wpcli-and-ops/SKILL.md +6 -0
@@ -0,0 +1,174 @@
1
+ # CI Pipeline Integration for WordPress Tests
2
+
3
+ Use this file when setting up GitHub Actions (or similar CI) for WordPress test automation.
4
+
5
+ ## GitHub Actions workflow
6
+
7
+ ```yaml
8
+ # .github/workflows/tests.yml
9
+ name: Tests
10
+
11
+ on:
12
+ push:
13
+ branches: [main]
14
+ pull_request:
15
+ branches: [main]
16
+
17
+ jobs:
18
+ php-tests:
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ php: ['8.1', '8.2', '8.3']
23
+ wp: ['6.8', '6.9']
24
+ services:
25
+ mysql:
26
+ image: mysql:8.0
27
+ env:
28
+ MYSQL_ROOT_PASSWORD: root
29
+ MYSQL_DATABASE: wordpress_test
30
+ ports: ['3306:3306']
31
+ options: >-
32
+ --health-cmd="mysqladmin ping"
33
+ --health-interval=10s
34
+ --health-timeout=5s
35
+ --health-retries=5
36
+
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+
40
+ - name: Setup PHP
41
+ uses: shivammathur/setup-php@v2
42
+ with:
43
+ php-version: ${{ matrix.php }}
44
+ extensions: mysqli
45
+ coverage: xdebug
46
+
47
+ - name: Cache Composer
48
+ uses: actions/cache@v4
49
+ with:
50
+ path: vendor
51
+ key: composer-${{ hashFiles('composer.lock') }}
52
+
53
+ - name: Install dependencies
54
+ run: composer install --no-interaction
55
+
56
+ - name: Install WP test suite
57
+ run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 ${{ matrix.wp }}
58
+
59
+ - name: Run PHPUnit
60
+ run: vendor/bin/phpunit
61
+
62
+ js-tests:
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+
67
+ - name: Setup Node.js
68
+ uses: actions/setup-node@v4
69
+ with:
70
+ node-version: 20
71
+ cache: npm
72
+
73
+ - name: Install dependencies
74
+ run: npm ci
75
+
76
+ - name: Run Jest
77
+ run: npx wp-scripts test-unit-js --ci --coverage
78
+
79
+ e2e-tests:
80
+ runs-on: ubuntu-latest
81
+ steps:
82
+ - uses: actions/checkout@v4
83
+
84
+ - name: Setup Node.js
85
+ uses: actions/setup-node@v4
86
+ with:
87
+ node-version: 20
88
+ cache: npm
89
+
90
+ - name: Install dependencies
91
+ run: npm ci
92
+
93
+ - name: Install Playwright browsers
94
+ run: npx playwright install chromium --with-deps
95
+
96
+ - name: Start wp-env
97
+ run: npx wp-env start
98
+
99
+ - name: Run Playwright tests
100
+ run: npx wp-scripts test-playwright
101
+
102
+ - name: Upload artifacts on failure
103
+ uses: actions/upload-artifact@v4
104
+ if: failure()
105
+ with:
106
+ name: playwright-artifacts
107
+ path: artifacts/
108
+ retention-days: 7
109
+ ```
110
+
111
+ ## Caching strategies
112
+
113
+ ```yaml
114
+ # Node modules
115
+ - uses: actions/cache@v4
116
+ with:
117
+ path: ~/.npm
118
+ key: npm-${{ hashFiles('package-lock.json') }}
119
+
120
+ # Playwright browsers
121
+ - uses: actions/cache@v4
122
+ with:
123
+ path: ~/.cache/ms-playwright
124
+ key: playwright-${{ hashFiles('package-lock.json') }}
125
+
126
+ # Docker images for wp-env
127
+ - uses: ScribeMD/docker-cache@0.5.0
128
+ with:
129
+ key: docker-${{ hashFiles('.wp-env.json') }}
130
+ ```
131
+
132
+ ## Parallel test execution
133
+
134
+ Split Playwright tests across multiple workers:
135
+
136
+ ```yaml
137
+ e2e-tests:
138
+ strategy:
139
+ matrix:
140
+ shard: [1, 2, 3]
141
+ steps:
142
+ # ...
143
+ - run: npx playwright test --shard=${{ matrix.shard }}/3
144
+ ```
145
+
146
+ ## PHPUnit without wp-env (standalone)
147
+
148
+ For projects that don't use wp-env, the `install-wp-tests.sh` script sets up the WP test suite:
149
+
150
+ ```bash
151
+ bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 latest true
152
+ ```
153
+
154
+ Arguments: `db_name db_user db_pass db_host wp_version skip_db_create`
155
+
156
+ ## Conditional jobs
157
+
158
+ Run expensive E2E tests only when relevant files change:
159
+
160
+ ```yaml
161
+ e2e-tests:
162
+ if: |
163
+ contains(github.event.pull_request.labels.*.name, 'e2e') ||
164
+ github.ref == 'refs/heads/main'
165
+ ```
166
+
167
+ ## Best practices
168
+
169
+ - Run PHPUnit with matrix strategy for PHP/WP version coverage
170
+ - Cache aggressively (npm, Composer, Playwright browsers, Docker)
171
+ - Upload test artifacts (traces, screenshots) on failure for debugging
172
+ - Use `--ci` flag for Jest to disable interactive mode
173
+ - Set reasonable timeouts to catch hanging tests early
174
+ - Run lint/format checks as a separate job (fast fail)
@@ -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