Skip to content

Commit d4cf875

Browse files
author
Miriad
committed
test: add Playwright E2E tests for dashboard + fix CI workflow
New tests (e2e/dashboard.spec.ts): - Dashboard auth: redirect to login, login form renders - Dashboard pages: content/videos/sponsors/settings return valid responses - API routes: webhook endpoints return 401 without auth, opt-out returns 400 CI fixes (.github/workflows/ci.yml): - Node.js 20 → 22 to match project requirements - Trigger on main branch (not just dev) - Run Playwright against production URL instead of localhost
1 parent 8dff08f commit d4cf875

File tree

2 files changed

+75
-9
lines changed

2 files changed

+75
-9
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ name: CI
22

33
on:
44
push:
5-
branches: [dev]
5+
branches: [dev, main]
66
pull_request:
7-
branches: [dev]
7+
branches: [dev, main]
88

99
concurrency:
1010
group: ${{ github.workflow }}-${{ github.ref }}
1111
cancel-in-progress: true
1212

1313
env:
14-
NODE_VERSION: "20"
14+
NODE_VERSION: "22"
1515

1616
jobs:
1717
ci:
@@ -71,12 +71,7 @@ jobs:
7171
run: pnpm exec playwright test
7272
env:
7373
CI: true
74-
PLAYWRIGHT_BASE_URL: http://localhost:3000
75-
NEXT_PUBLIC_SANITY_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_SANITY_PROJECT_ID }}
76-
NEXT_PUBLIC_SANITY_DATASET: ${{ secrets.NEXT_PUBLIC_SANITY_DATASET }}
77-
NEXT_PUBLIC_SANITY_API_VERSION: ${{ secrets.NEXT_PUBLIC_SANITY_API_VERSION }}
78-
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
79-
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
74+
PLAYWRIGHT_BASE_URL: https://codingcat.dev
8075

8176
- name: Upload Playwright report
8277
uses: actions/upload-artifact@v4

e2e/dashboard.spec.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
test.describe("Dashboard auth", () => {
4+
test("unauthenticated /dashboard redirects to /dashboard/login", async ({
5+
page,
6+
}) => {
7+
await page.goto("/dashboard");
8+
// Supabase auth proxy should redirect unauthenticated users to login
9+
await expect(page).toHaveURL(/\/dashboard\/login/);
10+
});
11+
12+
test("/dashboard/login renders a login form", async ({ page }) => {
13+
await page.goto("/dashboard/login");
14+
await expect(page.locator("body")).not.toBeEmpty();
15+
// Should have email and password inputs
16+
await expect(
17+
page.locator('input[type="email"], input[name="email"]').first(),
18+
).toBeVisible();
19+
await expect(
20+
page.locator('input[type="password"], input[name="password"]').first(),
21+
).toBeVisible();
22+
});
23+
});
24+
25+
test.describe("Dashboard pages exist", () => {
26+
const routes = [
27+
"/dashboard/content",
28+
"/dashboard/videos",
29+
"/dashboard/sponsors",
30+
"/dashboard/settings",
31+
];
32+
33+
for (const route of routes) {
34+
test(`${route} returns a valid response`, async ({ page }) => {
35+
const response = await page.goto(route);
36+
expect(response).not.toBeNull();
37+
// Should not be a 404 — redirects (302) to login are fine
38+
expect(response!.status()).not.toBe(404);
39+
});
40+
}
41+
});
42+
43+
test.describe("API routes respond", () => {
44+
test("POST /api/webhooks/sanity-content returns 401 without auth", async ({
45+
request,
46+
}) => {
47+
const response = await request.post("/api/webhooks/sanity-content");
48+
expect(response.status()).toBe(401);
49+
});
50+
51+
test("POST /api/webhooks/sanity-distribute returns 401 without auth", async ({
52+
request,
53+
}) => {
54+
const response = await request.post("/api/webhooks/sanity-distribute");
55+
expect(response.status()).toBe(401);
56+
});
57+
58+
test("POST /api/webhooks/sponsor-inbound returns 401 without auth", async ({
59+
request,
60+
}) => {
61+
const response = await request.post("/api/webhooks/sponsor-inbound");
62+
expect(response.status()).toBe(401);
63+
});
64+
65+
test("GET /api/sponsor/opt-out returns 400 without token", async ({
66+
request,
67+
}) => {
68+
const response = await request.get("/api/sponsor/opt-out");
69+
expect(response.status()).toBe(400);
70+
});
71+
});

0 commit comments

Comments
 (0)