Add self-contained e2e test harness with ephemeral PocketBase
Some checks failed
CI / quality (push) Failing after 29s
Deploy / deploy (push) Successful in 2m37s

Previously, 15 e2e tests were skipped because TEST_USER_EMAIL and
TEST_USER_PASSWORD env vars weren't set. Now the test harness:

- Starts a fresh PocketBase instance in /tmp on port 8091
- Creates admin user, collections, and API rules automatically
- Seeds test user with period data for authenticated tests
- Cleans up temp directory after tests complete

Also fixes:
- Override toggle tests now use checkbox role (not button)
- Adds proper wait for OVERRIDES section before testing toggles
- Suppresses document.cookie lint warning with explanation

Test results: 64 e2e tests pass, 1014 unit tests pass

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-13 09:38:24 +00:00
parent eeeece17bf
commit 8c59b3bd67
12 changed files with 603 additions and 28 deletions

54
e2e/global-teardown.ts Normal file
View File

@@ -0,0 +1,54 @@
// ABOUTME: Playwright global teardown - stops PocketBase and cleans up temp data.
// ABOUTME: Runs after all e2e tests to ensure clean shutdown.
import * as fs from "node:fs";
import * as path from "node:path";
const STATE_FILE = path.join(__dirname, ".harness-state.json");
interface HarnessStateFile {
dataDir: string;
url: string;
pid: number;
}
export default async function globalTeardown(): Promise<void> {
console.log("Stopping PocketBase...");
// Read the saved state
if (!fs.existsSync(STATE_FILE)) {
console.log("No harness state file found, nothing to clean up.");
return;
}
const state: HarnessStateFile = JSON.parse(
fs.readFileSync(STATE_FILE, "utf-8"),
);
// Kill the PocketBase process
if (state.pid) {
try {
process.kill(state.pid, "SIGTERM");
// Wait for graceful shutdown
await new Promise((resolve) => setTimeout(resolve, 500));
// Force kill if still running
try {
process.kill(state.pid, "SIGKILL");
} catch {
// Process already dead, which is fine
}
} catch {
// Process might already be dead
}
}
// Clean up the temporary data directory
if (state.dataDir && fs.existsSync(state.dataDir)) {
fs.rmSync(state.dataDir, { recursive: true, force: true });
console.log(`Cleaned up temp directory: ${state.dataDir}`);
}
// Remove the state file
fs.unlinkSync(STATE_FILE);
console.log("PocketBase stopped and cleaned up.");
}