Skip to content

Commit 7147f9b

Browse files
committed
test: rewrite e2e to match human demo flow
1. Bench UI landing → 2. Browse scenarios → 3. Trigger certify (API, TODO: UI button) → 4. Exam results → 5. Leaderboard → 6. Evidra UI evidence + dashboard. Uses Traefik-routed /lab/ path instead of separate BENCH_UI_URL. Signed-off-by: Vitas <vitas@users.noreply.github.com>
1 parent 33c54cf commit 7147f9b

1 file changed

Lines changed: 74 additions & 78 deletions

File tree

tests/e2e/demo-flow.spec.ts

Lines changed: 74 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
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

1819
import { 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";
2222
const 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

2629
async function setApiKey(page: Page) {
@@ -35,7 +38,7 @@ async function setApiKey(page: Page) {
3538
}
3639

3740
async 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

Comments
 (0)