Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/api-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ env:
PGLITE: true
DATABASE_URL: postgresql://localhost/test
JWT_SECRET: e2e-jwt-secret-min-32-chars-for-tests
AI_PROVIDER: anthropic
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

jobs:
Expand Down Expand Up @@ -53,6 +54,16 @@ jobs:
run: pnpm turbo run test --filter=@repo/api
- name: Install Playwright
run: pnpm --filter @repo/api exec playwright install chromium --with-deps
- name: Verify ANTHROPIC_API_KEY for E2E (AI_PROVIDER=anthropic)
shell: bash
env:
AI_PROVIDER: ${{ env.AI_PROVIDER }}
ANTHROPIC_API_KEY: ${{ env.ANTHROPIC_API_KEY }}
run: |
if [[ "${AI_PROVIDER}" == "anthropic" && -z "${ANTHROPIC_API_KEY}" ]]; then
echo "::error::AI_PROVIDER is anthropic but ANTHROPIC_API_KEY is empty or unset. Set the ANTHROPIC_API_KEY repository secret; required before E2E tests (local)." >&2
exit 1
fi
- name: E2E tests (local)
run: pnpm --filter @repo/api test:e2e:local
- name: Upload Playwright report
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/web-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ env:
DATABASE_URL: postgresql://localhost/test
JWT_SECRET: e2e-jwt-secret-min-32-chars-for-tests
NEXT_PUBLIC_API_URL: http://localhost:3001
AI_PROVIDER: anthropic
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

jobs:
Expand Down Expand Up @@ -57,6 +58,16 @@ jobs:
run: pnpm turbo run test --filter=@repo/web
- name: Install Playwright
run: pnpm --filter @repo/web exec playwright install chromium --with-deps
- name: Verify ANTHROPIC_API_KEY for E2E (AI_PROVIDER=anthropic)
shell: bash
env:
AI_PROVIDER: ${{ env.AI_PROVIDER }}
ANTHROPIC_API_KEY: ${{ env.ANTHROPIC_API_KEY }}
run: |
if [[ "${AI_PROVIDER}" == "anthropic" && -z "${ANTHROPIC_API_KEY}" ]]; then
echo "::error::AI_PROVIDER is anthropic but ANTHROPIC_API_KEY is empty or unset. Set the ANTHROPIC_API_KEY repository secret; required before E2E tests (local)." >&2
exit 1
fi
- name: E2E tests (local)
env:
SKIP_BUILD: '1'
Expand Down
1 change: 1 addition & 0 deletions apps/api/.env.test.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ DATABASE_URL=postgresql://localhost/test
# AI
# -----------------------------------------------------------------------------
ANTHROPIC_API_KEY=sk-ant-xxx
AI_PROVIDER=anthropic
# OPEN_ROUTER_API_KEY=
# OLLAMA_BASE_URL=http://localhost:11434
# AI_DEFAULT_MODEL=claude-sonnet-4-20250514
Expand Down
13 changes: 13 additions & 0 deletions apps/api/scripts/run-e2e-local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/**
* E2E local: spawn Fastify API, poll until healthy, run Playwright, cleanup on exit.
* No wait-on. Uses ALLOW_TEST, PGLITE, NODE_ENV=test.
* Forces AI_PROVIDER=anthropic and strips Open Router/Ollama/AI_DEFAULT_MODEL (parity with Vitest + web E2E).
*/
import { spawn, spawnSync } from 'node:child_process'
import { existsSync, readFileSync } from 'node:fs'
Expand Down Expand Up @@ -67,14 +68,26 @@ async function main() {
)
process.exit(1)
}
const anthropicApiKey = loaded.ANTHROPIC_API_KEY ?? process.env.ANTHROPIC_API_KEY
if (!anthropicApiKey) {
process.stderr.write(
'E2E local: ANTHROPIC_API_KEY must be set in .env.test or process.env when AI_PROVIDER is anthropic. Refusing to run without it.\n',
)
process.exit(1)
}
const env = {
...process.env,
...loaded,
ALLOW_TEST: 'true',
PGLITE: 'true',
NODE_ENV: 'test',
JWT_SECRET: jwtSecret,
AI_PROVIDER: 'anthropic',
ANTHROPIC_API_KEY: anthropicApiKey,
}
delete env.OPEN_ROUTER_API_KEY
delete env.OLLAMA_BASE_URL
delete env.AI_DEFAULT_MODEL

