ace-test 0.6.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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.ace-defaults/nav/protocols/agent-sources/ace-test.yml +19 -0
  3. data/.ace-defaults/nav/protocols/guide-sources/ace-test.yml +19 -0
  4. data/.ace-defaults/nav/protocols/tmpl-sources/ace-test.yml +11 -0
  5. data/.ace-defaults/nav/protocols/wfi-sources/ace-test.yml +19 -0
  6. data/CHANGELOG.md +169 -0
  7. data/LICENSE +21 -0
  8. data/README.md +40 -0
  9. data/Rakefile +12 -0
  10. data/handbook/agents/mock.ag.md +164 -0
  11. data/handbook/agents/profile-tests.ag.md +132 -0
  12. data/handbook/agents/test.ag.md +99 -0
  13. data/handbook/guides/SUMMARY.md +95 -0
  14. data/handbook/guides/embedded-testing-guide.g.md +261 -0
  15. data/handbook/guides/mocking-patterns.g.md +464 -0
  16. data/handbook/guides/quick-reference.g.md +46 -0
  17. data/handbook/guides/test-driven-development-cycle/meta-documentation.md +26 -0
  18. data/handbook/guides/test-driven-development-cycle/ruby-application.md +18 -0
  19. data/handbook/guides/test-driven-development-cycle/ruby-gem.md +19 -0
  20. data/handbook/guides/test-driven-development-cycle/rust-cli.md +18 -0
  21. data/handbook/guides/test-driven-development-cycle/rust-wasm-zed.md +19 -0
  22. data/handbook/guides/test-driven-development-cycle/typescript-nuxt.md +18 -0
  23. data/handbook/guides/test-driven-development-cycle/typescript-vue.md +19 -0
  24. data/handbook/guides/test-layer-decision.g.md +261 -0
  25. data/handbook/guides/test-mocking-patterns.g.md +414 -0
  26. data/handbook/guides/test-organization.g.md +140 -0
  27. data/handbook/guides/test-performance.g.md +353 -0
  28. data/handbook/guides/test-responsibility-map.g.md +220 -0
  29. data/handbook/guides/test-review-checklist.g.md +231 -0
  30. data/handbook/guides/test-suite-health.g.md +337 -0
  31. data/handbook/guides/testable-code-patterns.g.md +315 -0
  32. data/handbook/guides/testing/ruby-rspec-config-examples.md +120 -0
  33. data/handbook/guides/testing/ruby-rspec.md +87 -0
  34. data/handbook/guides/testing/rust.md +52 -0
  35. data/handbook/guides/testing/test-maintenance.md +364 -0
  36. data/handbook/guides/testing/typescript-bun.md +47 -0
  37. data/handbook/guides/testing/vue-firebase-auth.md +546 -0
  38. data/handbook/guides/testing/vue-vitest.md +236 -0
  39. data/handbook/guides/testing-philosophy.g.md +82 -0
  40. data/handbook/guides/testing-strategy.g.md +151 -0
  41. data/handbook/guides/testing-tdd-cycle.g.md +146 -0
  42. data/handbook/guides/testing.g.md +170 -0
  43. data/handbook/skills/as-test-create-cases/SKILL.md +24 -0
  44. data/handbook/skills/as-test-fix/SKILL.md +26 -0
  45. data/handbook/skills/as-test-improve-coverage/SKILL.md +22 -0
  46. data/handbook/skills/as-test-optimize/SKILL.md +34 -0
  47. data/handbook/skills/as-test-performance-audit/SKILL.md +34 -0
  48. data/handbook/skills/as-test-plan/SKILL.md +34 -0
  49. data/handbook/skills/as-test-review/SKILL.md +34 -0
  50. data/handbook/skills/as-test-verify-suite/SKILL.md +45 -0
  51. data/handbook/templates/e2e-sandbox-checklist.template.md +289 -0
  52. data/handbook/templates/test-case.template.md +56 -0
  53. data/handbook/templates/test-performance-audit.template.md +132 -0
  54. data/handbook/templates/test-responsibility-map.template.md +92 -0
  55. data/handbook/templates/test-review-checklist.template.md +163 -0
  56. data/handbook/workflow-instructions/test/analyze-failures.wf.md +120 -0
  57. data/handbook/workflow-instructions/test/create-cases.wf.md +675 -0
  58. data/handbook/workflow-instructions/test/fix.wf.md +120 -0
  59. data/handbook/workflow-instructions/test/improve-coverage.wf.md +370 -0
  60. data/handbook/workflow-instructions/test/optimize.wf.md +368 -0
  61. data/handbook/workflow-instructions/test/performance-audit.wf.md +17 -0
  62. data/handbook/workflow-instructions/test/plan.wf.md +323 -0
  63. data/handbook/workflow-instructions/test/review.wf.md +16 -0
  64. data/handbook/workflow-instructions/test/verify-suite.wf.md +343 -0
  65. data/lib/ace/test/version.rb +7 -0
  66. data/lib/ace/test.rb +10 -0
  67. metadata +152 -0
