@take-out/docs 0.0.42

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.
@@ -0,0 +1,564 @@
1
+ ---
2
+ name: testing-integration
3
+ description: Integration testing guide. Use when writing Playwright tests, running bun test:integration, testing with loginAsDemo, or debugging test failures. INVOKE WHEN: integration tests, e2e tests, end-to-end, Playwright, browser tests, test:integration, test fixtures, test setup, test users.
4
+ ---
5
+
6
+ # Integration Testing Guide
7
+
8
+ This guide covers integration testing setup, best practices, and troubleshooting
9
+ for the chat application. Integration tests verify that different parts of the
10
+ system work together correctly, from the UI through the API to the database.
11
+
12
+ ## Quick Start
13
+
14
+ ### Running Integration Tests Locally
15
+
16
+ For easy development integration testing, use the unified command:
17
+
18
+ ```bash
19
+ # starts both dev web server and backend services in one command
20
+ bun ci:start-dev
21
+
22
+ # in another terminal, run all integration tests
23
+ bun test:integration
24
+
25
+ # or run a specific test file
26
+ bun test:integration src/integration/search.test.ts
27
+ ```
28
+
29
+ You can also run in production mode (like CI does) if you've run `bun web:build`
30
+ first:
31
+
32
+ ```bash
33
+ bun web:build
34
+ bun ci:start-prod
35
+
36
+ # in another terminal, run tests
37
+ bun test:integration
38
+ ```
39
+
40
+ ### Important: Database State Management
41
+
42
+ Always restart `bun ci:start-dev` between test runs to ensure a clean database
43
+ state. This is critical because:
44
+
45
+ - Tests may create data that persists between runs
46
+ - Search tests in particular can fail if old test messages remain in the
47
+ database
48
+ - The database accumulates test data with each run if not restarted
49
+
50
+ ```bash
51
+ # if tests fail or behave unexpectedly:
52
+ 1. Stop ci:start-dev (Ctrl+C)
53
+ 2. Run `bun ci:start-dev` again to get a fresh database
54
+ 3. Run your tests
55
+ ```
56
+
57
+ ## Architecture Overview
58
+
59
+ ### Test Stack
60
+
61
+ - Framework: Playwright Test
62
+ - Database: PostgreSQL with pgvector extension
63
+ - Real-time: Zero sync system
64
+ - Auth: Better Auth with test admin user
65
+ - Search: pgvector for semantic search
66
+ - Browser: Chromium (automatically installed)
67
+
68
+ ### Directory Structure
69
+
70
+ ```
71
+ src/integration/
72
+ ├── .output/ # test results and traces
73
+ ├── global-setup.ts # global test setup
74
+ ├── helpers.ts # shared helper functions
75
+ ├── api.test.ts # api integration tests
76
+ ├── basic.test.ts # basic app functionality
77
+ ├── chat-api.test.ts # chat api tests
78
+ ├── embeddings.test.ts # embeddings and vector search
79
+ ├── search.test.ts # search functionality
80
+ └── private-chat-stats.test.ts
81
+ ```
82
+
83
+ ## Test Infrastructure
84
+
85
+ ### Playwright Configuration
86
+
87
+ The test configuration is defined in `playwright.config.ts`:
88
+
89
+ ```typescript
90
+ export default defineConfig({
91
+ testDir: './src/integration',
92
+ outputDir: './src/integration/.output/test-results',
93
+ globalSetup: './src/integration/global-setup.ts',
94
+ timeout: 30 * 1000,
95
+ expect: {
96
+ timeout: 5000,
97
+ },
98
+ fullyParallel: false,
99
+ retries: process.env.CI ? 1 : 0,
100
+ workers: process.env.CI ? 1 : undefined,
101
+ reporter: process.env.CI ? 'dot' : 'line',
102
+ use: {
103
+ baseURL: 'http://localhost:8081',
104
+ trace: 'on-first-retry',
105
+ },
106
+ projects: [
107
+ {
108
+ name: 'chromium',
109
+ use: { ...devices['Desktop Chrome'] },
110
+ },
111
+ ],
112
+ })
113
+ ```
114
+
115
+ ### Global Setup
116
+
117
+ The global setup (`global-setup.ts`) runs once before all tests:
118
+
119
+ 1. Launches a browser instance
120
+ 2. Logs in as admin user
121
+ 3. Creates a Tamagui test server
122
+ 4. Sets up initial test environment
123
+
124
+ This ensures a consistent starting state for all tests.
125
+
126
+ ## Writing Integration Tests
127
+
128
+ ### Basic Test Structure
129
+
130
+ ```typescript
131
+ import { test, expect } from '@playwright/test'
132
+
133
+ test.describe('Feature Name', () => {
134
+ test.beforeAll(async ({ browser }) => {
135
+ // setup that runs once before all tests in this suite
136
+ })
137
+
138
+ test.beforeEach(async ({ page }) => {
139
+ // setup that runs before each test
140
+ await page.goto('/tamagui')
141
+ })
142
+
143
+ test('should do something', async ({ page }) => {
144
+ await expect(page.locator('[data-testid="element"]')).toBeVisible()
145
+ })
146
+
147
+ test.afterEach(async ({ page }) => {
148
+ // cleanup after each test
149
+ })
150
+ })
151
+ ```
152
+
153
+ ### Using Data Test IDs
154
+
155
+ Always use `data-testid` attributes for reliable element selection:
156
+
157
+ ```typescript
158
+ // good - uses test ID
159
+ const button = page.locator('[data-testid="submit-button"]')
160
+
161
+ // avoid - fragile selector
162
+ const button = page.locator('button.submit')
163
+ ```
164
+
165
+ ### Waiting for Elements
166
+
167
+ Use proper wait strategies for async operations:
168
+
169
+ ```typescript
170
+ // wait for element to be visible
171
+ await page.waitForSelector('[data-testid="message-list"]', {
172
+ state: 'visible',
173
+ timeout: 5000,
174
+ })
175
+
176
+ // wait for network idle
177
+ await page.goto(url, { waitUntil: 'networkidle' })
178
+
179
+ // wait for specific time (use sparingly)
180
+ await page.waitForTimeout(800)
181
+ ```
182
+
183
+ ## Database State Management
184
+
185
+ ### Clean Database Requirements
186
+
187
+ Integration tests require a clean database state. This is managed through:
188
+
189
+ 1. Docker Compose: Spins up fresh PostgreSQL with pgvector
190
+ 2. Migrations: Automatically applied on startup
191
+ 3. Test Data: Created by individual tests as needed
192
+
193
+ ### Database Reset Strategy
194
+
195
+ ```bash
196
+ # manual database reset
197
+ docker compose down
198
+ docker compose up -d
199
+ bun db:migrate
200
+
201
+ # or simply restart the development environment
202
+ bun ci:start-dev
203
+ ```
204
+
205
+ ### Test Data Isolation
206
+
207
+ Use unique identifiers to prevent data collision:
208
+
209
+ ```typescript
210
+ test('create and search for message', async ({ page }) => {
211
+ const uniqueMessage = `Test message ${Date.now()}`
212
+ await writeMessageInChannel(page, uniqueMessage)
213
+ })
214
+ ```
215
+
216
+ ## Common Test Patterns
217
+
218
+ ### Search Tests
219
+
220
+ ```typescript
221
+ test('search for messages', async ({ page }) => {
222
+ const testMessage = `Search test ${Date.now()}`
223
+ await writeMessageInChannel(page, testMessage)
224
+
225
+ await page.waitForTimeout(800) // wait for indexing
226
+
227
+ await page.keyboard.press('Meta+f')
228
+ await page.keyboard.type('search test')
229
+
230
+ const results = page.locator('.message-item-minimal')
231
+ await expect(results).toHaveCount(1)
232
+ })
233
+ ```
234
+
235
+ ### Real-time Updates
236
+
237
+ ```typescript
238
+ test('real-time message sync', async ({ page, context }) => {
239
+ const page2 = await context.newPage()
240
+ await page2.goto('/tamagui')
241
+
242
+ await writeMessageInChannel(page, 'Real-time test')
243
+
244
+ await expect(page2.locator('text="Real-time test"')).toBeVisible({
245
+ timeout: 2000,
246
+ })
247
+ })
248
+ ```
249
+
250
+ ### API Integration Tests
251
+
252
+ ```typescript
253
+ test('API endpoint', async ({ request }) => {
254
+ const response = await request.post('/api/messages', {
255
+ data: {
256
+ content: 'API test message',
257
+ channelId: 'test-channel',
258
+ },
259
+ })
260
+
261
+ expect(response.ok()).toBeTruthy()
262
+ const json = await response.json()
263
+ expect(json.success).toBe(true)
264
+ })
265
+ ```
266
+
267
+ ## Helper Functions
268
+
269
+ The `helpers.ts` file provides reusable functions:
270
+
271
+ ### Core Helpers
272
+
273
+ ```typescript
274
+ await finishOnboarding(page)
275
+ await setupTamaguiServer(page)
276
+ await writeMessageInChannel(page, 'Hello world')
277
+ await expectMessageInChannel(page, 'Hello world')
278
+ await openHUD(page)
279
+ await focusChannelInput(page)
280
+ ```
281
+
282
+ ### Visibility Helpers
283
+
284
+ ```typescript
285
+ await expectToBeVisible(locator, { timeout: 2000 })
286
+ await expectToBeHidden(locator, { timeout: 2000 })
287
+ ```
288
+
289
+ ## CI/CD Integration
290
+
291
+ ### GitHub Actions Workflow
292
+
293
+ Integration tests run automatically in CI:
294
+
295
+ ```yaml
296
+ - name: Install Playwright
297
+ run: bunx playwright install --with-deps chromium
298
+
299
+ - name: Start backend services
300
+ run: bun ci:backend &
301
+
302
+ - name: Start frontend (production)
303
+ run: bun ci:frontend &
304
+
305
+ - name: Wait for server
306
+ run: bun wait-for-server
307
+
308
+ - name: Run integration tests
309
+ run: bun test:integration
310
+ ```
311
+
312
+ ### CI-Specific Configuration
313
+
314
+ The CI environment has specific settings:
315
+
316
+ - Retries: 1 retry on failure
317
+ - Workers: Single worker for consistency
318
+ - Reporter: Dot reporter for compact output
319
+ - Timeout: Extended timeouts for slower CI machines
320
+
321
+ ### Testing Everything in Production Mode
322
+
323
+ This is not often needed when developing but may be useful to test things fully
324
+ end-to-end:
325
+
326
+ ```bash
327
+ # run the full CI setup except for deploying at the end
328
+ bun run ci --skip-deploy
329
+ ```
330
+
331
+ ### Running Tests with Different Configurations
332
+
333
+ ```bash
334
+ # run with specific skips
335
+ bun run ci --skip-tests --skip-deploy
336
+
337
+ # just run tests in CI mode
338
+ CI=true bun test:integration
339
+ ```
340
+
341
+ ## Troubleshooting
342
+
343
+ ### Common Issues and Solutions
344
+
345
+ #### ERR_JWKS_NO_MATCHING_KEY
346
+
347
+ Problem: Authentication keys mismatch
348
+
349
+ Solution:
350
+
351
+ ```bash
352
+ # restart services to regenerate keys
353
+ bun ci:start-dev
354
+
355
+ # clear browser storage if the issue persists
356
+ ```
357
+
358
+ #### Tests Finding Unexpected Data
359
+
360
+ Problem: Old test data affecting new runs
361
+
362
+ Solution:
363
+
364
+ ```bash
365
+ Ctrl+C # stop ci:start-dev
366
+ bun ci:start-dev # fresh start
367
+ bun test:integration
368
+ ```
369
+
370
+ #### Search Tests Failing
371
+
372
+ Problem: Search results not as expected
373
+
374
+ Solution:
375
+
376
+ ```typescript
377
+ // ensure sufficient wait time for indexing
378
+ await page.waitForTimeout(800)
379
+
380
+ // add debug logging
381
+ const count = await results.count()
382
+ if (count !== expected) {
383
+ for (let i = 0; i < count; i++) {
384
+ const text = await results.nth(i).textContent()
385
+ console.info(`Result ${i}: ${text}`)
386
+ }
387
+ }
388
+ ```
389
+
390
+ #### Port 8081 Already in Use
391
+
392
+ Problem: Previous test run didn't clean up
393
+
394
+ Solution:
395
+
396
+ ```bash
397
+ lsof -i :8081
398
+ kill -9 <PID>
399
+
400
+ # or use docker cleanup
401
+ docker compose down
402
+ ```
403
+
404
+ #### Playwright Installation Issues
405
+
406
+ Problem: Browser binaries not installed
407
+
408
+ Solution:
409
+
410
+ ```bash
411
+ bunx playwright install --with-deps chromium
412
+ # or install all browsers
413
+ bunx playwright install
414
+ ```
415
+
416
+ ### Debug Techniques
417
+
418
+ #### Enable Playwright Inspector
419
+
420
+ ```bash
421
+ PWDEBUG=1 bun test:integration --headed
422
+ ```
423
+
424
+ #### Add Console Logging
425
+
426
+ ```typescript
427
+ test('debug test', async ({ page }) => {
428
+ page.on('console', (msg) => {
429
+ console.info('Browser console:', msg.text())
430
+ })
431
+
432
+ page.on('pageerror', (error) => {
433
+ console.error('Page error:', error)
434
+ })
435
+ })
436
+ ```
437
+
438
+ #### Save Test Traces
439
+
440
+ ```typescript
441
+ test.use({
442
+ trace: 'on',
443
+ video: 'on',
444
+ screenshot: 'on',
445
+ })
446
+ ```
447
+
448
+ View traces:
449
+
450
+ ```bash
451
+ bunx playwright show-trace src/integration/.output/test-results/*/trace.zip
452
+ ```
453
+
454
+ ## Best Practices
455
+
456
+ 1. Use Unique Test Data: Always include unique identifiers to avoid conflicts
457
+ 2. Clean State Assumption: Never assume data from previous tests
458
+ 3. Proper Waits: Use appropriate wait strategies
459
+ 4. Descriptive Test Names: Write clear, descriptive test names
460
+ 5. Isolate Test Logic: Keep tests independent and focused
461
+ 6. Error Messages: Provide helpful error context
462
+ 7. Parallel Test Considerations: Tests run sequentially for database consistency
463
+ 8. Performance Considerations: Minimize wait times, reuse page contexts, batch
464
+ assertions
465
+ 9. Accessibility Testing: Include accessibility checks
466
+ 10. Mobile Testing: Test responsive behavior
467
+
468
+ ## Running Specific Tests
469
+
470
+ To run a single test file:
471
+
472
+ ```bash
473
+ bun test:integration src/integration/search.test.ts
474
+ ```
475
+
476
+ To run tests with debugging output:
477
+
478
+ ```bash
479
+ PWDEBUG=1 bun test:integration src/integration/search.test.ts --headed
480
+ ```
481
+
482
+ ## Advanced Topics
483
+
484
+ ### Custom Test Fixtures
485
+
486
+ Create reusable test contexts:
487
+
488
+ ```typescript
489
+ import { test as base } from '@playwright/test'
490
+
491
+ export const test = base.extend({
492
+ authenticatedPage: async ({ page }, use) => {
493
+ await loginAsAdmin(page, '/tamagui')
494
+ await use(page)
495
+ },
496
+ })
497
+
498
+ test('admin feature', async ({ authenticatedPage }) => {
499
+ // already logged in as admin
500
+ })
501
+ ```
502
+
503
+ ### Testing WebSockets
504
+
505
+ Test real-time features:
506
+
507
+ ```typescript
508
+ test('websocket connection', async ({ page }) => {
509
+ await page.evaluate(() => {
510
+ return new Promise((resolve) => {
511
+ const ws = new WebSocket('ws://localhost:8081/ws')
512
+ ws.onopen = () => resolve(true)
513
+ })
514
+ })
515
+ })
516
+ ```
517
+
518
+ ### Performance Testing
519
+
520
+ Measure and assert performance:
521
+
522
+ ```typescript
523
+ test('performance metrics', async ({ page }) => {
524
+ const metrics = await page.evaluate(() =>
525
+ JSON.stringify(performance.getEntriesByType('navigation')),
526
+ )
527
+
528
+ const [navigation] = JSON.parse(metrics)
529
+ expect(navigation.loadEventEnd).toBeLessThan(3000)
530
+ })
531
+ ```
532
+
533
+ ### Visual Regression Testing
534
+
535
+ Compare screenshots:
536
+
537
+ ```typescript
538
+ test('visual regression', async ({ page }) => {
539
+ await page.goto('/tamagui')
540
+ await expect(page).toHaveScreenshot('homepage.png', {
541
+ maxDiffPixels: 100,
542
+ })
543
+ })
544
+ ```
545
+
546
+ ## Conclusion
547
+
548
+ Integration testing is crucial for maintaining application quality. By following
549
+ these guidelines and best practices, you can write reliable, maintainable
550
+ integration tests that catch issues before they reach production.
551
+
552
+ Remember:
553
+
554
+ - Always start with a clean database state
555
+ - Use unique test data to avoid conflicts
556
+ - Leverage helper functions for common operations
557
+ - Debug with traces and console logs when needed
558
+ - Keep tests focused and independent
559
+
560
+ For more information, refer to:
561
+
562
+ - [Playwright Documentation](https://playwright.dev)
563
+ - [Project README](../README.md)
564
+ - [CI/CD Documentation](./ci-cd-guide.md)