@react-spa-scaffold/mcp 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +2 -1
  2. package/dist/constants.d.ts +4 -0
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +4 -0
  5. package/dist/constants.js.map +1 -1
  6. package/dist/features/definitions/auth.d.ts +3 -0
  7. package/dist/features/definitions/auth.d.ts.map +1 -0
  8. package/dist/features/definitions/auth.js +17 -0
  9. package/dist/features/definitions/auth.js.map +1 -0
  10. package/dist/features/definitions/core.d.ts.map +1 -1
  11. package/dist/features/definitions/core.js +16 -1
  12. package/dist/features/definitions/core.js.map +1 -1
  13. package/dist/features/definitions/database.d.ts +3 -0
  14. package/dist/features/definitions/database.d.ts.map +1 -0
  15. package/dist/features/definitions/database.js +45 -0
  16. package/dist/features/definitions/database.js.map +1 -0
  17. package/dist/features/definitions/deployment.d.ts +3 -0
  18. package/dist/features/definitions/deployment.d.ts.map +1 -0
  19. package/dist/features/definitions/deployment.js +14 -0
  20. package/dist/features/definitions/deployment.js.map +1 -0
  21. package/dist/features/definitions/forms.d.ts.map +1 -1
  22. package/dist/features/definitions/forms.js +4 -0
  23. package/dist/features/definitions/forms.js.map +1 -1
  24. package/dist/features/definitions/index.d.ts +3 -0
  25. package/dist/features/definitions/index.d.ts.map +1 -1
  26. package/dist/features/definitions/index.js +3 -0
  27. package/dist/features/definitions/index.js.map +1 -1
  28. package/dist/features/definitions/mobile.d.ts.map +1 -1
  29. package/dist/features/definitions/mobile.js +11 -2
  30. package/dist/features/definitions/mobile.js.map +1 -1
  31. package/dist/features/definitions/observability.js +1 -1
  32. package/dist/features/definitions/observability.js.map +1 -1
  33. package/dist/features/definitions/routing.d.ts.map +1 -1
  34. package/dist/features/definitions/routing.js +2 -1
  35. package/dist/features/definitions/routing.js.map +1 -1
  36. package/dist/features/definitions/state.d.ts.map +1 -1
  37. package/dist/features/definitions/state.js +9 -2
  38. package/dist/features/definitions/state.js.map +1 -1
  39. package/dist/features/definitions/testing.d.ts.map +1 -1
  40. package/dist/features/definitions/testing.js +4 -2
  41. package/dist/features/definitions/testing.js.map +1 -1
  42. package/dist/features/registry.d.ts.map +1 -1
  43. package/dist/features/registry.js +4 -1
  44. package/dist/features/registry.js.map +1 -1
  45. package/dist/features/types.test.js +6 -2
  46. package/dist/features/types.test.js.map +1 -1
  47. package/dist/resources/docs.d.ts.map +1 -1
  48. package/dist/resources/docs.js +5 -0
  49. package/dist/resources/docs.js.map +1 -1
  50. package/dist/tools/add-features.js +1 -1
  51. package/dist/tools/add-features.js.map +1 -1
  52. package/dist/utils/docs.d.ts.map +1 -1
  53. package/dist/utils/docs.js +2 -0
  54. package/dist/utils/docs.js.map +1 -1
  55. package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -1
  56. package/dist/utils/scaffold/claude-md/index.js +3 -1
  57. package/dist/utils/scaffold/claude-md/index.js.map +1 -1
  58. package/dist/utils/scaffold/claude-md/sections.d.ts +2 -0
  59. package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -1
  60. package/dist/utils/scaffold/claude-md/sections.js +132 -2
  61. package/dist/utils/scaffold/claude-md/sections.js.map +1 -1
  62. package/dist/utils/scaffold/compute.js +1 -1
  63. package/dist/utils/scaffold/compute.js.map +1 -1
  64. package/dist/utils/scaffold/generators.d.ts +2 -2
  65. package/dist/utils/scaffold/generators.d.ts.map +1 -1
  66. package/dist/utils/scaffold/generators.js +64 -22
  67. package/dist/utils/scaffold/generators.js.map +1 -1
  68. package/package.json +1 -1
  69. package/templates/.env.example +44 -10
  70. package/templates/.github/workflows/ci.yml +12 -4
  71. package/templates/.github/workflows/deploy.yml +59 -0
  72. package/templates/CLAUDE.md +251 -2
  73. package/templates/docs/ARCHITECTURE.md +13 -12
  74. package/templates/docs/AUTHENTICATION.md +325 -0
  75. package/templates/docs/CODING_STANDARDS.md +65 -0
  76. package/templates/docs/DEPLOYMENT.md +268 -0
  77. package/templates/docs/E2E_TESTING.md +133 -11
  78. package/templates/docs/SUPABASE_INTEGRATION.md +310 -0
  79. package/templates/docs/TESTING.md +195 -77
  80. package/templates/e2e/auth/auth.setup.ts +60 -0
  81. package/templates/e2e/fixtures/index.ts +24 -2
  82. package/templates/e2e/tests/profile.auth.spec.ts +103 -0
  83. package/templates/e2e/tests/profile.spec.ts +64 -0
  84. package/templates/e2e/tests/register-form.spec.ts +38 -0
  85. package/templates/gitignore +5 -0
  86. package/templates/package.json +15 -3
  87. package/templates/playwright.config.ts +39 -4
  88. package/templates/src/App.tsx +32 -19
  89. package/templates/src/components/layout/Header.test.tsx +17 -1
  90. package/templates/src/components/layout/Header.tsx +13 -1
  91. package/templates/src/components/shared/AccountButton/AccountButton.test.tsx +30 -0
  92. package/templates/src/components/shared/AccountButton/AccountButton.tsx +38 -0
  93. package/templates/src/components/shared/AccountButton/index.ts +1 -0
  94. package/templates/src/components/shared/ErrorBoundary/ErrorBoundary.test.tsx +4 -4
  95. package/templates/src/components/shared/ErrorBoundary/ErrorBoundary.tsx +55 -53
  96. package/templates/src/components/shared/ProfileSync/ProfileSync.test.tsx +44 -0
  97. package/templates/src/components/shared/ProfileSync/ProfileSync.tsx +104 -0
  98. package/templates/src/components/shared/ProfileSync/index.ts +1 -0
  99. package/templates/src/components/shared/ProtectedRoute/ProtectedRoute.test.tsx +43 -0
  100. package/templates/src/components/shared/ProtectedRoute/ProtectedRoute.tsx +35 -0
  101. package/templates/src/components/shared/ProtectedRoute/index.ts +1 -0
  102. package/templates/src/components/shared/index.ts +5 -2
  103. package/templates/src/contexts/clerkContext.tsx +45 -0
  104. package/templates/src/contexts/performanceContext.tsx +3 -3
  105. package/templates/src/contexts/supabaseContext.test.tsx +59 -0
  106. package/templates/src/contexts/supabaseContext.tsx +87 -0
  107. package/templates/src/hooks/index.ts +40 -2
  108. package/templates/src/hooks/supabase/index.ts +12 -0
  109. package/templates/src/hooks/supabase/useProfiles.test.tsx +207 -0
  110. package/templates/src/hooks/supabase/useProfiles.ts +213 -0
  111. package/templates/src/hooks/supabase/useSupabaseQuery.test.tsx +150 -0
  112. package/templates/src/hooks/supabase/useSupabaseQuery.ts +91 -0
  113. package/templates/src/hooks/useCopyFeedback.test.ts +129 -0
  114. package/templates/src/hooks/useCopyFeedback.ts +41 -0
  115. package/templates/src/hooks/useDebouncedCallback.test.ts +164 -0
  116. package/templates/src/hooks/useDebouncedCallback.ts +47 -0
  117. package/templates/src/hooks/useDocumentTitle.test.ts +59 -0
  118. package/templates/src/hooks/useDocumentTitle.ts +31 -0
  119. package/templates/src/hooks/useIOSViewportReset.test.ts +58 -0
  120. package/templates/src/hooks/useIOSViewportReset.ts +18 -0
  121. package/templates/src/hooks/useKeyboardShortcut.test.ts +86 -0
  122. package/templates/src/hooks/useKeyboardShortcuts.ts +44 -0
  123. package/templates/src/hooks/useLocalStorage.test.ts +111 -0
  124. package/templates/src/hooks/useLocalStorage.ts +77 -0
  125. package/templates/src/hooks/useSyncedFormData.test.ts +75 -0
  126. package/templates/src/hooks/useSyncedFormData.ts +21 -0
  127. package/templates/src/hooks/useSyncedState.test.ts +119 -0
  128. package/templates/src/hooks/useSyncedState.ts +30 -0
  129. package/templates/src/index.css +1 -0
  130. package/templates/src/lib/api.test.ts +30 -38
  131. package/templates/src/lib/api.ts +1 -7
  132. package/templates/src/lib/config.ts +54 -4
  133. package/templates/src/lib/constants.ts +10 -0
  134. package/templates/src/lib/createSelectors.test.ts +136 -0
  135. package/templates/src/lib/createSelectors.ts +31 -0
  136. package/templates/src/lib/env.ts +36 -14
  137. package/templates/src/lib/index.ts +5 -2
  138. package/templates/src/lib/routes.ts +1 -0
  139. package/templates/src/lib/sentry.ts +58 -0
  140. package/templates/src/lib/storage.ts +6 -2
  141. package/templates/src/lib/supabase/client.ts +58 -0
  142. package/templates/src/lib/supabase/index.ts +5 -0
  143. package/templates/src/main.tsx +19 -31
  144. package/templates/src/mocks/constants.ts +31 -0
  145. package/templates/src/mocks/fixtures/index.ts +3 -1
  146. package/templates/src/mocks/fixtures/profiles.ts +55 -0
  147. package/templates/src/mocks/fixtures/users.ts +91 -0
  148. package/templates/src/mocks/handlers/index.ts +2 -1
  149. package/templates/src/mocks/handlers/supabase.ts +64 -0
  150. package/templates/src/mocks/handlers/todos.ts +1 -1
  151. package/templates/src/mocks/index.ts +6 -0
  152. package/templates/src/pages/Profile.test.tsx +263 -0
  153. package/templates/src/pages/Profile.tsx +171 -0
  154. package/templates/src/pages/index.ts +1 -0
  155. package/templates/src/stores/preferencesStore.ts +35 -9
  156. package/templates/src/test/clerkMock.tsx +137 -0
  157. package/templates/src/test/fetchMock.ts +58 -0
  158. package/templates/src/test/index.ts +51 -2
  159. package/templates/src/test/mocks.ts +128 -1
  160. package/templates/src/test/providers.tsx +10 -4
  161. package/templates/src/test/supabaseMock.ts +112 -0
  162. package/templates/src/test-setup.ts +42 -2
  163. package/templates/src/types/database.ts +46 -0
  164. package/templates/src/types/index.ts +1 -0
  165. package/templates/src/types/supabase.ts +167 -0
  166. package/templates/src/vite-env.d.ts +6 -0
  167. package/templates/supabase/migrations/20260104000000_create_profiles_table.sql +67 -0
  168. package/templates/vitest.config.ts +9 -1
