Skip to content

Commit 4909ad3

Browse files
chore: add security audit and playwright e2e pipeline stage
1 parent 23edd57 commit 4909ad3

5 files changed

Lines changed: 168 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,55 @@ jobs:
132132

133133
- name: Build web
134134
run: pnpm --filter web build
135+
136+
security-audit:
137+
runs-on: ubuntu-latest
138+
steps:
139+
- name: Checkout
140+
uses: actions/checkout@v4
141+
142+
- name: Setup pnpm
143+
uses: pnpm/action-setup@v4
144+
with:
145+
version: 10
146+
147+
- name: Setup Node
148+
uses: actions/setup-node@v4
149+
with:
150+
node-version: 20
151+
cache: pnpm
152+
153+
- name: Install dependencies
154+
run: pnpm install --frozen-lockfile=false
155+
156+
- name: Security audit
157+
run: pnpm audit --prod
158+
159+
e2e:
160+
runs-on: ubuntu-latest
161+
needs: [build]
162+
steps:
163+
- name: Checkout
164+
uses: actions/checkout@v4
165+
166+
- name: Setup pnpm
167+
uses: pnpm/action-setup@v4
168+
with:
169+
version: 10
170+
171+
- name: Setup Node
172+
uses: actions/setup-node@v4
173+
with:
174+
node-version: 20
175+
cache: pnpm
176+
177+
- name: Install dependencies
178+
run: pnpm install --frozen-lockfile=false
179+
180+
- name: Install Playwright Browsers
181+
run: pnpm exec playwright install --with-deps chromium
182+
183+
- name: Run E2E tests
184+
env:
185+
RUN_E2E: 'false'
186+
run: pnpm e2e

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
"dev:web": "pnpm --filter web dev",
1111
"lint": "turbo run lint",
1212
"test": "turbo run test",
13+
"e2e": "playwright test",
1314
"format": "prettier --write .",
1415
"seed": "pnpm --filter backend seed"
1516
},
1617
"devDependencies": {
18+
"@playwright/test": "^1.55.1",
1719
"prettier": "^3.6.2",
1820
"turbo": "^2.5.0",
1921
"typescript": "^5.7.3"

playwright.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { defineConfig } from '@playwright/test';
2+
3+
export default defineConfig({
4+
testDir: './tests/e2e',
5+
timeout: 120_000,
6+
use: {
7+
baseURL: process.env.E2E_API_BASE_URL ?? 'http://localhost:3001/api/v1',
8+
},
9+
});

pnpm-lock.yaml

Lines changed: 34 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/e2e/fairshare.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+
const randomEmail = () => `fairshare_${Date.now()}_${Math.random().toString(36).slice(2)}@example.com`;
4+
5+
test('register login create group add expense settle expense', async ({ request, baseURL }) => {
6+
test.skip(process.env.RUN_E2E !== 'true', 'Set RUN_E2E=true to run backend e2e flow');
7+
test.skip(!baseURL, 'Base URL is required');
8+
9+
const emailA = randomEmail();
10+
const emailB = randomEmail();
11+
const password = 'Password123';
12+
13+
const registerA = await request.post('/auth/register', {
14+
data: { name: 'User A', email: emailA, password },
15+
});
16+
expect(registerA.ok()).toBeTruthy();
17+
const userA = await registerA.json();
18+
19+
const registerB = await request.post('/auth/register', {
20+
data: { name: 'User B', email: emailB, password },
21+
});
22+
expect(registerB.ok()).toBeTruthy();
23+
const userB = await registerB.json();
24+
25+
const loginA = await request.post('/auth/login', {
26+
data: { email: emailA, password },
27+
});
28+
expect(loginA.ok()).toBeTruthy();
29+
const loginPayload = await loginA.json();
30+
const token = loginPayload.accessToken as string;
31+
32+
const authHeaders = { Authorization: `Bearer ${token}` };
33+
34+
const createGroup = await request.post('/groups', {
35+
headers: authHeaders,
36+
data: { name: 'E2E Group', currency: 'USD' },
37+
});
38+
expect(createGroup.ok()).toBeTruthy();
39+
const group = await createGroup.json();
40+
41+
const invite = await request.post(`/groups/${group.id}/invite`, {
42+
headers: authHeaders,
43+
data: { email: emailB },
44+
});
45+
expect(invite.ok()).toBeTruthy();
46+
47+
const createExpense = await request.post(`/groups/${group.id}/expenses`, {
48+
headers: authHeaders,
49+
data: {
50+
payerId: userA.user.id,
51+
description: 'E2E Dinner',
52+
totalAmountCents: '1000',
53+
currency: 'USD',
54+
splits: [
55+
{ userId: userA.user.id, owedAmountCents: '500', paidAmountCents: '1000' },
56+
{ userId: userB.user.id, owedAmountCents: '500', paidAmountCents: '0' },
57+
],
58+
},
59+
});
60+
expect(createExpense.ok()).toBeTruthy();
61+
62+
const settle = await request.post(`/groups/${group.id}/settlements`, {
63+
headers: authHeaders,
64+
data: {
65+
payerId: userB.user.id,
66+
receiverId: userA.user.id,
67+
amountCents: '500',
68+
},
69+
});
70+
expect(settle.ok()).toBeTruthy();
71+
});

0 commit comments

Comments
 (0)