E2E Test Infrastructure Fix
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Get the E2E homepage test passing - server returns 200 at http://localhost:3000/en with no console errors.
Architecture: Debug why next-intl v4.11.0 can't find i18n.ts despite it existing at project root. Likely Turbopack alias resolution issue. Fix by either: (1) correcting the next-intl plugin path resolution, or (2) falling back to webpack mode.
Tech Stack: Playwright, next-intl v4.11.0, Next.js 16.2.4, Turbopack
Pre-flight: Reproduce the Issue
- Step 1: Kill all Next.js processes
pkill -f "next" 2>/dev/null; sleep 2; echo "cleared"- Step 2: Start production server
cd /Users/curlyz/usr/hol/apps/frontend
PORT=3000 pnpm run start > /tmp/next-prod.log 2>&1 &
sleep 5- Step 3: Confirm 500 error
curl -sL -o /dev/null -w "%{http_code}" http://localhost:3000/en
# Expected: 500Task 1: Diagnose exact next-intl resolution failure
Files:
-
Inspect:
node_modules/.pnpm/next-intl@4.11.0_*/node_modules/next-intl/dist/esm/development/plugin/getNextConfig.js(lines 19-54) -
Inspect:
apps/frontend/next.config.ts -
Inspect:
apps/frontend/i18n.ts -
Step 1: Add debug logging to getNextConfig.js
The resolveI18nPath function (line 19) throws "Could not find i18n config" when pathExists(providedPath) returns false. Add console.log before line 33:
console.log(
"[DEBUG] resolveI18nPath called, providedPath:",
providedPath,
"cwd:",
cwd,
);
for (const candidate of [
...withExtensions("./i18n/request"),
...withExtensions("./src/i18n/request"),
]) {
console.log(
"[DEBUG] checking candidate:",
candidate,
"exists:",
pathExists(candidate),
);
}- Step 2: Run dev server with debug output
cd /Users/curlyz/usr/hol/apps/frontend
rm -rf .next
pnpm run dev 2>&1 | grep -E "DEBUG|Error" | head -20
# Observe what candidates are being checked- Step 3: Commit diagnostic change
git add apps/frontend/next.config.ts
git commit -m "chore: debug next-intl config resolution"Task 2: Use next-intl's default i18n folder structure
Files:
- Create:
apps/frontend/i18n/request.ts(move content fromi18n.ts) - Modify:
apps/frontend/next.config.ts - Delete:
apps/frontend/i18n.ts(after moving content)
Root Cause: next-intl v4 defaults to looking for ./i18n/request.{ts,tsx,js,jsx} not ./i18n.ts. The resolveI18nPath function checks for these extensions by default (line 38-41 of getNextConfig.js).
- Step 1: Create i18n folder with request.ts
mkdir -p apps/frontend/i18nCreate apps/frontend/i18n/request.ts:
// ipsoc checked: 2026-05-05
// SoC: next-intl server configuration - locale resolution and message loading
import { getRequestConfig } from "next-intl/server";
import { routing } from "../routing";
export default getRequestConfig(async ({ requestLocale }) => {
let locale = await requestLocale;
const isValidLocale = (l: unknown): l is (typeof routing.locales)[number] => {
return typeof l === "string" && (l === "en" || l === "vi");
};
if (!locale || !isValidLocale(locale)) {
locale = routing.defaultLocale;
}
return {
locale,
messages: (await import(`../messages/${locale}.json`)).default,
};
});- Step 2: Simplify next.config.ts to use default path
// ipsoc checked: 2026-04-27
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
// No argument = uses ./i18n/request.ts by default
const withNextIntl = createNextIntlPlugin();
const _nextConfig: NextConfig = {
images: {
unoptimized: true,
},
pageExtensions: ["ts", "tsx"],
};
function defineNextConfig(config: NextConfig): NextConfig {
return config;
}
export const nextConfig = withNextIntl(defineNextConfig(_nextConfig));- Step 3: Delete old i18n.ts
rm apps/frontend/i18n.ts- Step 4: Test with production build
pkill -f "next" 2>/dev/null; sleep 1
rm -rf apps/frontend/.next
cd apps/frontend && pnpm run build 2>&1 | tail -10
# Should compile without error- Step 5: Start and test
PORT=3000 pnpm run start > /tmp/next-prod.log 2>&1 &
sleep 5
curl -sL -o /dev/null -w "%{http_code}" http://localhost:3000/en
# Expected: 200- Step 6: Commit
git add apps/frontend/i18n/request.ts apps/frontend/next.config.ts
git rm apps/frontend/i18n.ts
git commit -m "fix: use next-intl default i18n/request.ts path"// ipsoc checked: 2026-04-27
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin({
requestConfig: "./i18n.ts",
});
const _nextConfig: NextConfig = {
images: {
unoptimized: true,
},
pageExtensions: ["ts", "tsx"],
};
function defineNextConfig(config: NextConfig): NextConfig {
return config;
}
export const nextConfig = withNextIntl(defineNextConfig(_nextConfig));- Step 2: Test with production build
pkill -f "next" 2>/dev/null; sleep 1
rm -rf apps/frontend/.next
cd apps/frontend && pnpm run build 2>&1 | tail -10
# Should compile without error- Step 3: Start and test
PORT=3000 pnpm run start > /tmp/next-prod.log 2>&1 &
sleep 5
curl -sL -o /dev/null -w "%{http_code}" http://localhost:3000/en
# If 200: fix works
# If 500: try Task 3- Step 4: Commit
git add apps/frontend/next.config.ts
git commit -m "fix: explicit requestConfig path in next-intl plugin"Task 3: Fall back to webpack (disable Turbopack)
Files:
- Modify:
apps/frontend/next.config.ts - Modify:
apps/frontend/package.jsonscripts
If Task 2 fails: Turbopack + next-intl has a known incompatibility. Use webpack by removing Turbopack.
- Step 1: Change dev script to use webpack
Modify package.json scripts:
{
"dev": "next dev --no-turbopack",
"build": "next build",
"start": "next start"
}- Step 2: Rebuild and test
pkill -f "next" 2>/dev/null; sleep 1
rm -rf apps/frontend/.next
cd apps/frontend && pnpm run build 2>&1 | tail -10
PORT=3000 pnpm run start > /tmp/next-prod.log 2>&1 &
sleep 5
curl -sL -o /dev/null -w "%{http_code}" http://localhost:3000/en- Step 3: If 200, commit
git add apps/frontend/package.json
git commit -m "fix: disable Turbopack for next-intl compatibility"Task 4: Run E2E test with working server
Files:
-
Verify:
__tests__/e2e/homepage.spec.ts -
Verify:
playwright.config.ts -
Step 1: Revert test URL changes (restore baseURL support)
The test was changed to use full URL http://localhost:3000/en. Change it back to /en so baseURL works:
// Navigate to homepage (English locale)
const response = await page.goto("/en", { waitUntil: "networkidle" });And the second test:
const response = await page.goto("/en", { waitUntil: "networkidle" });- Step 2: Run single E2E test
cd /Users/curlyz/usr/hol/apps/frontend
pnpm exec playwright test __tests__/e2e/homepage.spec.ts --reporter=list 2>&1
# Expected: PASS (or FAIL with clear error if Convex not running)- Step 3: Commit test fix
git add __tests__/e2e/homepage.spec.ts
git commit -m "fix: homepage spec uses relative URL with baseURL"Task 5: Verify E2E infrastructure is robust
Files:
-
Verify:
playwright.config.ts -
Step 1: Ensure webServer config works
The playwright config has webServer that auto-starts pnpm run dev. Verify this works:
cd /Users/curlyz/usr/hol/apps/frontend
pnpm exec playwright test __tests__/e2e/homepage.spec.ts --config=playwright.config.ts --reporter=list 2>&1
# Should auto-start dev server and pass- Step 2: Add console error capture to test
The test should capture and report console errors clearly:
test("should load without ConvexProvider RuntimeError", async ({ page }) => {
const consoleMessages: { type: string; text: string }[] = [];
page.on("console", (msg) => {
consoleMessages.push({ type: msg.type(), text: msg.text() });
});
const response = await page.goto("/en", { waitUntil: "networkidle" });
const consoleErrors = consoleMessages.filter((m) => m.type === "error");
const convexError = consoleErrors.find(
(err) =>
err.text.includes("Could not find Convex client") ||
err.text.includes("NEXT_PUBLIC_CONVEX_URL is not set") ||
err.text.includes("next-intl"),
);
expect(
convexError,
`Console errors: ${JSON.stringify(consoleErrors, null, 2)}`,
).toBeUndefined();
expect(response?.status()).toBe(200);
});- Step 3: Commit enhanced test
git add __tests__/e2e/homepage.spec.ts
git commit -m "test: improve console error reporting in homepage e2e test"Verification Checklist
After all tasks:
-
curl http://localhost:3000/enreturns 200 -
pnpm exec playwright test __tests__/e2e/homepage.spec.ts --reporter=listpasses - No console errors on homepage
- All changes committed
Notes
- next-intl v4.11.0 + Next.js 16 + Turbopack has known alias resolution issues
- The
middleware.ts→proxy.tsrename is unrelated (Next.js 16 deprecation warning) - If Task 3 (disable Turbopack) works, it's the safest fix for now
- Convex also needs to be running for full homepage render (separate from next-intl issue)