Skip to content

Commit 8a2da1f

Browse files
committed
add tracing mode
1 parent ce5a53d commit 8a2da1f

File tree

6 files changed

+122
-7
lines changed

6 files changed

+122
-7
lines changed

.github/workflows/bdd.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: BDD E2E
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
pull_request:
7+
branches: ["**"]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
e2e:
14+
name: Run BDD (Cucumber + Playwright)
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 20
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
20+
21+
- name: Setup
22+
uses: ./.github/actions/setup
23+
24+
- name: Install Playwright browsers
25+
run: pnpm exec playwright install --with-deps
26+
27+
- name: Start dev stack (Next + OIDC + Mock)
28+
run: |
29+
pnpm dev &
30+
echo $! > dev.pid
31+
# Wait for Next (3000), OIDC (4000), and Mock API (9090)
32+
for url in http://localhost:3000 http://localhost:4000 http://localhost:9090; do
33+
echo "Waiting for $url..."
34+
for i in {1..60}; do
35+
if curl -sf "$url" >/dev/null; then
36+
echo "$url is up"
37+
break
38+
fi
39+
sleep 2
40+
done
41+
done
42+
43+
- name: Run BDD tests
44+
env:
45+
BASE_URL: http://localhost:3000
46+
run: pnpm run test:bdd
47+
48+
- name: Cleanup dev
49+
if: always()
50+
run: |
51+
if [ -f dev.pid ]; then kill $(cat dev.pid) || true; fi
52+

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
# testing
1414
/coverage
15+
# Playwright/Cucumber E2E artifacts
16+
test-results/
17+
playwright-report/
18+
blob-report/
1519

1620
# next.js
1721
/.next/

CLAUDE.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ pnpm generate-client:nofetch # Regenerate without fetching
155155

156156
### Backend API
157157

158-
- **Base URL**: Configured via `NEXT_PUBLIC_API_URL`
158+
- **Base URL**: Configured via `API_BASE_URL` (server-side only)
159159
- **Format**: Official MCP Registry API (upstream compatible)
160160
- **Endpoints**:
161161
- `GET /api/v0/servers` - List all MCP servers
@@ -178,7 +178,7 @@ pnpm generate-client:nofetch # Regenerate without fetching
178178
### Production
179179

180180
1. User accesses protected route
181-
2. Redirected to `/sign-in`
181+
2. Redirected to `/signin`
182182
3. Better Auth initiates OIDC flow with configured provider
183183
4. Provider redirects back with authorization code
184184
5. Better Auth exchanges code for tokens
@@ -218,6 +218,19 @@ pnpm generate-client:nofetch # Regenerate without fetching
218218
- **Testing Library** - Component testing
219219
- **jsdom** - DOM simulation
220220

221+
### BDD E2E (Cucumber + Playwright)
222+
223+
- End-to-end scenarios live under `tests/bdd` and run against a live dev stack.
224+
- Commands:
225+
- `pnpm dev` – starts Next.js (3000), mock OIDC (4000), and MSW mock API (9090)
226+
- `pnpm run test:bdd` – runs Cucumber scenarios with Playwright (headless)
227+
- `pnpm run test:bdd:debug` – runs with Playwright Inspector (headed, pauses on start)
228+
- `pnpm run test:bdd:trace` – runs with Playwright tracing, artifacts in `test-results/traces/*.zip`
229+
- CI runs BDD tests via `.github/workflows/bdd.yml` and installs Playwright browsers.
230+
- Install browsers locally once: `pnpm exec playwright install`
231+
232+
Scaffolded global steps include navigation, clicking buttons by accessible name, URL assertions, and text/heading checks. Prefer reusing global steps; add domain-specific ones only when needed for clarity.
233+
221234
### Example Test
222235