@@ -0,0 +1,546 @@
1
+ ---
2
+ doc-type: guide
3
+ title: Vue.js 3 + Firebase Authentication Testing Guide
4
+ purpose: Vue Firebase authentication testing
5
+ ace-docs:
6
+ last-updated: 2026-01-23
7
+ last-checked: 2026-03-21
8
+ ---
9
+
10
+ # Vue.js 3 + Firebase Authentication Testing Guide
11
+
12
+ ## Overview
13
+
14
+ This guide provides comprehensive testing strategies for Vue.js 3 applications using Firebase Authentication with Vitest, covering both mock-based unit tests and integration tests with Firebase Emulator Suite.
15
+
16
+ ## Testing Philosophy
17
+
18
+ Firebase Authentication testing follows a two-layer approach:
19
+
20
+ 1. **Unit/Component Tests (Mocked)** - Fast, offline tests focusing on application logic
21
+ 2. **Integration Tests (Emulator)** - Slower but comprehensive tests using Firebase Emulator Suite
22
+
23
+ ## Mock-Based Testing Setup
24
+
25
+ ### Firebase Auth Mock Module
26
+
27
+ Create a dedicated mock module for Firebase Authentication:
28
+
29
+ ```typescript
30
+ // tests/__mocks__/firebase-auth.ts
31
+ import { vi } from 'vitest'
32
+
33
+ export const fakeUser = {
34
+ uid: 'test-user-123',
35
+ email: 'test@example.com',
36
+ displayName: 'Test User',
37
+ emailVerified: true
38
+ }
39
+
40
+ export const getAuth = vi.fn(() => ({
41
+ currentUser: fakeUser,
42
+ }))
43
+
44
+ export const signInWithEmailAndPassword = vi.fn(async () => ({
45
+ user: fakeUser,
46
+ }))
47
+
48
+ export const signOut = vi.fn(async () => {})
49
+
50
+ export const onAuthStateChanged = vi.fn((auth, callback) => {
51
+ callback(fakeUser)
52
+ return vi.fn() // Unsubscribe function
53
+ })
54
+
55
+ export const signInWithPopup = vi.fn(async () => ({
56
+ user: fakeUser,
57
+ }))
58
+
59
+ export const GoogleAuthProvider = vi.fn()
60
+ ```
61
+
62
+ ### Vitest Configuration
63
+
64
+ Configure Vitest to use the mock:
65
+
66
+ ```javascript
67
+ // vitest.config.js
68
+ import { defineConfig } from 'vitest/config'
69
+
70
+ export default defineConfig({
71
+ test: {
72
+ environment: 'jsdom',
73
+ setupFiles: ['./tests/setup.ts']
74
+ }
75
+ })
76
+ ```
77
+
78
+ ```typescript
79
+ // tests/setup.ts
80
+ import { vi } from 'vitest'
81
+
82
+ // Mock Firebase Auth before any imports
83
+ vi.mock('firebase/auth', () => import('./__mocks__/firebase-auth'))
84
+
85
+ // Reset mocks between tests
86
+ afterEach(() => {
87
+ vi.resetAllMocks()
88
+ })
89
+ ```
90
+
91
+ ### Testing Vue Components with Authentication
92
+
93
+ ```typescript
94
+ // tests/components/LoginForm.test.ts
95
+ import { mount } from '@vue/test-utils'
96
+ import { createTestingPinia } from '@pinia/testing'
97
+ import LoginForm from '@/components/auth/LoginForm.vue'
98
+ import { signInWithEmailAndPassword } from 'firebase/auth'
99
+
100
+ describe('LoginForm', () => {
101
+ it('handles login successfully', async () => {
102
+ const wrapper = mount(LoginForm, {
103
+ global: {
104
+ plugins: [createTestingPinia()]
105
+ }
106
+ })
107
+
108
+ await wrapper.find('[data-testid="email"]').setValue('test@example.com')
109
+ await wrapper.find('[data-testid="password"]').setValue('password')
110
+ await wrapper.find('[data-testid="login-form"]').trigger('submit')
111
+
112
+ expect(signInWithEmailAndPassword).toHaveBeenCalledWith(
113
+ expect.anything(),
114
+ 'test@example.com',
115
+ 'password'
116
+ )
117
+ })
118
+
119
+ it('displays error message on failed login', async () => {
120
+ signInWithEmailAndPassword.mockRejectedValueOnce({
121
+ code: 'auth/invalid-password',
122
+ message: 'Invalid password'
123
+ })
124
+
125
+ const wrapper = mount(LoginForm, {
126
+ global: {
127
+ plugins: [createTestingPinia()]
128
+ }
129
+ })
130
+
131
+ await wrapper.find('[data-testid="login-form"]').trigger('submit')
132
+ await wrapper.vm.$nextTick()
133
+
134
+ expect(wrapper.find('[data-testid="error-message"]').text())
135
+ .toContain('Invalid password')
136
+ })
137
+ })
138
+ ```
139
+
140
+ ### Testing Pinia Stores
141
+
142
+ ```typescript
143
+ // tests/stores/userStore.test.ts
144
+ import { setActivePinia, createPinia } from 'pinia'
145
+ import { useUserStore } from '@/stores/userStore'
146
+ import { signInWithEmailAndPassword, signOut } from 'firebase/auth'
147
+
148
+ describe('userStore', () => {
149
+ beforeEach(() => {
150
+ setActivePinia(createPinia())
151
+ })
152
+
153
+ it('logs in user successfully', async () => {
154
+ const store = useUserStore()
155
+
156
+ await store.login('test@example.com', 'password')
157
+
158
+ expect(signInWithEmailAndPassword).toHaveBeenCalledWith(
159
+ expect.anything(),
160
+ 'test@example.com',
161
+ 'password'
162
+ )
163
+ expect(store.user).toEqual(expect.objectContaining({
164
+ email: 'test@example.com'
165
+ }))
166
+ expect(store.isAuthenticated).toBe(true)
167
+ })
168
+
169
+ it('handles logout correctly', async () => {
170
+ const store = useUserStore()
171
+ store.user = { uid: '123', email: 'test@example.com' }
172
+
173
+ await store.logout()
174
+
175
+ expect(signOut).toHaveBeenCalled()
176
+ expect(store.user).toBeNull()
177
+ expect(store.isAuthenticated).toBe(false)
178
+ })
179
+ })
180
+ ```
181
+
182
+ ### Testing Vue Router Guards
183
+
184
+ ```typescript
185
+ // tests/router/authGuards.test.ts
186
+ import { createRouter, createWebHistory } from 'vue-router'
187
+ import { useUserStore } from '@/stores/userStore'
188
+ import { createTestingPinia } from '@pinia/testing'
189
+
190
+ describe('Auth Guards', () => {
191
+ it('redirects unauthenticated users to login', async () => {
192
+ const pinia = createTestingPinia()
193
+ const userStore = useUserStore(pinia)
194
+ userStore.isAuthenticated = false
195
+
196
+ const router = createRouter({
197
+ history: createWebHistory(),
198
+ routes: [
199
+ { path: '/login', component: { template: '<div>Login</div>' } },
200
+ {
201
+ path: '/dashboard',
202
+ component: { template: '<div>Dashboard</div>' },
203
+ beforeEnter: requireAuth
204
+ }
205
+ ]
206
+ })
207
+
208
+ await router.push('/dashboard')
209
+
210
+ expect(router.currentRoute.value.path).toBe('/login')
211
+ })
212
+ })
213
+ ```
214
+
215
+ ## Firebase Emulator Integration Testing
216
+
217
+ ### Emulator Setup
218
+
219
+ ```json
220
+ {
221
+ "scripts": {
222
+ "test:integration": "firebase emulators:exec --only auth,firestore \"vitest run --config vitest.integration.config.js\"",
223
+ "test:emulator": "firebase emulators:start --only auth,firestore"
224
+ }
225
+ }
226
+ ```
227
+
228
+ ### Integration Test Configuration
229
+
230
+ ```javascript
231
+ // vitest.integration.config.js
232
+ import { defineConfig } from 'vitest/config'
233
+
234
+ export default defineConfig({
235
+ test: {
236
+ environment: 'jsdom',
237
+ setupFiles: ['./tests/integration/setup.ts'],
238
+ testTimeout: 10000
239
+ }
240
+ })
241
+ ```
242
+
243
+ ```typescript
244
+ // tests/integration/setup.ts
245
+ import { initializeApp } from 'firebase/app'
246
+ import { getAuth, connectAuthEmulator } from 'firebase/auth'
247
+ import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore'
248
+
249
+ // Initialize Firebase for testing
250
+ const app = initializeApp({
251
+ projectId: 'demo-test-project',
252
+ apiKey: 'fake-api-key'
253
+ })
254
+
255
+ const auth = getAuth(app)
256
+ const db = getFirestore(app)
257
+
258
+ // Connect to emulators
259
+ connectAuthEmulator(auth, 'http://localhost:9099', { disableWarnings: true })
260
+ connectFirestoreEmulator(db, 'localhost', 8080)
261
+
262
+ export { auth, db }
263
+ ```
264
+
265
+ ### Integration Test Examples
266
+
267
+ ```typescript
268
+ // tests/integration/auth.test.ts
269
+ import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth'
270
+ import { auth } from './setup'
271
+
272
+ describe('Firebase Auth Integration', () => {
273
+ beforeEach(async () => {
274
+ // Clear auth state before each test
275
+ await fetch('http://localhost:9099/emulator/v1/projects/demo-test-project/accounts', {
276
+ method: 'DELETE'
277
+ })
278
+ })
279
+
280
+ it('creates and authenticates user', async () => {
281
+ // Create user
282
+ const userCredential = await createUserWithEmailAndPassword(
283
+ auth,
284
+ 'test@example.com',
285
+ 'password123'
286
+ )
287
+
288
+ expect(userCredential.user.email).toBe('test@example.com')
289
+ expect(userCredential.user.emailVerified).toBe(false)
290
+
291
+ // Sign out
292
+ await auth.signOut()
293
+
294
+ // Sign in again
295
+ const signInCredential = await signInWithEmailAndPassword(
296
+ auth,
297
+ 'test@example.com',
298
+ 'password123'
299
+ )
300
+
301
+ expect(signInCredential.user.uid).toBe(userCredential.user.uid)
302
+ })
303
+
304
+ it('handles invalid credentials correctly', async () => {
305
+ await expect(
306
+ signInWithEmailAndPassword(auth, 'nonexistent@example.com', 'wrongpassword')
307
+ ).rejects.toThrow()
308
+ })
309
+ })
310
+ ```
311
+
312
+ ### Security Rules Testing
313
+
314
+ ```typescript
315
+ // tests/integration/securityRules.test.ts
316
+ import { doc, getDoc, setDoc } from 'firebase/firestore'
317
+ import { db, auth } from './setup'
318
+ import { signInWithEmailAndPassword } from 'firebase/auth'
319
+
320
+ describe('Firestore Security Rules', () => {
321
+ it('allows users to read their own profile', async () => {
322
+ // Create and sign in user
323
+ const user = await createTestUser('user@example.com', 'password')
324
+
325
+ // Try to read user's own profile
326
+ const profileRef = doc(db, 'users', user.uid)
327
+ const profileSnap = await getDoc(profileRef)
328
+
329
+ expect(profileSnap.exists()).toBe(true)
330
+ })
331
+
332
+ it('denies access to other users profiles', async () => {
333
+ const user1 = await createTestUser('user1@example.com', 'password')
334
+ const user2 = await createTestUser('user2@example.com', 'password')
335
+
336
+ // Sign in as user1
337
+ await signInWithEmailAndPassword(auth, 'user1@example.com', 'password')
338
+
339
+ // Try to read user2's profile
340
+ const otherProfileRef = doc(db, 'users', user2.uid)
341
+
342
+ await expect(getDoc(otherProfileRef)).rejects.toThrow()
343
+ })
344
+ })
345
+ ```
346
+
347
+ ## Test Helpers and Utilities
348
+
349
+ ### Authentication Test Helpers
350
+
351
+ ```typescript
352
+ // tests/helpers/authHelpers.ts
353
+ import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth'
354
+ import { auth } from '../integration/setup'
355
+
356
+ export async function createTestUser(email: string, password: string) {
357
+ const userCredential = await createUserWithEmailAndPassword(auth, email, password)
358
+ return userCredential.user
359
+ }
360
+
361
+ export async function signInTestUser(email: string, password: string) {
362
+ const userCredential = await signInWithEmailAndPassword(auth, email, password)
363
+ return userCredential.user
364
+ }
365
+
366
+ export async function clearAuthEmulator() {
367
+ await fetch('http://localhost:9099/emulator/v1/projects/demo-test-project/accounts', {
368
+ method: 'DELETE'
369
+ })
370
+ }
371
+ ```
372
+
373
+ ### Vue Component Test Helpers
374
+
375
+ ```typescript
376
+ // tests/helpers/vueHelpers.ts
377
+ import { mount } from '@vue/test-utils'
378
+ import { createTestingPinia } from '@pinia/testing'
379
+ import { createRouter, createWebHistory } from 'vue-router'
380
+
381
+ export function createTestWrapper(component: any, options: any = {}) {
382
+ const router = createRouter({
383
+ history: createWebHistory(),
384
+ routes: [
385
+ { path: '/', component: { template: '<div>Home</div>' } },
386
+ { path: '/login', component: { template: '<div>Login</div>' } }
387
+ ]
388
+ })
389
+
390
+ return mount(component, {
391
+ global: {
392
+ plugins: [
393
+ createTestingPinia({
394
+ stubActions: false,
395
+ ...options.pinia
396
+ }),
397
+ router
398
+ ],
399
+ ...options.global
400
+ },
401
+ ...options
402
+ })
403
+ }
404
+ ```
405
+
406
+ ## CI/CD Integration
407
+
408
+ ### GitHub Actions Workflow
409
+
410
+ ```yaml
411
+ # .github/workflows/test.yml
412
+ name: Test
413
+ on: [push, pull_request]
414
+
415
+ jobs:
416
+ test:
417
+ runs-on: ubuntu-latest
418
+ steps:
419
+ - uses: actions/checkout@v4
420
+
421
+ - uses: actions/setup-node@v4
422
+ with:
423
+ node-version: '20'
424
+ cache: 'npm'
425
+
426
+ - uses: actions/setup-java@v4
427
+ with:
428
+ distribution: 'temurin'
429
+ java-version: '17'
430
+
431
+ - run: npm ci
432
+
433
+ - name: Run unit tests
434
+ run: npm run test:unit
435
+
436
+ - name: Run integration tests
437
+ run: npm run test:integration
438
+
439
+ - name: Upload coverage
440
+ uses: codecov/codecov-action@v3
441
+ ```
442
+
443
+ ### NPM Scripts
444
+
445
+ ```json
446
+ {
447
+ "scripts": {
448
+ "test": "vitest",
449
+ "test:unit": "vitest run",
450
+ "test:integration": "firebase emulators:exec --only auth,firestore \"vitest run --config vitest.integration.config.js\"",
451
+ "test:coverage": "vitest run --coverage",
452
+ "test:watch": "vitest watch"
453
+ }
454
+ }
455
+ ```
456
+
457
+ ## Best Practices
458
+
459
+ ### Test Organization
460
+
461
+ 1. **Separate Unit and Integration Tests**
462
+ - Unit tests in `tests/unit/`
463
+ - Integration tests in `tests/integration/`
464
+ - Shared helpers in `tests/helpers/`
465
+
466
+ 2. **Mock Strategy**
467
+ - Mock Firebase SDK for unit tests
468
+ - Use real Firebase Emulator for integration tests
469
+ - Mock external APIs and services
470
+
471
+ 3. **Test Data Management**
472
+ - Use factories for test data creation
473
+ - Clear emulator state between tests
474
+ - Use meaningful test data that reflects real usage
475
+
476
+ ### Error Handling Testing
477
+
478
+ ```typescript
479
+ describe('Error Handling', () => {
480
+ it('handles network errors gracefully', async () => {
481
+ signInWithEmailAndPassword.mockRejectedValueOnce(
482
+ new Error('Network error')
483
+ )
484
+
485
+ const store = useUserStore()
486
+ await store.login('test@example.com', 'password')
487
+
488
+ expect(store.error).toContain('Network error')
489
+ expect(store.isLoading).toBe(false)
490
+ })
491
+
492
+ it('handles Firebase auth errors', async () => {
493
+ signInWithEmailAndPassword.mockRejectedValueOnce({
494
+ code: 'auth/user-not-found',
495
+ message: 'User not found'
496
+ })
497
+
498
+ const store = useUserStore()
499
+ await store.login('nonexistent@example.com', 'password')
500
+
501
+ expect(store.error).toContain('User not found')
502
+ })
503
+ })
504
+ ```
505
+
506
+ ### Performance Testing
507
+
508
+ ```typescript
509
+ describe('Authentication Performance', () => {
510
+ it('completes login within acceptable time', async () => {
511
+ const startTime = Date.now()
512
+
513
+ await store.login('test@example.com', 'password')
514
+
515
+ const duration = Date.now() - startTime
516
+ expect(duration).toBeLessThan(1000) // 1 second max
517
+ })
518
+ })
519
+ ```
520
+
521
+ ## Common Pitfalls and Solutions
522
+
523
+ ### Mock Issues
524
+
525
+ - **ESM Side Effects**: Always mock before importing your application code
526
+ - **TypeScript Errors**: Create proper type declarations for mocked modules
527
+ - **Async Callbacks**: Use `vi.fn()` with proper callback simulation
528
+
529
+ ### Emulator Issues
530
+
531
+ - **Connection Timing**: Ensure emulator is ready before running tests
532
+ - **State Pollution**: Clear emulator state between test suites
533
+ - **Port Conflicts**: Use dedicated ports for CI environments
534
+
535
+ ### Vue-Specific Issues
536
+
537
+ - **Pinia State**: Reset store state between tests
538
+ - **Router Navigation**: Mock or stub router in unit tests
539
+ - **Component Lifecycle**: Use proper Vue Test Utils methods for async operations
540
+
541
+ ## Resources
542
+
543
+ - [Vitest Documentation](https://vitest.dev/)
544
+ - [Firebase Emulator Suite](https://firebase.google.com/docs/emulator-suite)
545
+ - [Vue Test Utils](https://test-utils.vuejs.org/)
546
+ - [Pinia Testing](https://pinia.vuejs.org/cookbook/testing.html)