const fastify = spawn(process.execPath, ['--import', 'tsx', 'server.ts'], {
cwd: fastifyDir,
Expand Down
2 changes: 1 addition & 1 deletion apps/docu/content/docs/testing/frontend-testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ test.describe('Magic Link Auth E2E', () => {

- **workers: 1** — Required for PGlite (Fastify test DB) to avoid concurrent writer issues
- **Test order** — Auth project first (magic-link-auth: error tests, login/logout; ends with logout). Chromium: unauth badge tests first, then first authed test triggers fixture and confirms session, then remaining authed tests run consecutively
- **Chat E2E** — Requires `OPEN_ROUTER_API_KEY`; see [E2E Testing](/docs/testing/e2e-testing) for env vars. If UNAUTHORIZED, session/Bearer propagation to Fastify `/ai/chat` needs debugging.
- **Chat E2E** — Requires `ANTHROPIC_API_KEY` (Anthropic Sonnet); see [E2E Testing](/docs/testing/e2e-testing) for env vars. If UNAUTHORIZED, session/Bearer propagation to Fastify `/ai/chat` needs debugging.
- **Session model** — One login per worker via `authenticatedStorageState` fixture; cookies/storage saved to `test-results/.auth/user.json` (gitignored); each authed test gets a new context created with that storageState. No per-test login
- **Login/logout tests** — Import from `@playwright/test`, use `page`; need fresh contexts to verify the flow
- **Authed feature tests** — Import from `e2e/fixtures`, use `authenticatedPage`; no `loginAsTestUser` in test body. Each test gets a fresh context with saved storageState; navigate to target route (e.g. `goto('/')`) before asserting
Expand Down
2 changes: 1 addition & 1 deletion apps/web/e2e/chat-assistant.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test.describe('Chat Assistant', () => {
if (
/insufficient_quota|insufficient_credits|quota_exceeded|credits_exceeded/i.test(errorText)
) {
test.skip(true, 'OpenRouter 402 insufficient credits')
test.skip(true, 'AI provider quota/credits (402)')
return
}
if (/invalid x-api-key|authentication_error|invalid.*api.*key|401/i.test(errorText)) {
Expand Down
11 changes: 11 additions & 0 deletions apps/web/scripts/start-e2e-servers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Run in one terminal, then in another: pnpm test:e2e
*
* Use when Playwright's webServer spawns processes that get OOM killed (exit 137) on constrained VMs.
* Pins AI to Anthropic for API (same as test:e2e:local).
*/
import { spawn } from 'node:child_process'
import { dirname } from 'node:path'
Expand All @@ -18,7 +19,17 @@ const env = {
PGLITE: 'true',
NODE_ENV: 'test',
NEXT_PUBLIC_API_URL: 'http://localhost:3001',
AI_PROVIDER: 'anthropic',
}
if (env.AI_PROVIDER === 'anthropic' && !String(env.ANTHROPIC_API_KEY ?? '').trim()) {
process.stderr.write(
'start-e2e-servers: ANTHROPIC_API_KEY is required when AI_PROVIDER is anthropic. Set it in the environment (e.g. .env.local) before starting E2E servers.\n',
)
process.exit(1)
}
delete env.OPEN_ROUTER_API_KEY
delete env.OLLAMA_BASE_URL
delete env.AI_DEFAULT_MODEL

const api = spawn('pnpm', ['--filter', '@repo/api', 'start:ci'], {
cwd: repoRoot,
Expand Down
Loading