Skip to content

Commit 5bb97b3

Browse files
committed
2 parents 0dbc131 + 214d9b8 commit 5bb97b3

401 files changed

Lines changed: 37108 additions & 16037 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/test.yml

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
name: Test Suite
2+
3+
on:
4+
pull_request:
5+
branches: [main, dev, dev-*]
6+
types: [opened, synchronize, reopened]
7+
push:
8+
branches: [main]
9+
10+
# Cancel in-progress runs when a new workflow with the same name is triggered
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
13+
cancel-in-progress: true
14+
15+
permissions:
16+
contents: read
17+
pull-requests: write
18+
issues: write
19+
20+
jobs:
21+
# Unit and Integration Tests
22+
unit-tests:
23+
name: Unit & Integration Tests
24+
runs-on: ubuntu-latest
25+
timeout-minutes: 15
26+
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v4
30+
31+
- name: Setup Node.js
32+
uses: actions/setup-node@v4
33+
with:
34+
node-version: '20'
35+
cache: 'npm'
36+
37+
- name: Install dependencies
38+
run: npm ci
39+
40+
- name: Run unit and integration tests
41+
run: npm test -- --run --reporter=verbose
42+
env:
43+
CI: true
44+
45+
- name: Upload test results
46+
if: always()
47+
uses: actions/upload-artifact@v4
48+
with:
49+
name: unit-test-results
50+
path: |
51+
coverage/
52+
test-results/
53+
retention-days: 7
54+
55+
# E2E Tests
56+
e2e-tests:
57+
name: E2E Tests (Playwright)
58+
runs-on: ubuntu-latest
59+
timeout-minutes: 30
60+
61+
steps:
62+
- name: Checkout code
63+
uses: actions/checkout@v4
64+
65+
- name: Setup Node.js
66+
uses: actions/setup-node@v4
67+
with:
68+
node-version: '20'
69+
cache: 'npm'
70+
71+
- name: Install dependencies
72+
run: npm ci
73+
74+
- name: Install Playwright browsers
75+
run: npx playwright install --with-deps chromium
76+
77+
- name: Start dev server
78+
run: |
79+
npm run dev &
80+
npx wait-on http://localhost:8080 --timeout 60000
81+
env:
82+
CI: true
83+
84+
- name: Setup Supabase CLI
85+
uses: supabase/setup-cli@v1
86+
with:
87+
version: latest
88+
89+
- name: Start Supabase local
90+
run: |
91+
supabase start
92+
env:
93+
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
94+
95+
- name: Run Playwright tests
96+
run: npx playwright test --project=chromium
97+
env:
98+
CI: true
99+
# Add any required env variables for e2e tests
100+
VITE_SUPABASE_URL: http://127.0.0.1:54321
101+
VITE_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
102+
103+
- name: Upload Playwright report
104+
if: always()
105+
uses: actions/upload-artifact@v4
106+
with:
107+
name: playwright-report
108+
path: playwright-report/
109+
retention-days: 7
110+
111+
- name: Upload test screenshots
112+
if: failure()
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: playwright-screenshots
116+
path: test-results/
117+
retention-days: 7
118+
119+
- name: Stop Supabase
120+
if: always()
121+
run: supabase stop
122+
123+
# Type Check
124+
type-check:
125+
name: TypeScript Type Check
126+
runs-on: ubuntu-latest
127+
timeout-minutes: 10
128+
129+
steps:
130+
- name: Checkout code
131+
uses: actions/checkout@v4
132+
133+
- name: Setup Node.js
134+
uses: actions/setup-node@v4
135+
with:
136+
node-version: '20'
137+
cache: 'npm'
138+
139+
- name: Install dependencies
140+
run: npm ci
141+
142+
- name: Run type check
143+
run: npm run type-check || npx tsc --noEmit
144+
145+
# Lint Check
146+
lint:
147+
name: ESLint Check
148+
runs-on: ubuntu-latest
149+
timeout-minutes: 10
150+
151+
steps:
152+
- name: Checkout code
153+
uses: actions/checkout@v4
154+
155+
- name: Setup Node.js
156+
uses: actions/setup-node@v4
157+
with:
158+
node-version: '20'
159+
cache: 'npm'
160+
161+
- name: Install dependencies
162+
run: npm ci
163+
164+
- name: Run ESLint
165+
run: npm run lint || npx eslint . --ext .ts,.tsx,.js,.jsx
166+
167+
# Build Check
168+
build:
169+
name: Build Check
170+
runs-on: ubuntu-latest
171+
timeout-minutes: 15
172+
173+
steps:
174+
- name: Checkout code
175+
uses: actions/checkout@v4
176+
177+
- name: Setup Node.js
178+
uses: actions/setup-node@v4
179+
with:
180+
node-version: '20'
181+
cache: 'npm'
182+
183+
- name: Install dependencies
184+
run: npm ci
185+
186+
- name: Build project
187+
run: npm run build
188+
env:
189+
CI: true
190+
191+
- name: Check build output
192+
run: |
193+
if [ ! -d "dist" ]; then
194+
echo "Build failed: dist directory not created"
195+
exit 1
196+
fi
197+
198+
# Status Check - Required for PR merging
199+
test-status:
200+
name: Test Status Check
201+
runs-on: ubuntu-latest
202+
needs: [unit-tests, e2e-tests, type-check, lint, build]
203+
if: always()
204+
205+
steps:
206+
- name: Check test results
207+
run: |
208+
if [ "${{ needs.unit-tests.result }}" != "success" ] || \
209+
[ "${{ needs.e2e-tests.result }}" != "success" ] || \
210+
[ "${{ needs.type-check.result }}" != "success" ] || \
211+
[ "${{ needs.lint.result }}" != "success" ] || \
212+
[ "${{ needs.build.result }}" != "success" ]; then
213+
echo "❌ One or more test jobs failed"
214+
echo "Unit Tests: ${{ needs.unit-tests.result }}"
215+
echo "E2E Tests: ${{ needs.e2e-tests.result }}"
216+
echo "Type Check: ${{ needs.type-check.result }}"
217+
echo "Lint: ${{ needs.lint.result }}"
218+
echo "Build: ${{ needs.build.result }}"
219+
exit 1
220+
else
221+
echo "✅ All tests passed!"
222+
exit 0
223+
fi
224+
225+
- name: Post status comment
226+
if: github.event_name == 'pull_request'
227+
uses: actions/github-script@v7
228+
with:
229+
script: |
230+
const status = {
231+
unit: '${{ needs.unit-tests.result }}',
232+
e2e: '${{ needs.e2e-tests.result }}',
233+
typeCheck: '${{ needs.type-check.result }}',
234+
lint: '${{ needs.lint.result }}',
235+
build: '${{ needs.build.result }}'
236+
};
237+
238+
const icon = (result) => result === 'success' ? '✅' : '❌';
239+
240+
const comment = `## 🧪 Test Results
241+
242+
| Job | Status |
243+
|-----|--------|
244+
| Unit & Integration Tests | ${icon(status.unit)} ${status.unit} |
245+
| E2E Tests (Playwright) | ${icon(status.e2e)} ${status.e2e} |
246+
| TypeScript Check | ${icon(status.typeCheck)} ${status.typeCheck} |
247+
| ESLint | ${icon(status.lint)} ${status.lint} |
248+
| Build | ${icon(status.build)} ${status.build} |
249+
250+
${Object.values(status).every(s => s === 'success') ? '✅ **All checks passed!** Ready to merge.' : '❌ **Some checks failed.** Please fix the issues before merging.'}`;
251+
252+
github.rest.issues.createComment({
253+
issue_number: context.issue.number,
254+
owner: context.repo.owner,
255+
repo: context.repo.repo,
256+
body: comment
257+
});

