Skip to content

Commit 9c61190

Browse files
committed
Add CI job for legacy WordPress boot testing
Test that WordPress versions 1.2 through 4.9 boot on PHP 5.6 with SQLite and display "Hello world!". Starts the dev server, then runs each version through the browser via Playwright. WP 1.0.2 is excluded — the SQLite AST driver can't parse its enum() column types, causing WHERE clause failures.
1 parent ed8edf1 commit 9c61190

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

.github/workflows/ci.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,26 @@ jobs:
380380
node-version: 20
381381
- run: packages/php-wasm/cli/tests/smoke-test.sh
382382

383+
# Legacy WordPress boot test - verifies that WordPress versions
384+
# 1.2 through 4.9 boot on PHP 5.6 with SQLite and show "Hello world!"
385+
test-legacy-wp-version-boot:
386+
runs-on: ubuntu-latest
387+
steps:
388+
- uses: actions/checkout@v4
389+
with:
390+
submodules: true
391+
- uses: ./.github/actions/prepare-playground
392+
- name: Install Playwright Browser
393+
run: npx playwright install chromium --with-deps
394+
- name: Start dev server
395+
run: |
396+
npm run dev > /tmp/playground-dev.log 2>&1 &
397+
until curl -s -o /dev/null http://127.0.0.1:5400/website-server/ 2>/dev/null; do
398+
sleep 3
399+
done
400+
- name: Test legacy WordPress version boot
401+
run: node packages/playground/wordpress/tests/test-legacy-wp-version-boot.mjs
402+
383403
# Redis extension tests - verifies the php-redis extension loads
384404
# and provides the expected API, and can connect to a real Redis server.
385405
# Redis requires JSPI because asyncify cannot properly handle exceptions

