8.3 KiB
Testing
Last updated: 2026-05-14
Scope: Turborepo monorepo at /Users/arthur.belleville/Documents/perso/projects/xtablo-source
Frameworks
- Vitest is the only JS/TS test runner across every app and package (no Jest)
- React Testing Library (
@testing-library/react) for component tests;@testing-library/jest-dommatchers loaded via setup files - happy-dom / jsdom for DOM emulation in frontend tests (
apps/main/vite.config.tsconfiguresenvironment: "jsdom") - Vitest with mocked Supabase for
apps/api— node environment, real Hono router exercised, Supabase client stubbed (apps/api/src/__tests__/setup.ts+globalSetup.ts) - pgTAP SQL tests under
supabase/tests/database/*.test.sqlfor schema/RLS/trigger correctness (run via the Supabase CLI, separate from Vitest)
Test Commands
From the repo root (all dispatched by Turborepo via turbo.json):
pnpm test # Run every package's `test` task
pnpm test:watch # Watch mode across the workspace
pnpm test:api # Run only @xtablo/api (turbo --filter)
cd apps/main && pnpm test # Run a single package directly
cd apps/api && pnpm test:watch
Per-package script conventions (see e.g. apps/api/package.json):
"test": "NODE_ENV=test vitest run""test:watch": "NODE_ENV=test vitest"- Frontend apps similarly use
vitest run/vitest(noNODE_ENV=testprefix required)
Test File Location
Tests are co-located with the source they exercise:
- React components:
Foo.tsx+Foo.test.tsxin the same folder- e.g.
apps/main/src/components/CustomModal.test.tsx,apps/main/src/components/NavigationBar.test.tsx
- e.g.
- Hooks / contexts:
useFoo.ts+useFoo.test.ts(x)- e.g.
apps/main/src/contexts/UpgradeBlockContext.test.tsx
- e.g.
- API: tests under
apps/api/src/__tests__/<area>/<area>.test.ts(grouped by router/concern), plus co-located helper tests likeapps/api/src/helpers/orgIcons.test.ts - Expo app sometimes uses
__tests__/folders:xtablo-expo/components/__tests__/BillingPaywall.test.tsx
Vitest Configs
Two distinct flavors live in the repo:
-
Frontend (Vite + Vitest) — config embedded inside
vite.config.ts. Example,apps/main/vite.config.ts:test: { globals: true, environment: "jsdom", setupFiles: "./src/setupTests.ts", }The Cloudflare plugin is skipped when
process.env.VITEST === "true".VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEYare stubbed viadefinewhen running under Vitest.Other apps with the same pattern:
apps/admin/vite.config.ts+apps/admin/src/setupTests.tsapps/external/vite.config.ts+apps/external/src/setupTests.tsapps/clients/vite.config.ts+apps/clients/src/setupTests.ts
-
API (standalone Vitest) — dedicated
apps/api/vitest.config.ts:test: { globals: true, environment: "node", setupFiles: ["./src/__tests__/setup.ts"], globalSetup: ["./src/__tests__/globalSetup.ts"], testTimeout: 30000, hookTimeout: 60000, include: ["src/__tests__/**/*.test.ts", "src/**/*.test.ts"], pool: "forks", fileParallelism: false, }fileParallelism: falseis deliberate — tests share initialized middleware state and can't safely run concurrently across files.
Setup Files
apps/main/src/setupTests.ts— imports@testing-library/jest-dom, registers anafterEach(cleanup), mocksResizeObserver,Element.prototype.scrollIntoView,Element.prototype.scrollTo, andwindow.matchMedia. Also imports./i18n.testto bootstrap i18next for tests.apps/admin/src/setupTests.ts,apps/external/src/setupTests.ts,apps/clients/src/setupTests.ts— analogous per-app setupapps/api/src/__tests__/setup.ts— minimal per-test-file setup (DB init handled byglobalSetup.ts)apps/api/src/__tests__/globalSetup.ts— boots the test middleware manager viacreateConfig()reading.env.test
Mocking Patterns
- Frontend component tests import the component and render it with a
renderWithProvidershelper (seeapps/main/src/utils/testHelpers.tsxandapps/clients/src/test/testHelpers.test.tsx) that wires QueryClient, router, i18n, and Zustand stores. - React Query mocking: integration tests mock the hook return values directly with
vi.mock(...)and inject{ data, isLoading, error }shapes. - Supabase client: API tests stub
supabase.from(...)chains viavi.fn()— fixtures live inapps/api/src/__tests__/fixtures/. - Auth: API middleware tests bypass real Supabase auth by overriding the Bearer-token validator and asserting on 401 paths (see
docs/MIDDLEWARE_TESTS.md). - Toast / window APIs: stubbed globally in
setupTests.tsso individual tests don't have to. - i18n: each frontend test setup imports
./i18n.testsouseTranslation()works without network.
API Test Patterns
Documented in docs/API_TESTS.md and docs/MIDDLEWARE_TESTS.md. Key conventions:
- Tests live under
apps/api/src/__tests__/<area>/<area>.test.ts(e.g.notes/notes.test.ts,tasks/tasks.test.ts) - Each router has at minimum a smoke test that verifies the endpoint is reachable and returns the right shape
- Middleware is tested independently in
apps/api/src/__tests__/middlewares/middlewares.test.ts— auth header validation, Supabase client injection, R2/Stream/email middleware behavior - Test mode is detected via
NODE_ENV=testincreateConfig()so secrets load from.env.testinstead of Google Secret Manager — no GCP credentials required to run the suite - Fixtures (sample DB rows, sample Stripe payloads) live in
apps/api/src/__tests__/fixtures/ - Helpers (token mocking, app builders) live in
apps/api/src/__tests__/helpers/ - The
__tests__/README.mddocuments the suite layout in-tree
Snapshot from docs/MIDDLEWARE_TESTS.md: 116 passing tests across the API suite at last documented run (2025-11-10), with explicit coverage for the Supabase, Auth, Stream, R2, and email middlewares.
Coverage
- No coverage threshold is enforced in CI today —
vitest.config.tsfiles do not declarecoverageblocks andpackage.jsonhas notest:coveragescript at the root. - Coverage can still be produced ad-hoc with
vitest run --coveragein any individual package, but it's not part of the normal workflow. - The repo relies on (a) Biome lint errors blocking CI, (b)
pnpm typecheck(tsc -b) blocking CI, and (c)pnpm testblocking CI — coverage % is currently advisory only.
Existing Test Inventory
Total Vitest/RTL test files (*.test.ts(x), excluding node_modules, dist): 147
Breakdown by area (approximate):
apps/main/src/**/*.test.tsx— ~64 (components, contexts, providers, hooks)apps/api/src/**/*.test.ts— ~31 (routers, middlewares, helpers)apps/admin/src/**/*.test.tsx— pages, components, lib (e.g.AnalyticsStudioPage.test.tsx,PrivilegedGate.test.tsx)apps/clients/src/**/*.test.ts(x)— env + Vite config sanity checks plus page testsapps/external/src/viteConfig.test.ts,apps/main/src/viteConfig.test.ts— Vite build config smoke testsxtablo-expo/**/*.test.ts(x)— React Native (Expo) tests (e.g.auth.test.ts,BillingPaywall.test.tsx)supabase/tests/database/*.test.sql— 13 pgTAP files covering schema, RLS, triggers, indexes, Stripe + Apple billing functions
Representative examples worth reading first:
apps/main/src/components/CustomModal.test.tsx— minimal RTL testapps/main/src/components/NavigationBar.test.tsx— usesrenderWithProvidersapps/main/src/providers/UserStoreProvider.test.tsx— Zustand + React Query store testapps/main/src/contexts/UpgradeBlockContext.test.tsx— context provider testapps/api/src/helpers/orgIcons.test.ts— pure-helper unit testapps/api/src/__tests__/middlewares/middlewares.test.ts— middleware integration suitesupabase/tests/database/02_rls_policies_core.test.sql— pgTAP RLS coverage
Reference Documents
docs/API_TESTS.md— API router test catalog, env setup, known limitationsdocs/MIDDLEWARE_TESTS.md— middleware-by-middleware coverage notesdocs/ENV_TEST_SETUP.md—.env.teststructuredocs/TEST_FIXES.md,docs/TEST_ROUTER_REFACTOR.md— historical notes on test infrastructure changesdocs/TESTING_WITH_FAKE_ACCOUNTS.md— temporary-account flow for manual + integration testing