Skip to content

Commit 49e6265

Browse files
authored
Testing (#26)
* add unit and integration tests, partial e2e tests * fix e2e tests * add more e2e tests * e2e fixes * fixes * add local only .env.local loading * rename frontend test job/command * fix env var issue * add setup step to CI * fixes * fixes 2.0 * improve tests * fixes * e2e fixes * fix react stability issue * fix stable ref * add more backend unit tests * fixes * add more tests * fix type issues
1 parent 9803040 commit 49e6265

153 files changed

Lines changed: 20135 additions & 377 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 143 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,71 +7,123 @@ concurrency:
77
group: ${{ github.workflow }}-${{ github.ref }}
88
cancel-in-progress: true
99

10+
env:
11+
NODE_VERSION: 22
12+
STORE_PATH_KEY: pnpm-store-
13+
1014
jobs:
15+
setup:
16+
name: Setup
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v4
20+
- uses: pnpm/action-setup@v4
21+
- uses: actions/setup-node@v4
22+
with:
23+
node-version: ${{ env.NODE_VERSION }}
24+
cache: pnpm
25+
- run: pnpm install --frozen-lockfile
26+
- uses: actions/cache/save@v4
27+
with:
28+
path: |
29+
node_modules
30+
~/.local/share/pnpm/store
31+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
32+
1133
typecheck:
1234
name: Type Check
1335
runs-on: ubuntu-latest
36+
needs: [setup]
1437
steps:
1538
- uses: actions/checkout@v4
1639
- uses: pnpm/action-setup@v4
1740
- uses: actions/setup-node@v4
1841
with:
19-
node-version: 22
20-
cache: pnpm
42+
node-version: ${{ env.NODE_VERSION }}
43+
- uses: actions/cache/restore@v4
44+
with:
45+
path: |
46+
node_modules
47+
~/.local/share/pnpm/store
48+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
2149
- run: pnpm install --frozen-lockfile
2250
- run: pnpm tsc --noEmit
2351

2452
lint:
2553
name: Lint
2654
runs-on: ubuntu-latest
55+
needs: [setup]
2756
steps:
2857
- uses: actions/checkout@v4
2958
- uses: pnpm/action-setup@v4
3059
- uses: actions/setup-node@v4
3160
with:
32-
node-version: 22
33-
cache: pnpm
61+
node-version: ${{ env.NODE_VERSION }}
62+
- uses: actions/cache/restore@v4
63+
with:
64+
path: |
65+
node_modules
66+
~/.local/share/pnpm/store
67+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
3468
- run: pnpm install --frozen-lockfile
3569
- run: pnpm lint
3670

3771
format:
3872
name: Format Check
3973
runs-on: ubuntu-latest
74+
needs: [setup]
4075
steps:
4176
- uses: actions/checkout@v4
4277
- uses: pnpm/action-setup@v4
4378
- uses: actions/setup-node@v4
4479
with:
45-
node-version: 22
46-
cache: pnpm
80+
node-version: ${{ env.NODE_VERSION }}
81+
- uses: actions/cache/restore@v4
82+
with:
83+
path: |
84+
node_modules
85+
~/.local/share/pnpm/store
86+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
4787
- run: pnpm install --frozen-lockfile
4888
- run: pnpm prettier --check .
4989

5090
react-compiler:
5191
name: React Compiler Health
5292
runs-on: ubuntu-latest
93+
needs: [setup]
5394
steps:
5495
- uses: actions/checkout@v4
5596
- uses: pnpm/action-setup@v4
5697
- uses: actions/setup-node@v4
5798
with:
58-
node-version: 22
59-
cache: pnpm
99+
node-version: ${{ env.NODE_VERSION }}
100+
- uses: actions/cache/restore@v4
101+
with:
102+
path: |
103+
node_modules
104+
~/.local/share/pnpm/store
105+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
60106
- run: pnpm install --frozen-lockfile
61107
- run: npx react-compiler-healthcheck --src "src/**/*.{ts,tsx}"
62108

63109
react-doctor:
64110
name: React Doctor
65111
runs-on: ubuntu-latest
112+
needs: [setup]
66113
steps:
67114
- uses: actions/checkout@v4
68115
with:
69116
fetch-depth: 0
70117
- uses: pnpm/action-setup@v4
71118
- uses: actions/setup-node@v4
72119
with:
73-
node-version: 22
74-
cache: pnpm
120+
node-version: ${{ env.NODE_VERSION }}
121+
- uses: actions/cache/restore@v4
122+
with:
123+
path: |
124+
node_modules
125+
~/.local/share/pnpm/store
126+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
75127
- run: pnpm install --frozen-lockfile
76128
- name: Run React Doctor (full scan on main, diff on PRs)
77129
run: |
@@ -81,6 +133,44 @@ jobs:
81133
npx -y react-doctor@latest . --verbose --fail-on error
82134
fi
83135
136+
frontend-test:
137+
name: Frontend Tests
138+
runs-on: ubuntu-latest
139+
needs: [typecheck]
140+
steps:
141+
- uses: actions/checkout@v4
142+
- uses: pnpm/action-setup@v4
143+
- uses: actions/setup-node@v4
144+
with:
145+
node-version: ${{ env.NODE_VERSION }}
146+
- uses: actions/cache/restore@v4
147+
with:
148+
path: |
149+
node_modules
150+
~/.local/share/pnpm/store
151+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
152+
- run: pnpm install --frozen-lockfile
153+
- run: pnpm test:frontend --reporter=verbose
154+
155+
backend-test:
156+
name: Backend Tests
157+
runs-on: ubuntu-latest
158+
needs: [typecheck]
159+
steps:
160+
- uses: actions/checkout@v4
161+
- uses: pnpm/action-setup@v4
162+
- uses: actions/setup-node@v4
163+
with:
164+
node-version: ${{ env.NODE_VERSION }}
165+
- uses: actions/cache/restore@v4
166+
with:
167+
path: |
168+
node_modules
169+
~/.local/share/pnpm/store
170+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
171+
- run: pnpm install --frozen-lockfile
172+
- run: pnpm test:backend:run --reporter=verbose
173+
84174
build:
85175
name: Build
86176
runs-on: ubuntu-latest
@@ -90,7 +180,48 @@ jobs:
90180
- uses: pnpm/action-setup@v4
91181
- uses: actions/setup-node@v4
92182
with:
93-
node-version: 22
94-
cache: pnpm
183+
node-version: ${{ env.NODE_VERSION }}
184+
- uses: actions/cache/restore@v4
185+
with:
186+
path: |
187+
node_modules
188+
~/.local/share/pnpm/store
189+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
95190
- run: pnpm install --frozen-lockfile
96191
- run: pnpm vite build
192+
193+
e2e-test:
194+
name: E2E Tests
195+
runs-on: ubuntu-latest
196+
environment: preview
197+
needs: [build]
198+
steps:
199+
- uses: actions/checkout@v4
200+
- uses: pnpm/action-setup@v4
201+
- uses: actions/setup-node@v4
202+
with:
203+
node-version: ${{ env.NODE_VERSION }}
204+
- uses: actions/cache/restore@v4
205+
with:
206+
path: |
207+
node_modules
208+
~/.local/share/pnpm/store
209+
key: ${{ env.STORE_PATH_KEY }}${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
210+
- run: pnpm install --frozen-lockfile
211+
- run: npx playwright install --with-deps chromium
212+
- run: pnpm test:e2e
213+
env:
214+
VITE_CONVEX_URL: ${{ secrets.VITE_CONVEX_URL }}
215+
VITE_CONVEX_SITE_URL: ${{ secrets.VITE_CONVEX_SITE_URL }}
216+
VITE_SITE_URL: ${{ secrets.VITE_SITE_URL }}
217+
E2E_TEST_EMAIL: ${{ secrets.E2E_TEST_EMAIL }}
218+
E2E_TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }}
219+
E2E_PLAYER_EMAIL: ${{ secrets.E2E_PLAYER_EMAIL }}
220+
E2E_PLAYER_PASSWORD: ${{ secrets.E2E_PLAYER_PASSWORD }}
221+
TEST_RUN_ID: ${{ github.run_id }}-${{ github.run_attempt }}
222+
- uses: actions/upload-artifact@v4
223+
if: ${{ !cancelled() }}
224+
with:
225+
name: playwright-report
226+
path: playwright-report/
227+
retention-days: 14

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ yarn.lock
2424
# Sentry Config File
2525
.env.sentry-build-plugin
2626
/test-results/
27-
/playwright-report/
2827
/blob-report/
2928
/playwright/.cache/
29+
.playwright-mcp/*
3030

3131
app.config.timestamp_*.js
3232

3333

34+
e2e/.auth/
35+
playwright-report/
36+
coverage/
37+
3438
.claude/
3539

convex/_test/assertions.helper.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { expect } from 'vitest'
2+
import { ConvexError } from 'convex/values'
3+
import { isClientError } from '../errors'
4+
import type { ClientErrorCode } from '../errors'
5+
6+
export async function expectClientError(
7+
promise: Promise<unknown>,
8+
code: ClientErrorCode,
9+
) {
10+
try {
11+
await promise
12+
expect.fail(
13+
`Expected ConvexError with code ${code}, but no error was thrown`,
14+
)
15+
} catch (error) {
16+
if (error instanceof ConvexError && isClientError(error, code)) {
17+
return error
18+
}
19+
if (
20+
error instanceof Error &&
21+
'data' in error &&
22+
isClientError(error, code)
23+
) {
24+
return error
25+
}
26+
throw error
27+
}
28+
}
29+
30+
export function expectNotAuthenticated(promise: Promise<unknown>) {
31+
return expectClientError(promise, 'NOT_AUTHENTICATED')
32+
}
33+
34+
export function expectPermissionDenied(promise: Promise<unknown>) {
35+
return expectClientError(promise, 'PERMISSION_DENIED')
36+
}
37+
38+
export function expectNotFound(promise: Promise<unknown>) {
39+
return expectClientError(promise, 'NOT_FOUND')
40+
}
41+
42+
export function expectValidationFailed(promise: Promise<unknown>) {
43+
return expectClientError(promise, 'VALIDATION_FAILED')
44+
}
45+
46+
export function expectConflict(promise: Promise<unknown>) {
47+
return expectClientError(promise, 'CONFLICT')
48+
}

0 commit comments

Comments
 (0)