223236
```typescript
@@ -274,7 +287,10 @@ git push origin v0.x.x
274287

275288
### Authentication Not Working
276289

277-
- **Development**: Ensure OIDC mock is running (`pnpm oidc`)
290+
- **Development**:
291+
- Ensure OIDC mock is running (`pnpm oidc`) or start the full stack with `pnpm dev`
292+
- Dev provider issues refresh tokens unconditionally and uses a short AccessToken TTL (15s) to exercise the refresh flow
293+
- If you see origin errors (403), ensure `BETTER_AUTH_URL` matches the port you use (default `http://localhost:3000`) or include it in `TRUSTED_ORIGINS`
278294
- **Production**: Check environment variables:
279295
- `OIDC_ISSUER_URL` - OIDC provider URL
280296
- `OIDC_CLIENT_ID` - OAuth2 client ID
@@ -285,7 +301,7 @@ git push origin v0.x.x
285301

286302
### API Calls Failing
287303

288-
- Check `NEXT_PUBLIC_API_URL` environment variable
304+
- Check `API_BASE_URL` environment variable
289305
- Verify backend API is running
290306
- Check browser console for CORS errors
291307

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ bun dev
1616

1717
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
1818

19-
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
19+
Authentication: the dev stack also starts a local OIDC provider (on :4000) and MSW mock API (on :9090). The `/signin` page initiates the OIDC flow and redirects back to `/catalog` on success.
2020

2121
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
2222

@@ -109,6 +109,30 @@ To learn more about Next.js, take a look at the following resources:
109109

110110
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
111111

112+
## Testing
113+
114+
### Unit/Component
115+
116+
```bash
117+
pnpm test # Vitest
118+
pnpm type-check # TypeScript
119+
pnpm lint # Biome
120+
```
121+
122+
### BDD E2E (Cucumber + Playwright)
123+
124+
Run the app and E2E tests locally:
125+
126+
```bash
127+
pnpm exec playwright install # one-time browser install
128+
pnpm dev # start Next (3000) + OIDC (4000) + Mock API (9090)
129+
pnpm run test:bdd # run Cucumber scenarios (headless)
130+
pnpm run test:bdd:debug # headed with Playwright Inspector (PWDEBUG=1)
131+
pnpm run test:bdd:trace # capture Playwright traces (PWTRACE=1)
132+
```
133+
134+
CI runs the E2E suite via `.github/workflows/bdd.yml`.
135+
112136
## Deploy on Vercel
113137

114138
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"format": "biome format --write",
1414
"test": "vitest",
1515
"test:bdd": "cucumber-js",
16+
"test:bdd:trace": "PWTRACE=1 cucumber-js",
1617
"test:bdd:debug": "PWDEBUG=1 cucumber-js",
1718
"type-check": "tsc --noEmit",
1819
"prepare": "husky",

tests/bdd/support/hooks.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { After, Before, setDefaultTimeout } from "@cucumber/cucumber";
1+
import {
2+
After,
3+
Before,
4+
type ITestCaseHookParameter,
5+
setDefaultTimeout,
6+
} from "@cucumber/cucumber";
27
import {
38
type Browser,
49
type BrowserContext,
@@ -8,6 +13,7 @@ import {
813
import type { PlaywrightWorld } from "./world";
914

1015
let browser: Browser | undefined;
16+
const TRACE_ENABLED = process.env.PWTRACE === "1";
1117

1218
setDefaultTimeout(60 * 1000); // 60s per step
1319

@@ -24,12 +30,24 @@ Before(async function (this: PlaywrightWorld) {
2430
this.context = context;
2531
this.page = page;
2632

33+
if (TRACE_ENABLED) {
34+
await this.context.tracing.start({ screenshots: true, snapshots: true });
35+
}
36+
2737
if (isDebug) {
2838
await this.page.pause();
2939
}
3040
});
3141

32-
After(async function (this: PlaywrightWorld) {
42+
After(async function (this: PlaywrightWorld, scenario: ITestCaseHookParameter) {
43+
if (TRACE_ENABLED && this.context) {
44+
const safeName = scenario.pickle.name.replace(/[^a-z0-9-]+/gi, "_");
45+
const { mkdir } = await import("node:fs/promises");
46+
await mkdir("test-results/traces", { recursive: true });
47+
await this.context.tracing.stop({
48+
path: `test-results/traces/${safeName}.zip`,
49+
});
50+
}
3351
if (this.page) await this.page.close();
3452
if (this.context) await this.context.close();
3553
});

0 commit comments

Comments
 (0)