@@ -19,6 +19,71 @@ interface User {
19
19
 
20
20
  See [Architecture Guide](./ARCHITECTURE.md#state-management) for when to use each solution.
21
21
 
22
+ ### Zustand Best Practices
23
+
24
+ **Auto-generated selectors**: All stores use `createSelectors` for cleaner access:
25
+
26
+ ```tsx
27
+ // Store definition
28
+ const useStoreBase = create<State>()(/* ... */);
29
+ export const useStore = createSelectors(useStoreBase);
30
+
31
+ // Component usage - auto-generated selectors
32
+ const count = useStore.use.count();
33
+ const increment = useStore.use.increment();
34
+ ```
35
+
36
+ **Use `useShallow` for multiple values**: Prevents unnecessary re-renders:
37
+
38
+ ```tsx
39
+ import { useShallow } from 'zustand/react/shallow';
40
+
41
+ // Group state values with useShallow
42
+ const { searchQuery, sortBy } = useStore(
43
+ useShallow((s) => ({
44
+ searchQuery: s.searchQuery,
45
+ sortBy: s.sortBy,
46
+ })),
47
+ );
48
+ ```
49
+
50
+ **Persist versioning**: Always include version and migrate for persisted stores:
51
+
52
+ ```tsx
53
+ persist(
54
+ (set, get) => ({
55
+ /* ... */
56
+ }),
57
+ {
58
+ name: 'store-key',
59
+ version: 1, // Increment on breaking changes
60
+ migrate: (persisted, version) => {
61
+ if (version === 0) {
62
+ return { ...persisted, newField: 'default' };
63
+ }
64
+ return persisted;
65
+ },
66
+ },
67
+ );
68
+ ```
69
+
70
+ **Middleware order**: Stack middlewares correctly:
71
+
72
+ ```tsx
73
+ // devtools → persist → subscribeWithSelector → store
74
+ create<State>()(
75
+ devtools(
76
+ persist(
77
+ subscribeWithSelector((set, get) => ({
78
+ /* ... */
79
+ })),
80
+ { name: 'key' },
81
+ ),
82
+ { name: 'StoreName', enabled: process.env.NODE_ENV === 'development' },
83
+ ),
84
+ );
85
+ ```
86
+
22
87
  ### Query Hooks
23
88
 
24
89
  Extract the fetcher function:
@@ -0,0 +1,268 @@
1
+ # Deployment Guide
2
+
3
+ Automated deployment to Netlify with GitHub Actions for preview and production environments.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ ```
10
+ PR Created/Updated → GitHub Actions → Build → Netlify Preview
11
+
12
+ Comment with preview URL on PR
13
+
14
+ Push to main → GitHub Actions → Build → Netlify Production
15
+ ```
16
+
17
+ **Features:**
18
+
19
+ - Automatic preview deploys for pull requests
20
+ - Production deploys on push to main/master
21
+ - PR comments with preview URLs
22
+ - Manual deploy via workflow_dispatch
23
+ - Security headers pre-configured
24
+
25
+ **Note:** Enable branch protection rules to require CI to pass before merging to main.
26
+
27
+ ---
28
+
29
+ ## Netlify Setup
30
+
31
+ ### 1. Create Netlify Site
32
+
33
+ 1. Go to [app.netlify.com](https://app.netlify.com) and sign in
34
+ 2. Click "Add new site" → "Import an existing project"
35
+ 3. Connect your GitHub repository
36
+ 4. Configure build settings (auto-detected from `netlify.toml`):
37
+ - Build command: `npm run build`
38
+ - Publish directory: `dist`
39
+ 5. Click "Deploy site"
40
+
41
+ ### 2. Get API Credentials
42
+
43
+ 1. **Personal Access Token** (for `NETLIFY_AUTH_TOKEN`):
44
+ - User Settings → Applications → Personal access tokens
45
+ - Click "New access token", name it, and copy the token
46
+
47
+ 2. **Site ID** (for `NETLIFY_SITE_ID`):
48
+ - Site Settings → General → Site details → Site ID
49
+
50
+ ### 3. Add GitHub Secrets
51
+
52
+ Go to your repository → Settings → Secrets and variables → Actions → New repository secret:
53
+
54
+ | Secret Name | Description |
55
+ | -------------------- | -------------------------------- |
56
+ | `NETLIFY_AUTH_TOKEN` | Personal access token from above |
57
+ | `NETLIFY_SITE_ID` | Site ID from above |
58
+
59
+ ---
60
+
61
+ ## Environment Variables
62
+
63
+ ### Build-Time Variables
64
+
65
+ Set in Netlify Dashboard → Site Settings → Environment variables:
66
+
67
+ | Variable | Required | Description |
68
+ | ---------------------------- | ------------- | --------------------- |
69
+ | `VITE_CLERK_PUBLISHABLE_KEY` | If using auth | Clerk publishable key |
70
+ | `VITE_SUPABASE_DATABASE_URL` | If using db | Supabase project URL |
71
+ | `VITE_SUPABASE_ANON_KEY` | If using db | Supabase anon key |
72
+
73
+ ### Context-Specific Variables
74
+
75
+ Use Netlify CLI to set variables for specific contexts:
76
+
77
+ ```bash
78
+ # Set for all contexts
79
+ netlify env:set VAR_NAME value
80
+
81
+ # Set for production only
82
+ netlify env:set VAR_NAME value --context production
83
+
84
+ # Set for deploy previews only
85
+ netlify env:set VAR_NAME value --context deploy-preview
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Supabase Integration
91
+
92
+ If using the database feature, connect Supabase to Netlify for automatic environment variable sync.
93
+
94
+ ### Extension Setup
95
+
96
+ 1. **Netlify Dashboard** → Extensions → Search "Supabase" → Install
97
+ 2. **Project Settings** → General → Supabase → Connect
98
+ 3. Authorize with Supabase and select your project
99
+ 4. For Vite projects:
100
+ - Framework: Select "Other"
101
+ - Environment variable prefix: Enter `VITE_`
102
+
103
+ ### Auto-Configured Variables
104
+
105
+ After connecting, these are automatically injected:
106
+
107
+ | Variable | Description |
108
+ | ---------------------------- | ---------------------------------------- |
109
+ | `VITE_SUPABASE_DATABASE_URL` | Project URL |
110
+ | `VITE_SUPABASE_ANON_KEY` | Client API key |
111
+ | `SUPABASE_SERVICE_ROLE_KEY` | Server-side only (not exposed to client) |
112
+
113
+ ### Local Development
114
+
115
+ Run `netlify dev` to inject Supabase variables locally:
116
+
117
+ ```bash
118
+ npm install -g netlify-cli
119
+ netlify login
120
+ netlify link # Link to your Netlify site
121
+ netlify dev # Starts dev server with injected env vars
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Preview Deploys
127
+
128
+ Every pull request automatically gets a preview deployment:
129
+
130
+ 1. Open a PR against main/master
131
+ 2. GitHub Actions builds and deploys to Netlify
132
+ 3. Bot comments on PR with preview URL
133
+ 4. Preview updates on each push to the PR
134
+ 5. Preview is deleted when PR is closed
135
+
136
+ ### Preview URL Pattern
137
+
138
+ - PR previews: `https://pr-{number}--{site-name}.netlify.app`
139
+ - Branch deploys: `https://{branch}--{site-name}.netlify.app`
140
+
141
+ ---
142
+
143
+ ## Production Deploys
144
+
145
+ Pushing to main/master triggers production deployment:
146
+
147
+ 1. Deploy workflow builds the app
148
+ 2. Deploys to production URL: `https://your-site.netlify.app`
149
+
150
+ **Important:** Enable branch protection rules on main/master to require CI to pass before merging. This ensures production only gets code that passed all checks.
151
+
152
+ ### Manual Deploys
153
+
154
+ Use workflow_dispatch for manual production deploys:
155
+
156
+ 1. Go to Actions → Deploy → Run workflow
157
+ 2. Select branch
158
+ 3. Click "Run workflow"
159
+
160
+ ---
161
+
162
+ ## Configuration
163
+
164
+ ### netlify.toml
165
+
166
+ The `netlify.toml` file in your project root configures:
167
+
168
+ - **Build settings**: Command and publish directory
169
+ - **Redirects**: SPA fallback to index.html
170
+ - **Headers**: Security headers and caching rules
171
+ - **Context overrides**: Environment-specific settings
172
+
173
+ ### Customizing Headers
174
+
175
+ Add custom headers in `netlify.toml`:
176
+
177
+ ```toml
178
+ [[headers]]
179
+ for = "/api/*"
180
+ [headers.values]
181
+ Access-Control-Allow-Origin = "https://example.com"
182
+ ```
183
+
184
+ ### Redirect Rules
185
+
186
+ Add redirects before the SPA fallback:
187
+
188
+ ```toml
189
+ [[redirects]]
190
+ from = "/old-path"
191
+ to = "/new-path"
192
+ status = 301
193
+
194
+ # SPA fallback (keep last)
195
+ [[redirects]]
196
+ from = "/*"
197
+ to = "/index.html"
198
+ status = 200
199
+ ```
200
+
201
+ ---
202
+
203
+ ## CLI Commands
204
+
205
+ ```bash
206
+ # Install Netlify CLI
207
+ npm install -g netlify-cli
208
+
209
+ # Login and link
210
+ netlify login
211
+ netlify link
212
+
213
+ # Local development with Netlify env vars
214
+ netlify dev
215
+
216
+ # Manual deploys
217
+ npm run deploy:preview # Deploy preview build
218
+ npm run deploy:prod # Deploy to production
219
+
220
+ # Environment variables
221
+ netlify env:list # List all variables
222
+ netlify env:set KEY value # Set variable
223
+ netlify env:get KEY # Get variable value
224
+ netlify env:unset KEY # Remove variable
225
+
226
+ # Build locally
227
+ netlify build # Test production build
228
+ netlify build --context deploy-preview # Test preview build
229
+
230
+ # Check status
231
+ netlify status
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Troubleshooting
237
+
238
+ | Issue | Cause | Solution |
239
+ | ---------------------------------- | ---------------------------- | --------------------------------------------- |
240
+ | Deploy fails with "Site not found" | Missing `NETLIFY_SITE_ID` | Add secret to GitHub repository |
241
+ | Deploy fails with "Unauthorized" | Invalid `NETLIFY_AUTH_TOKEN` | Regenerate token in Netlify |
242
+ | Preview not commenting on PR | Missing permissions | Check workflow has `pull-requests: write` |
243
+ | Env vars undefined in build | Not set in Netlify | Add to Netlify Dashboard or use `netlify env` |
244
+ | 404 on page refresh | SPA fallback not working | Check `netlify.toml` has `/* -> /index.html` |
245
+ | Production deploy not triggered | CI workflow failed | Check CI workflow status first |
246
+
247
+ ### Debug Build Locally
248
+
249
+ Test production build locally:
250
+
251
+ ```bash
252
+ npm run build
253
+ npx serve dist
254
+
255
+ # Or with Netlify CLI:
256
+ netlify build
257
+ netlify deploy --dir=dist
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Resources
263
+
264
+ - [Netlify Documentation](https://docs.netlify.com/)
265
+ - [Netlify CLI Reference](https://cli.netlify.com/)
266
+ - [File-based Configuration](https://docs.netlify.com/configure-builds/file-based-configuration/)
267
+ - [GitHub Actions for Netlify](https://github.com/nwtgck/actions-netlify)
268
+ - [Netlify Supabase Extension](https://www.netlify.com/integrations/supabase/)
@@ -10,25 +10,29 @@
10
10
 
11
11
  ```
12
12
  e2e/
13
+ ├── auth/
14
+ │ └── auth.setup.ts # Clerk authentication setup
13
15
  ├── fixtures/
14
- │ └── index.ts # setupPage, clearAppState
16
+ │ └── index.ts # setupPage, setupCleanPage, test, expect
15
17
  ├── tests/ # Functional E2E tests
16
18
  │ ├── home.spec.ts # Page structure, accessibility
17
19
  │ ├── theme.spec.ts # Theme toggle, persistence
18
20
  │ ├── language.spec.ts # Language switcher
19
- └── navigation.spec.ts # Routing, 404
20
- └── performance/ # Performance regression tests
21
- ├── setup.ts # Performance test fixture
22
- └── home.spec.ts # Home page performance tests
21
+ ├── navigation.spec.ts # Routing, 404
22
+ │ ├── profile.spec.ts # Unauthenticated profile tests
23
+ │ └── profile.auth.spec.ts # Authenticated profile tests
24
+ ├── performance/ # Performance regression tests
25
+ │ ├── setup.ts # Performance test fixture
26
+ │ └── home.spec.ts # Home page performance tests
27
+ └── .clerk/ # Auth state storage (gitignored)
28
+ └── user.json # Saved auth state for tests
23
29
  ```
24
30
 
25
31
  ## Imports
26
32
 
27
33
  ```typescript
28
34
  import { expect, test } from '@playwright/test';
29
-
30
- // For tests that need state clearing
31
- import { setupPage } from '../fixtures';
35
+ import { setupPage, setupCleanPage } from '../fixtures';
32
36
  ```
33
37
 
34
38
  ## Core Patterns
@@ -107,13 +111,130 @@ await page.waitForTimeout(500);
107
111
  ## Running Tests
108
112
 
109
113
  ```bash
110
- npm run e2e # Run functional tests
111
- npm run e2e:ui # Functional tests with interactive UI
114
+ npm run e2e # Run desktop tests
115
+ npm run e2e:mobile # Run mobile tests (Pixel 5 emulation)
116
+ npm run e2e:all # Run both desktop and mobile
117
+ npm run e2e:ui # Interactive UI mode
112
118
  npm run e2e:perf # Run performance tests
113
119
  npm run e2e:perf:ui # Performance tests with interactive UI
114
- npm run e2e:all # Run all tests (functional + performance)
120
+
121
+ # Authenticated tests (requires credentials)
122
+ npx playwright test --project=authenticated
123
+ ```
124
+
125
+ ## Authenticated Testing
126
+
127
+ Tests requiring authentication use `@clerk/testing` with Playwright. These tests run with a real authenticated user session.
128
+
129
+ ### Setup
130
+
131
+ 1. Install the testing package (already included):
132
+
133
+ ```bash
134
+ npm install -D @clerk/testing
135
+ ```
136
+
137
+ 2. Set environment variables in `.env`:
138
+
139
+ ```bash
140
+ CLERK_SECRET_KEY=sk_test_xxxxx
141
+ E2E_CLERK_USER_USERNAME=test@example.com
142
+ E2E_CLERK_USER_PASSWORD=your-test-password
143
+ ```
144
+
145
+ 3. Create a test user in your Clerk dashboard with the above credentials.
146
+
147
+ ### File Naming Convention
148
+
149
+ - `*.spec.ts` - Regular tests (run in `desktop`/`mobile` projects)
150
+ - `*.auth.spec.ts` - Authenticated tests (run in `authenticated` project only)
151
+
152
+ ### Writing Authenticated Tests
153
+
154
+ ```typescript
155
+ // e2e/tests/my-feature.auth.spec.ts
156
+ import { expect, test } from '@playwright/test';
157
+ import { existsSync } from 'fs';
158
+ import { dirname, join } from 'path';
159
+ import { fileURLToPath } from 'url';
160
+
161
+ const __dirname = dirname(fileURLToPath(import.meta.url));
162
+ const authFile = join(__dirname, '../.clerk/user.json');
163
+ const hasAuthState = existsSync(authFile);
164
+
165
+ test.describe('My Authenticated Feature', () => {
166
+ // Skip if auth state doesn't exist
167
+ test.skip(!hasAuthState, 'Authentication required');
168
+
169
+ test.beforeEach(async ({ page }) => {
170
+ // User is already authenticated via storageState
171
+ await page.goto('/protected-page');
172
+ });
173
+
174
+ test('can access protected content', async ({ page }) => {
175
+ await expect(page.getByText('Protected Content')).toBeVisible();
176
+ });
177
+ });
115
178
  ```
116
179
 
180
+ ### How It Works
181
+
182
+ 1. **Setup project** runs `auth.setup.ts` which:
183
+ - Calls `clerkSetup()` to get a testing token
184
+ - Signs in with test credentials
185
+ - Saves auth state to `e2e/.clerk/user.json`
186
+
187
+ 2. **Authenticated project** uses the saved state:
188
+ - Loads `storageState` from `user.json`
189
+ - Tests run with pre-authenticated session
190
+
191
+ 3. **Tests skip gracefully** when credentials aren't configured
192
+
193
+ ## Mobile Testing
194
+
195
+ Tests run on both desktop (Chrome) and mobile (Pixel 5) viewports. Use the `isMobile` fixture for device-specific behavior.
196
+
197
+ ### Using isMobile Fixture
198
+
199
+ ```typescript
200
+ import { expect, test } from '@playwright/test';
201
+
202
+ test('theme toggle works on all devices', async ({ page, isMobile }) => {
203
+ await page.goto('/');
204
+
205
+ // Same test logic works on both platforms
206
+ await page.getByRole('button', { name: /dark mode/i }).click();
207
+ await expect(page.locator('html')).toHaveClass(/dark/);
208
+
209
+ // Add mobile-specific assertions if needed
210
+ if (isMobile) {
211
+ // Verify touch-friendly button size, etc.
212
+ }
213
+ });
214
+ ```
215
+
216
+ ### Skip Tests by Platform
217
+
218
+ ```typescript
219
+ test('hover tooltip shows', async ({ page, isMobile }) => {
220
+ test.skip(isMobile, 'Hover not available on touch devices');
221
+ // Desktop-only test
222
+ });
223
+
224
+ test('touch gesture works', async ({ page, isMobile }) => {
225
+ test.skip(!isMobile, 'Touch gesture only on mobile');
226
+ // Mobile-only test
227
+ });
228
+ ```
229
+
230
+ ### Common Patterns
231
+
232
+ | Pattern | Desktop | Mobile |
233
+ | -------------- | --------- | --------------- |
234
+ | Viewport width | 1280px | 393px (Pixel 5) |
235
+ | Touch events | Click | Tap |
236
+ | Hover states | Supported | Not applicable |
237
+
117
238
  ## Performance Testing
118
239
 
119
240
  Performance tests use [react-performance-tracking](https://github.com/mkaczkowski/react-performance-tracking) to measure:
@@ -129,3 +250,4 @@ Performance tests use [react-performance-tracking](https://github.com/mkaczkowsk
129
250
  - [ ] No arbitrary timeouts
130
251
  - [ ] Tests behavior, not implementation
131
252
  - [ ] Uses `setupPage` when testing persistence
253
+ - [ ] Considers mobile viewport when relevant