packages/playground/wordpress/project.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@
7171
"reportsDirectory": "../../../coverage/packages/playground/wordpress"
7272
}
7373
},
74+
"test-legacy-wp-version-boot": {
75+
"executor": "nx:run-commands",
76+
"options": {
77+
"commands": [
78+
"node packages/playground/wordpress/tests/test-legacy-wp-version-boot.mjs"
79+
]
80+
}
81+
},
7482
"lint": {
7583
"executor": "@nx/eslint:lint",
7684
"outputs": ["{options.outputFile}"],
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/**
2+
* Tests that legacy WordPress versions (4.9 down to 1.2) boot
3+
* successfully on PHP 5.6 with SQLite and display "Hello world!".
4+
*
5+
* Requires the dev server to be running on port 5400
6+
* (started by the CI job or manually via `npm run dev`).
7+
*
8+
* Usage: node packages/playground/wordpress/test-legacy-wp-version-boot.mjs
9+
*/
10+
import { chromium } from 'playwright';
11+
12+
// All WordPress minor versions from 4.9 down to 1.2.
13+
// Note: there was no WordPress 2.4 release.
14+
// For WP 1.x, wordpress.org requires full version numbers (e.g. 1.5.2).
15+
const WP_VERSIONS = [
16+
'4.9',
17+
'4.8',
18+
'4.7',
19+
'4.6',
20+
'4.5',
21+
'4.4',
22+
'4.3',
23+
'4.2',
24+
'4.1',
25+
'4.0',
26+
'3.9',
27+
'3.8',
28+
'3.7',
29+
'3.6',
30+
'3.5',
31+
'3.4',
32+
'3.3',
33+
'3.2',
34+
'3.1',
35+
'3.0',
36+
'2.9',
37+
'2.8',
38+
'2.7',
39+
'2.6',
40+
'2.5',
41+
'2.3',
42+
'2.2',
43+
'2.1',
44+
'2.0',
45+
'1.5.2',
46+
'1.2.2',
47+
// WP 1.0.2 is excluded: the SQLite AST driver can't parse its
48+
// enum() column types, causing WHERE clause failures. Fixing this
49+
// requires changes to the AST driver's type metadata system.
50+
// '1.0.2',
51+
];
52+
53+
const PORT = 5400;
54+
const TIMEOUT_S = 120;
55+
const results = [];
56+
57+
const browser = await chromium.launch({ headless: true });
58+
59+
for (const wp of WP_VERSIONS) {
60+
const label = `WP ${wp}`;
61+
process.stdout.write(`${label}... `);
62+
63+
const wpUrl = `http://127.0.0.1:${PORT}/cors-proxy/?https://wordpress.org/wordpress-${wp}.zip`;
64+
const encodedWp = encodeURIComponent(wpUrl);
65+
const url = `http://127.0.0.1:${PORT}/website-server/?php=5.6&wp=${encodedWp}`;
66+
67+
const page = await browser.newPage();
68+
const consoleErrors = [];
69+
page.on('console', (msg) => {
70+
if (msg.type() === 'error')
71+
consoleErrors.push(msg.text().slice(0, 300));
72+
});
73+
74+
try {
75+
await page.goto(url, {
76+
timeout: 180_000,
77+
waitUntil: 'domcontentloaded',
78+
});
79+
80+
let found = false;
81+
for (let i = 0; i < TIMEOUT_S / 3; i++) {
82+
await page.waitForTimeout(3000);
83+
84+
for (const frame of page.frames()) {
85+
try {
86+
const furl = frame.url();
87+
if (!furl.includes('scope:')) continue;
88+
89+
const body = await frame
90+
.locator('body')
91+
.innerText({ timeout: 2000 });
92+
if (!body || body.length < 20) continue;
93+
94+
const hasHelloWorld =
95+
body.includes('Hello world') ||
96+
body.includes('Hello World');
97+
const hasWP =
98+
body.includes('WordPress') ||
99+
body.includes('My WordPress') ||
100+
body.includes('My Weblog');
101+
const hasNotFound =
102+
body.includes('Not Found') && !hasHelloWorld;
103+
const hasError =
104+
body.includes('Parse error') ||
105+
body.includes('Fatal error') ||
106+
body.includes('database error');
107+
108+
if (hasError) {
109+
const errorLine =
110+
body
111+
.split('\n')
112+
.find(
113+
(l) =>
114+
l.includes('Parse error') ||
115+
l.includes('Fatal error') ||
116+
l.includes('database error')
117+
) || body.slice(0, 150);
118+
console.log(`FAIL: ${errorLine.slice(0, 120)}`);
119+
results.push({
120+
wp,
121+
status: 'ERROR',
122+
detail: errorLine.slice(0, 120),
123+
});
124+
} else if (hasHelloWorld) {
125+
console.log(`OK`);
126+
results.push({ wp, status: 'OK' });
127+
} else if (hasNotFound) {
128+
console.log(`NOT FOUND (no posts)`);
129+
results.push({ wp, status: 'NOT_FOUND' });
130+
} else if (hasWP) {
131+
console.log(
132+
`PARTIAL: ${body.slice(0, 80).replace(/\n/g, ' ')}`
133+
);
134+
results.push({
135+
wp,
136+
status: 'PARTIAL',
137+
detail: body.slice(0, 80),
138+
});
139+
} else {
140+
console.log(
141+
`UNKNOWN: ${body.slice(0, 80).replace(/\n/g, ' ')}`
142+
);
143+
results.push({
144+
wp,
145+
status: 'UNKNOWN',
146+
detail: body.slice(0, 80),
147+
});
148+
}
149+
found = true;
150+
break;
151+
} catch {}
152+
}
153+
if (found) break;
154+
}
155+
156+
if (!found) {
157+
const lastError = consoleErrors[consoleErrors.length - 1] || '';
158+
console.log(`TIMEOUT: ${lastError.slice(0, 100)}`);
159+
results.push({
160+
wp,
161+
status: 'TIMEOUT',
162+
detail: lastError.slice(0, 100),
163+
});
164+
}
165+
} catch (e) {
166+
console.log(`CRASH: ${e.message.slice(0, 100)}`);
167+
results.push({ wp, status: 'CRASH', detail: e.message.slice(0, 100) });
168+
}
169+
170+
await page.close();
171+
}
172+
173+
await browser.close();
174+
175+
console.log(`\n${'='.repeat(60)}`);
176+
console.log('RESULTS SUMMARY:');
177+
console.log(`${'='.repeat(60)}`);
178+
for (const r of results) {
179+
const icon = r.status === 'OK' ? 'PASS' : 'FAIL';
180+
console.log(
181+
` ${icon} WP ${r.wp.padEnd(6)} ${r.status}${r.detail ? ` — ${r.detail}` : ''}`
182+
);
183+
}
184+
const ok = results.filter((r) => r.status === 'OK').length;
185+
const failed = results.filter((r) => r.status !== 'OK');
186+
console.log(`\n${ok}/${results.length} versions boot with Hello world!`);
187+
188+
if (failed.length > 0) {
189+
console.error(`\n${failed.length} version(s) failed.`);
190+
process.exit(1);
191+
}

0 commit comments

Comments
 (0)