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.
- package/.claude-plugin/plugin.json +7 -3
- package/CHANGELOG.md +111 -0
- package/README.md +10 -3
- package/agents/wp-accessibility-auditor.md +206 -0
- package/agents/wp-content-strategist.md +18 -0
- package/agents/wp-deployment-engineer.md +34 -2
- package/agents/wp-performance-optimizer.md +12 -0
- package/agents/wp-security-auditor.md +20 -0
- package/agents/wp-security-hardener.md +266 -0
- package/agents/wp-site-manager.md +14 -0
- package/agents/wp-test-engineer.md +207 -0
- package/docs/GUIDE.md +68 -15
- package/docs/guides/INDEX.md +46 -0
- package/docs/guides/wp-blog.md +590 -0
- package/docs/guides/wp-design-system.md +976 -0
- package/docs/guides/wp-ecommerce.md +786 -0
- package/docs/guides/wp-landing-page.md +762 -0
- package/docs/guides/wp-portfolio.md +713 -0
- package/docs/plans/2026-02-27-design-system-guide-design.md +30 -0
- package/docs/plans/2026-02-27-local-dev-tools-assessment.md +332 -0
- package/docs/plans/2026-02-27-local-env-design.md +179 -0
- package/docs/plans/2026-02-27-site-type-guides-design.md +44 -0
- package/package.json +7 -3
- package/skills/wordpress-router/SKILL.md +25 -5
- package/skills/wordpress-router/references/decision-tree.md +59 -3
- package/skills/wp-accessibility/SKILL.md +170 -0
- package/skills/wp-accessibility/references/a11y-audit-tools.md +248 -0
- package/skills/wp-accessibility/references/a11y-testing.md +222 -0
- package/skills/wp-accessibility/references/block-a11y.md +247 -0
- package/skills/wp-accessibility/references/interactive-a11y.md +272 -0
- package/skills/wp-accessibility/references/media-a11y.md +254 -0
- package/skills/wp-accessibility/references/theme-a11y.md +309 -0
- package/skills/wp-audit/SKILL.md +4 -0
- package/skills/wp-block-development/SKILL.md +5 -0
- package/skills/wp-block-themes/SKILL.md +4 -0
- package/skills/wp-deploy/SKILL.md +12 -0
- package/skills/wp-e2e-testing/SKILL.md +186 -0
- package/skills/wp-e2e-testing/references/ci-integration.md +174 -0
- package/skills/wp-e2e-testing/references/jest-wordpress.md +114 -0
- package/skills/wp-e2e-testing/references/phpunit-wordpress.md +141 -0
- package/skills/wp-e2e-testing/references/playwright-wordpress.md +108 -0
- package/skills/wp-e2e-testing/references/test-data-generation.md +127 -0
- package/skills/wp-e2e-testing/references/visual-regression.md +107 -0
- package/skills/wp-e2e-testing/references/wp-env-setup.md +97 -0
- package/skills/wp-e2e-testing/scripts/test_inspect.mjs +375 -0
- package/skills/wp-headless/SKILL.md +168 -0
- package/skills/wp-headless/references/api-layer-choice.md +160 -0
- package/skills/wp-headless/references/cors-config.md +245 -0
- package/skills/wp-headless/references/frontend-integration.md +331 -0
- package/skills/wp-headless/references/headless-auth.md +286 -0
- package/skills/wp-headless/references/webhooks.md +277 -0
- package/skills/wp-headless/references/wpgraphql.md +331 -0
- package/skills/wp-headless/scripts/headless_inspect.mjs +321 -0
- package/skills/wp-i18n/SKILL.md +170 -0
- package/skills/wp-i18n/references/js-i18n.md +201 -0
- package/skills/wp-i18n/references/multilingual-setup.md +219 -0
- package/skills/wp-i18n/references/php-i18n.md +196 -0
- package/skills/wp-i18n/references/rtl-support.md +206 -0
- package/skills/wp-i18n/references/translation-workflow.md +178 -0
- package/skills/wp-i18n/references/wpcli-i18n.md +177 -0
- package/skills/wp-i18n/scripts/i18n_inspect.mjs +330 -0
- package/skills/wp-interactivity-api/SKILL.md +4 -0
- package/skills/wp-local-env/SKILL.md +233 -0
- package/skills/wp-local-env/references/localwp-adapter.md +156 -0
- package/skills/wp-local-env/references/mcp-adapter-setup.md +153 -0
- package/skills/wp-local-env/references/studio-adapter.md +127 -0
- package/skills/wp-local-env/references/wpenv-adapter.md +121 -0
- package/skills/wp-local-env/scripts/detect_local_env.mjs +404 -0
- package/skills/wp-playground/SKILL.md +13 -1
- package/skills/wp-plugin-development/SKILL.md +6 -0
- package/skills/wp-rest-api/SKILL.md +4 -0
- package/skills/wp-security/SKILL.md +179 -0
- package/skills/wp-security/references/api-restriction.md +147 -0
- package/skills/wp-security/references/authentication-hardening.md +105 -0
- package/skills/wp-security/references/filesystem-hardening.md +105 -0
- package/skills/wp-security/references/http-headers.md +105 -0
- package/skills/wp-security/references/incident-response.md +144 -0
- package/skills/wp-security/references/user-capabilities.md +115 -0
- package/skills/wp-security/references/wp-config-security.md +129 -0
- package/skills/wp-security/scripts/security_inspect.mjs +393 -0
- package/skills/wp-wpcli-and-ops/SKILL.md +6 -0
|
@@ -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`
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* test_inspect.mjs — Detect testing frameworks and configuration in a WordPress project.
|
|
3
|
+
*
|
|
4
|
+
* Scans for Playwright, Jest, PHPUnit, wp-env, and CI config.
|
|
5
|
+
* Outputs a JSON report to stdout with detected frameworks,
|
|
6
|
+
* test directories, configuration files, and CI integration.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node test_inspect.mjs [--cwd=/path/to/check]
|
|
10
|
+
*
|
|
11
|
+
* Exit codes:
|
|
12
|
+
* 0 — at least one test framework detected
|
|
13
|
+
* 1 — no test frameworks detected
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import process from "node:process";
|
|
19
|
+
import { execSync } from "node:child_process";
|
|
20
|
+
|
|
21
|
+
const TOOL_VERSION = "1.0.0";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
function statSafe(p) {
|
|
28
|
+
try {
|
|
29
|
+
return fs.statSync(p);
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readFileSafe(p) {
|
|
36
|
+
try {
|
|
37
|
+
return fs.readFileSync(p, "utf8");
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readJsonSafe(p) {
|
|
44
|
+
const raw = readFileSafe(p);
|
|
45
|
+
if (!raw) return null;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(raw);
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function execSafe(cmd, cwd, timeoutMs = 5000) {
|
|
54
|
+
try {
|
|
55
|
+
return execSync(cmd, { encoding: "utf8", timeout: timeoutMs, cwd, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function globDirs(base, patterns) {
|
|
62
|
+
const found = [];
|
|
63
|
+
for (const pattern of patterns) {
|
|
64
|
+
const full = path.join(base, pattern);
|
|
65
|
+
if (statSafe(full)?.isDirectory()) {
|
|
66
|
+
found.push(pattern);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return found;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function globFiles(base, patterns) {
|
|
73
|
+
const found = [];
|
|
74
|
+
for (const pattern of patterns) {
|
|
75
|
+
const full = path.join(base, pattern);
|
|
76
|
+
if (statSafe(full)?.isFile()) {
|
|
77
|
+
found.push(pattern);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return found;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Parse --cwd argument
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
function parseCwd() {
|
|
88
|
+
const cwdArg = process.argv.find((a) => a.startsWith("--cwd="));
|
|
89
|
+
return cwdArg ? cwdArg.slice(6) : process.cwd();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Detect Playwright
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
function detectPlaywright(cwd) {
|
|
97
|
+
const result = { detected: false, configFile: null, testDirs: [], wpE2eUtils: false };
|
|
98
|
+
|
|
99
|
+
const configFiles = [
|
|
100
|
+
"playwright.config.js",
|
|
101
|
+
"playwright.config.ts",
|
|
102
|
+
"playwright.config.mjs",
|
|
103
|
+
];
|
|
104
|
+
const found = globFiles(cwd, configFiles);
|
|
105
|
+
if (found.length > 0) {
|
|
106
|
+
result.detected = true;
|
|
107
|
+
result.configFile = found[0];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const testDirs = globDirs(cwd, [
|
|
111
|
+
"tests/e2e",
|
|
112
|
+
"tests/playwright",
|
|
113
|
+
"e2e",
|
|
114
|
+
"test/e2e",
|
|
115
|
+
"specs",
|
|
116
|
+
]);
|
|
117
|
+
result.testDirs = testDirs;
|
|
118
|
+
|
|
119
|
+
// Check for @wordpress/e2e-test-utils-playwright
|
|
120
|
+
const pkg = readJsonSafe(path.join(cwd, "package.json"));
|
|
121
|
+
if (pkg) {
|
|
122
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
123
|
+
if (allDeps["@wordpress/e2e-test-utils-playwright"]) {
|
|
124
|
+
result.wpE2eUtils = true;
|
|
125
|
+
result.detected = true;
|
|
126
|
+
}
|
|
127
|
+
if (allDeps["@playwright/test"] || allDeps["playwright"]) {
|
|
128
|
+
result.detected = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Detect Jest
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
function detectJest(cwd) {
|
|
140
|
+
const result = { detected: false, configFile: null, testDirs: [], wpScripts: false };
|
|
141
|
+
|
|
142
|
+
const configFiles = [
|
|
143
|
+
"jest.config.js",
|
|
144
|
+
"jest.config.ts",
|
|
145
|
+
"jest.config.mjs",
|
|
146
|
+
"jest.config.json",
|
|
147
|
+
];
|
|
148
|
+
const found = globFiles(cwd, configFiles);
|
|
149
|
+
if (found.length > 0) {
|
|
150
|
+
result.detected = true;
|
|
151
|
+
result.configFile = found[0];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check package.json for jest config
|
|
155
|
+
const pkg = readJsonSafe(path.join(cwd, "package.json"));
|
|
156
|
+
if (pkg) {
|
|
157
|
+
if (pkg.jest) {
|
|
158
|
+
result.detected = true;
|
|
159
|
+
result.configFile = result.configFile || "package.json (jest key)";
|
|
160
|
+
}
|
|
161
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
162
|
+
if (allDeps["@wordpress/scripts"]) {
|
|
163
|
+
result.wpScripts = true;
|
|
164
|
+
result.detected = true;
|
|
165
|
+
}
|
|
166
|
+
if (allDeps["jest"]) {
|
|
167
|
+
result.detected = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const testDirs = globDirs(cwd, [
|
|
172
|
+
"tests/js",
|
|
173
|
+
"tests/unit",
|
|
174
|
+
"tests/jest",
|
|
175
|
+
"src/__tests__",
|
|
176
|
+
"__tests__",
|
|
177
|
+
"test/js",
|
|
178
|
+
]);
|
|
179
|
+
result.testDirs = testDirs;
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// Detect PHPUnit
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
function detectPHPUnit(cwd) {
|
|
189
|
+
const result = { detected: false, configFile: null, testDirs: [], bootstrap: null };
|
|
190
|
+
|
|
191
|
+
const configFiles = [
|
|
192
|
+
"phpunit.xml",
|
|
193
|
+
"phpunit.xml.dist",
|
|
194
|
+
"phpunit.dist.xml",
|
|
195
|
+
];
|
|
196
|
+
const found = globFiles(cwd, configFiles);
|
|
197
|
+
if (found.length > 0) {
|
|
198
|
+
result.detected = true;
|
|
199
|
+
result.configFile = found[0];
|
|
200
|
+
|
|
201
|
+
// Parse bootstrap path from XML
|
|
202
|
+
const content = readFileSafe(path.join(cwd, found[0]));
|
|
203
|
+
if (content) {
|
|
204
|
+
const match = content.match(/bootstrap="([^"]+)"/);
|
|
205
|
+
if (match) result.bootstrap = match[1];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const testDirs = globDirs(cwd, [
|
|
210
|
+
"tests/phpunit",
|
|
211
|
+
"tests/php",
|
|
212
|
+
"tests/unit",
|
|
213
|
+
"tests",
|
|
214
|
+
]);
|
|
215
|
+
result.testDirs = testDirs;
|
|
216
|
+
|
|
217
|
+
// Check composer.json
|
|
218
|
+
const composer = readJsonSafe(path.join(cwd, "composer.json"));
|
|
219
|
+
if (composer) {
|
|
220
|
+
const allDeps = { ...composer.require, ...composer["require-dev"] };
|
|
221
|
+
if (allDeps["phpunit/phpunit"] || allDeps["yoast/phpunit-polyfills"]) {
|
|
222
|
+
result.detected = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Detect wp-env
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
function detectWpEnv(cwd) {
|
|
234
|
+
const result = { detected: false, configFile: null, running: false };
|
|
235
|
+
|
|
236
|
+
if (statSafe(path.join(cwd, ".wp-env.json"))?.isFile()) {
|
|
237
|
+
result.detected = true;
|
|
238
|
+
result.configFile = ".wp-env.json";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (statSafe(path.join(cwd, ".wp-env.override.json"))?.isFile()) {
|
|
242
|
+
result.detected = true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const pkg = readJsonSafe(path.join(cwd, "package.json"));
|
|
246
|
+
if (pkg) {
|
|
247
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
248
|
+
if (allDeps["@wordpress/env"]) {
|
|
249
|
+
result.detected = true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check if wp-env is running (Docker containers)
|
|
254
|
+
const dockerCheck = execSafe("docker ps --filter name=wp-env --format '{{.Names}}'", cwd);
|
|
255
|
+
if (dockerCheck && dockerCheck.length > 0) {
|
|
256
|
+
result.running = true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
// Detect CI configuration
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
function detectCI(cwd) {
|
|
267
|
+
const result = { detected: false, provider: null, hasTestStep: false };
|
|
268
|
+
|
|
269
|
+
// GitHub Actions
|
|
270
|
+
const ghDirs = globDirs(cwd, [".github/workflows"]);
|
|
271
|
+
if (ghDirs.length > 0) {
|
|
272
|
+
const workflowDir = path.join(cwd, ".github", "workflows");
|
|
273
|
+
try {
|
|
274
|
+
const files = fs.readdirSync(workflowDir);
|
|
275
|
+
for (const file of files) {
|
|
276
|
+
if (file.endsWith(".yml") || file.endsWith(".yaml")) {
|
|
277
|
+
result.detected = true;
|
|
278
|
+
result.provider = "github-actions";
|
|
279
|
+
const content = readFileSafe(path.join(workflowDir, file));
|
|
280
|
+
if (content && /phpunit|jest|playwright|wp-env|npm test|npm run test/i.test(content)) {
|
|
281
|
+
result.hasTestStep = true;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} catch { /* ignore */ }
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// GitLab CI
|
|
289
|
+
if (statSafe(path.join(cwd, ".gitlab-ci.yml"))?.isFile()) {
|
|
290
|
+
result.detected = true;
|
|
291
|
+
result.provider = result.provider || "gitlab-ci";
|
|
292
|
+
const content = readFileSafe(path.join(cwd, ".gitlab-ci.yml"));
|
|
293
|
+
if (content && /phpunit|jest|playwright|wp-env/i.test(content)) {
|
|
294
|
+
result.hasTestStep = true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// Detect npm test scripts
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
function detectScripts(cwd) {
|
|
306
|
+
const pkg = readJsonSafe(path.join(cwd, "package.json"));
|
|
307
|
+
if (!pkg?.scripts) return {};
|
|
308
|
+
|
|
309
|
+
const testScripts = {};
|
|
310
|
+
for (const [key, value] of Object.entries(pkg.scripts)) {
|
|
311
|
+
if (/test|e2e|playwright|jest|phpunit/i.test(key)) {
|
|
312
|
+
testScripts[key] = value;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return testScripts;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
// Main
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
function main() {
|
|
323
|
+
const cwd = parseCwd();
|
|
324
|
+
|
|
325
|
+
if (!statSafe(cwd)?.isDirectory()) {
|
|
326
|
+
console.error(`Error: directory not found: ${cwd}`);
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const playwright = detectPlaywright(cwd);
|
|
331
|
+
const jest = detectJest(cwd);
|
|
332
|
+
const phpunit = detectPHPUnit(cwd);
|
|
333
|
+
const wpEnv = detectWpEnv(cwd);
|
|
334
|
+
const ci = detectCI(cwd);
|
|
335
|
+
const scripts = detectScripts(cwd);
|
|
336
|
+
|
|
337
|
+
const anyDetected = playwright.detected || jest.detected || phpunit.detected;
|
|
338
|
+
|
|
339
|
+
const report = {
|
|
340
|
+
tool: "test_inspect",
|
|
341
|
+
version: TOOL_VERSION,
|
|
342
|
+
cwd,
|
|
343
|
+
detected: anyDetected,
|
|
344
|
+
frameworks: {
|
|
345
|
+
playwright,
|
|
346
|
+
jest,
|
|
347
|
+
phpunit,
|
|
348
|
+
},
|
|
349
|
+
environment: {
|
|
350
|
+
wpEnv,
|
|
351
|
+
},
|
|
352
|
+
ci,
|
|
353
|
+
scripts,
|
|
354
|
+
recommendations: [],
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// Generate recommendations
|
|
358
|
+
if (!anyDetected) {
|
|
359
|
+
report.recommendations.push("No test frameworks detected. Consider adding Playwright for E2E and PHPUnit for unit tests.");
|
|
360
|
+
}
|
|
361
|
+
if (!wpEnv.detected && (playwright.detected || phpunit.detected)) {
|
|
362
|
+
report.recommendations.push("Consider adding .wp-env.json for a consistent WordPress test environment.");
|
|
363
|
+
}
|
|
364
|
+
if (anyDetected && !ci.hasTestStep) {
|
|
365
|
+
report.recommendations.push("Test frameworks detected but CI does not run tests. Add a test step to your CI pipeline.");
|
|
366
|
+
}
|
|
367
|
+
if (playwright.detected && !playwright.wpE2eUtils) {
|
|
368
|
+
report.recommendations.push("Consider using @wordpress/e2e-test-utils-playwright for WordPress-specific test helpers.");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
console.log(JSON.stringify(report, null, 2));
|
|
372
|
+
process.exit(anyDetected ? 0 : 1);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
main();
|