11/**
2- * E2E browser test for the hackathon demo flow.
2+ * E2E browser test — simulates a human demo flow through bench-ui and evidra-ui .
33 *
44 * Prerequisites:
5- * docker compose up -d postgres evidra-api evidra-mcp bench-cli bench-ui
6- * docker compose run --rm kind-bootstrap
5+ * docker compose up -d
76 *
87 * Run:
98 * cd tests/e2e && npx playwright test demo-flow.spec.ts
109 *
11- * What the audience sees (and this test verifies):
12- * 1. Evidra UI — landing, evidence, scorecard, bench dashboard
13- * 2. Trigger scenario via API → bench-cli runs it
14- * 3. Evidence chain populated with prescribe/report entries
15- * 4. Bench UI — leaderboard, certification results
10+ * Flow:
11+ * 1. Open bench UI landing page
12+ * 2. Browse scenarios
13+ * 3. Start certify run (TODO: UI button not yet implemented, uses API)
14+ * 4. Check exam results page
15+ * 5. Check bench leaderboard
16+ * 6. Switch to evidra UI — evidence and dashboard
1617 */
1718
1819import { test , expect , type Page } from "@playwright/test" ;
1920
20- const EVIDRA_URL = process . env . EVIDRA_URL || "http://localhost:28080" ;
21- const BENCH_UI_URL = process . env . BENCH_UI_URL || "http://localhost:28081" ;
21+ const BASE_URL = process . env . EVIDRA_URL || "http://localhost:28080" ;
2222const API_KEY = process . env . EVIDRA_API_KEY || "dev-api-key" ;
2323
24+ // Bench UI is served under /lab/ via Traefik.
25+ const LAB_URL = `${ BASE_URL } /lab` ;
26+
2427// ── Helpers ──────────────────────────────────────────────────────────
2528
2629async function setApiKey ( page : Page ) {
@@ -35,7 +38,7 @@ async function setApiKey(page: Page) {
3538}
3639
3740async function apiRequest ( path : string , options ?: RequestInit ) {
38- return fetch ( `${ EVIDRA_URL } ${ path } ` , {
41+ return fetch ( `${ BASE_URL } ${ path } ` , {
3942 headers : {
4043 Authorization : `Bearer ${ API_KEY } ` ,
4144 "Content-Type" : "application/json" ,
@@ -47,50 +50,34 @@ async function apiRequest(path: string, options?: RequestInit) {
4750
4851// ── Tests ────────────────────────────────────────────────────────────
4952
50- test . describe ( "Hackathon Demo Flow" , ( ) => {
53+ test . describe ( "Demo Flow" , ( ) => {
5154 test . setTimeout ( 180_000 ) ;
5255
53- test ( "1. Evidra UI loads with navigation" , async ( { page } ) => {
54- await page . goto ( EVIDRA_URL ) ;
56+ // Step 1: Open bench UI landing page
57+ test ( "1. Bench UI landing page loads" , async ( { page } ) => {
58+ await page . goto ( LAB_URL ) ;
5559 await page . waitForLoadState ( "networkidle" ) ;
5660
57- // Landing page should have the main headline.
58- await expect ( page . locator ( "text=Know what your agent intended" ) ) . toBeVisible (
59- { timeout : 10_000 }
60- ) ;
61- } ) ;
62-
63- test ( "2. Evidence page loads" , async ( { page } ) => {
64- await page . goto ( `${ EVIDRA_URL } /evidence` ) ;
65- await setApiKey ( page ) ;
66- await page . waitForTimeout ( 2_000 ) ;
67-
68- // Evidence page should load — table or empty state.
69- const body = await page . textContent ( "body" ) ;
70- expect ( body ) . toBeTruthy ( ) ;
61+ await expect ( page . locator ( "text=before shipping" ) ) . toBeVisible ( {
62+ timeout : 10_000 ,
63+ } ) ;
64+ await page . screenshot ( { path : "test-results/01-bench-landing.png" } ) ;
7165 } ) ;
7266
73- test ( "3. Bench dashboard loads with trigger button" , async ( { page } ) => {
74- await page . goto ( `${ EVIDRA_URL } /bench` ) ;
75- await setApiKey ( page ) ;
76- await page . waitForTimeout ( 3_000 ) ;
77-
78- // Take screenshot to see what the bench page looks like.
79- await page . screenshot ( { path : "test-results/bench-dashboard.png" } ) ;
67+ // Step 2: Browse and select scenarios
68+ test ( "2. Browse scenarios" , async ( { page } ) => {
69+ await page . goto ( `${ LAB_URL } /scenarios` ) ;
70+ await page . waitForLoadState ( "networkidle" ) ;
8071
81- // Bench page should load .
72+ // Scenarios page should list available scenarios .
8273 const body = await page . textContent ( "body" ) ;
8374 expect ( body ) . toBeTruthy ( ) ;
75+ await page . screenshot ( { path : "test-results/02-scenarios.png" } ) ;
8476 } ) ;
8577
86- test ( "4. Scenarios API returns 75+ scenarios" , async ( ) => {
87- const res = await apiRequest ( "/v1/bench/scenarios" ) ;
88- expect ( res . status ) . toBe ( 200 ) ;
89- const data = await res . json ( ) ;
90- expect ( data . scenarios . length ) . toBeGreaterThanOrEqual ( 50 ) ;
91- } ) ;
92-
93- test ( "5. Trigger scenario and wait for completion" , async ( ) => {
78+ // Step 3: Start certify run
79+ // TODO: Implement "Run" button in bench-ui. For now, trigger via API.
80+ test ( "3. Trigger certify run via API" , async ( ) => {
9481 const triggerRes = await apiRequest ( "/v1/bench/trigger" , {
9582 method : "POST" ,
9683 body : JSON . stringify ( {
@@ -114,58 +101,67 @@ test.describe("Hackathon Demo Flow", () => {
114101 }
115102
116103 expect ( [ "completed" , "failed" ] ) . toContain ( status ) ;
117- // Job should have processed 1 scenario.
118104 expect ( job . total ) . toBe ( 1 ) ;
119105 expect ( job . completed ) . toBe ( 1 ) ;
120106 } ) ;
121107
122- test ( "6. Evidence entries exist after scenario run" , async ( ) => {
123- const res = await apiRequest ( "/v1/evidence/entries?limit=10" ) ;
124- expect ( res . status ) . toBe ( 200 ) ;
125- const data = await res . json ( ) ;
126- // bench-cli's MCP subprocess forwards evidence to evidra-api.
127- expect ( data . entries ) . toBeDefined ( ) ;
128- } ) ;
129-
130- test ( "7. Bench runs API has results" , async ( ) => {
131- const res = await apiRequest ( "/v1/bench/runs" ) ;
132- expect ( res . status ) . toBe ( 200 ) ;
133- const data = await res . json ( ) ;
134- expect ( data . items ) . toBeDefined ( ) ;
135- } ) ;
108+ // Step 4: Check exam results page
109+ test ( "4. Exam results page shows data" , async ( { page } ) => {
110+ await page . goto ( `${ LAB_URL } /results` ) ;
111+ await page . waitForLoadState ( "networkidle" ) ;
112+ await page . waitForTimeout ( 2_000 ) ;
136113
137- test ( "8. Leaderboard API responds (public)" , async ( ) => {
138- const res = await fetch ( `${ EVIDRA_URL } /v1/bench/leaderboard` ) ;
139- expect ( res . status ) . toBe ( 200 ) ;
114+ // Results page should not show "Not Configured" error.
115+ const body = await page . textContent ( "body" ) ;
116+ expect ( body ) . not . toContain ( "Not Configured" ) ;
117+ await page . screenshot ( { path : "test-results/04-exam-results.png" } ) ;
140118 } ) ;
141119
142- test ( "9. Bench UI loads landing and leaderboard" , async ( { page } ) => {
143- await page . goto ( BENCH_UI_URL ) ;
120+ // Step 5: Check bench leaderboard
121+ test ( "5. Leaderboard shows model results" , async ( { page } ) => {
122+ await page . goto ( `${ LAB_URL } /bench` ) ;
144123 await page . waitForLoadState ( "networkidle" ) ;
145124
146- // Landing page should show the main headline.
147- await expect ( page . locator ( "text=before shipping" ) ) . toBeVisible ( {
125+ await expect ( page . locator ( "text=Model Leaderboard" ) ) . toBeVisible ( {
148126 timeout : 10_000 ,
149127 } ) ;
150- await page . screenshot ( { path : "test-results/bench-ui-landing.png" } ) ;
128+ await page . screenshot ( { path : "test-results/05-leaderboard.png" } ) ;
129+ } ) ;
151130
152- // Navigate to leaderboard.
153- await page . goto ( `${ BENCH_UI_URL } /bench` ) ;
131+ // Step 6: Switch to evidra UI — evidence and dashboard
132+ test ( "6a. Evidra UI landing page" , async ( { page } ) => {
133+ await page . goto ( BASE_URL ) ;
154134 await page . waitForLoadState ( "networkidle" ) ;
155- await expect ( page . locator ( "text=Model Leaderboard" ) ) . toBeVisible ( {
135+
136+ await expect ( page . locator ( "text=Know what your agent intended" ) ) . toBeVisible ( {
156137 timeout : 10_000 ,
157138 } ) ;
158- await page . screenshot ( { path : "test-results/bench-ui-leaderboard .png" } ) ;
139+ await page . screenshot ( { path : "test-results/06a-evidra-landing .png" } ) ;
159140 } ) ;
160141
161- test ( "10. Bench UI shows bench page with nav" , async ( { page } ) => {
162- await page . goto ( `${ BENCH_UI_URL } /bench` ) ;
163- await page . waitForLoadState ( "networkidle" ) ;
142+ test ( "6b. Evidence page loads" , async ( { page } ) => {
143+ await page . goto ( `${ BASE_URL } /evidence` ) ;
144+ await setApiKey ( page ) ;
145+ await page . waitForTimeout ( 2_000 ) ;
164146
165- // Nav should have: Home, Dashboard, Evidence, Bench, Lab
166- await expect ( page . locator ( "nav >> text=Bench" ) ) . toBeVisible ( ) ;
167- await expect ( page . locator ( "nav >> text=Dashboard" ) ) . toBeVisible ( ) ;
147+ const body = await page . textContent ( "body" ) ;
148+ expect ( body ) . toBeTruthy ( ) ;
149+ await page . screenshot ( { path : "test-results/06b-evidence.png" } ) ;
150+ } ) ;
151+
152+ test ( "6c. Dashboard loads" , async ( { page } ) => {
153+ await page . goto ( `${ BASE_URL } /bench` ) ;
154+ await setApiKey ( page ) ;
155+ await page . waitForTimeout ( 2_000 ) ;
168156
169- await page . screenshot ( { path : "test-results/bench-ui-bench.png" } ) ;
157+ const body = await page . textContent ( "body" ) ;
158+ expect ( body ) . toBeTruthy ( ) ;
159+ await page . screenshot ( { path : "test-results/06c-dashboard.png" } ) ;
160+ } ) ;
161+
162+ // API verification — leaderboard is public, no auth needed.
163+ test ( "7. Leaderboard API responds (public)" , async ( ) => {
164+ const res = await fetch ( `${ BASE_URL } /v1/bench/leaderboard` ) ;
165+ expect ( res . status ) . toBe ( 200 ) ;
170166 } ) ;
171167} ) ;
0 commit comments