docs/ARCHITECTURE_GUIDE.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Architecture Guide
2+
3+
This repo uses a **feature-first** folder structure.
4+
5+
## High-level map
6+
7+
- `src/features/<feature>/...`
8+
- Feature-owned UI, hooks, services, and (optionally) feature-local tests.
9+
- `src/pages/*`
10+
- Thin route wrappers that re-export the corresponding feature page.
11+
- `src/components/*`
12+
- Shared UI and app-level components used by multiple features (layout, shared widgets).
13+
- `src/components/ui/*` is the shared UI kit.
14+
- `src/hooks/*`
15+
- Cross-feature hooks (auth, subscription, theme, generic infra hooks).
16+
- `src/services/*`
17+
- Cross-feature services (analytics, test runner, editor bounds, etc.).
18+
- `src/shared/*`
19+
- Shared “app infrastructure” (e.g. notification service, shared queries) that is not feature-owned.
20+
- `src/types/*`
21+
- Cross-feature shared types.
22+
23+
## What goes where (rules of thumb)
24+
25+
### 1) Creating a new feature
26+
Create a folder:
27+
28+
- `src/features/<feature>/`
29+
30+
Common subfolders:
31+
32+
- `src/features/<feature>/components/`
33+
- `src/features/<feature>/hooks/`
34+
- `src/features/<feature>/services/`
35+
- `src/features/<feature>/types/` (only if types are feature-private)
36+
37+
Feature pages:
38+
39+
- `src/features/<feature>/<Feature>Page.tsx` (or `<Feature>HubPage.tsx`, `<Feature>SolverPage.tsx`, etc.)
40+
41+
Then create a thin wrapper route:
42+
43+
- `src/pages/<Feature>.tsx` that simply `export { default } from "@/features/<feature>/<...>";`
44+
45+
### 2) Creating a new component
46+
Decide ownership first:
47+
48+
- **Feature-owned component**: put it in `src/features/<feature>/components/`.
49+
- Examples: Survey steps, System Design canvas UI, Behavioral interview UI.
50+
- **Shared component** (reused by multiple features): put it in `src/components/`.
51+
- Examples: `Sidebar`, `ConfirmDialog`, shared editor/diagram components.
52+
- **UI primitives** (buttons, dialog, tabs, etc.): keep in `src/components/ui/`.
53+
54+
### 3) Creating a new hook
55+
56+
- **Feature-specific behavior/data**: `src/features/<feature>/hooks/`.
57+
- **Cross-feature / infrastructure** (auth, subscription, theme, localStorage helpers, etc.): `src/hooks/`.
58+
59+
### 4) Creating a new service
60+
61+
- **Feature-specific persistence/integration**: `src/features/<feature>/services/`.
62+
- **Cross-feature infrastructure**: `src/services/`.
63+
64+
### 5) Shared utilities and types
65+
66+
- If multiple features use it:
67+
- Types: `src/types/*`
68+
- Utilities: `src/lib/*` or `src/utils/*`
69+
- Shared services: `src/shared/*` or `src/services/*` depending on responsibility
70+
71+
## Import boundaries (recommended)
72+
73+
- Features can import from:
74+
- `@/components/*` (shared UI)
75+
- `@/components/ui/*`
76+
- `@/hooks/*` (cross-feature)
77+
- `@/services/*` (cross-feature)
78+
- `@/shared/*`, `@/types/*`, `@/utils/*`, `@/lib/*`
79+
- Their own feature code: `@/features/<feature>/*`
80+
81+
- Shared code (`src/components`, `src/hooks`, `src/services`) should **avoid** importing from feature folders when possible.
82+
- Exception: “shared dashboard widgets” may currently import feature hooks (e.g. `useProblems`). If these are actually dashboard-owned, prefer moving them under `src/features/dashboard/components/*`.
83+
84+
## Testing conventions
85+
86+
This repo currently uses a **mixed** approach:
87+
88+
### A) Centralized test folders (current common pattern)
89+
90+
- Shared hooks: `src/hooks/__tests__/*`
91+
- Shared services: `src/services/__tests__/*`
92+
- Shared components: `src/components/__tests__/*`
93+
94+
This works well for cross-feature code.
95+
96+
### B) Co-located tests (recommended for new feature code)
97+
98+
For feature-owned code, prefer co-location:
99+
100+
- `src/features/<feature>/components/__tests__/*`
101+
- `src/features/<feature>/hooks/__tests__/*`
102+
- `src/features/<feature>/services/__tests__/*`
103+
104+
Example already in repo:
105+
106+
- `src/features/survey/components/steps/__tests__/PaywallStep.test.tsx`
107+
108+
### Suggested rule going forward
109+
110+
- **If the code is feature-owned**: co-locate tests under that feature.
111+
- **If the code is shared cross-feature**: keep tests in the centralized `src/<area>/__tests__` folders.
112+
- Avoid testing `src/pages/*` wrappers; test the feature pages instead.
113+
114+
## Known follow-up opportunities (optional cleanups)
115+
116+
These are not required for correctness, but they improve “clean architecture”:
117+
118+
- **Dashboard widgets in `src/components/*`**
119+
- Several dashboard-centric components in `src/components` import feature hooks (e.g. `useProblems`). Consider moving them into `src/features/dashboard/components/*`.
120+
- **Admin access logic duplication**
121+
- `ADMIN_EMAILS` is duplicated in `Sidebar` and `AdminRoute`. Consider extracting to a single module (e.g. `src/features/admin/constants.ts` or `src/shared/auth/adminAccess.ts`).
122+
123+
## Quick checklist when adding code
124+
125+
- Is it feature-owned?
126+
- Yes -> `src/features/<feature>/...`
127+
- No -> shared folders (`src/components`, `src/hooks`, `src/services`, `src/shared`)
128+
- Does it need a route?
129+
- Add `src/pages/<RouteName>.tsx` wrapper
130+
- Does it need tests?
131+
- Feature-owned -> co-locate under the feature
132+
- Shared -> keep in centralized `__tests__` folder

0 commit comments

Comments
 (0)