Files
e2e 1e78e21c36
Some checks failed
Main / Setup and Test (push) Has been cancelled
Main / Build Website (push) Has been cancelled
chore: heygermany customer project
2026-07-11 09:36:03 +02:00

149 lines
3.4 KiB
TypeScript

import {
Before,
After,
BeforeAll,
AfterAll,
setDefaultTimeout,
} from '@cucumber/cucumber'
import { readFileSync } from 'fs'
import { spawn, execFileSync, type ChildProcess } from 'child_process'
import { resolve, dirname, join } from 'path'
import { fileURLToPath } from 'url'
import type { AppWorld } from './world.js'
import { config } from './types.js'
setDefaultTimeout(Math.max(config.responseTimeout, 120_000))
let backendProcess: ChildProcess | undefined
let frontendProcess: ChildProcess | undefined
async function waitForUrl(url: string, label: string, timeoutMs = 60_000) {
const deadline = Date.now() + timeoutMs
while (Date.now() < deadline) {
try {
const res = await fetch(url)
if (res.status < 500) return
} catch {
// not ready yet
}
await new Promise((r) => setTimeout(r, 500))
}
throw new Error(`[e2e] ${label} did not become ready at ${url} within ${timeoutMs}ms`)
}
function startProcess({
cmd,
cwd,
env,
label,
}: {
cmd: string
cwd: string
env: NodeJS.ProcessEnv
label: string
}) {
const p = spawn(cmd, {
cwd,
env,
stdio: 'pipe',
shell: true,
detached: true,
})
p.stderr?.on('data', (d: Buffer) => process.stderr.write(`[${label}] ${d}`))
p.stdout?.on('data', (d: Buffer) => process.stdout.write(`[${label}] ${d}`))
return p
}
async function stopProcess(p: ChildProcess | undefined) {
if (!p?.pid) return
try {
process.kill(-p.pid, 'SIGTERM')
} catch {
try {
p.kill('SIGTERM')
} catch {
// ignore
}
}
await new Promise((r) => setTimeout(r, 1_000))
}
function killPort(port: number) {
try {
const out = execFileSync('lsof', ['-ti', `tcp:${port}`], {
encoding: 'utf8',
}).trim()
if (!out) return
for (const pid of out.split(/\s+/)) {
if (!pid) continue
try {
process.kill(Number(pid), 'SIGTERM')
} catch {
// ignore
}
}
} catch {
// ignore
}
}
const __dirname = dirname(fileURLToPath(import.meta.url))
const repoRoot = resolve(__dirname, '../../..')
const seedFile = join(repoRoot, 'db', 'sqlite-seed.sql')
const sqliteDb = join(repoRoot, '.pikku-runtime', 'dev.db')
function prepareDatabase() {
execFileSync('sqlite3', [sqliteDb], {
cwd: repoRoot,
input: readFileSync(seedFile),
stdio: ['pipe', 'inherit', 'inherit'],
})
}
BeforeAll(async function () {
if (!config.manageServers) return
if (process.env.E2E_PREPARE_DB !== '0' && process.env.E2E_PREPARE_DB !== 'false') {
prepareDatabase()
}
killPort(3000)
killPort(3001)
backendProcess = startProcess({
cmd: process.env.E2E_BACKEND_CMD ?? 'bun run dev:backend',
cwd: repoRoot,
env: {
...process.env,
NODE_ENV: 'test',
},
label: 'backend',
})
await waitForUrl(`${config.apiUrl}/api/auth/get-session`, 'backend')
frontendProcess = startProcess({
cmd: process.env.E2E_FRONTEND_CMD ?? 'bun run dev:frontend',
cwd: repoRoot,
env: {
...process.env,
NODE_ENV: 'test',
VITE_API_BASE_URL: config.apiUrl,
},
label: 'frontend',
})
await waitForUrl(`${config.appUrl}/en`, 'frontend')
})
AfterAll(async function () {
await stopProcess(frontendProcess)
await stopProcess(backendProcess)
})
Before(async function (this: AppWorld) {
await this.openBrowser()
})
After(async function (this: AppWorld) {
await this.closeBrowser()
})