Migrate landing app from egirl-platform with full feature parity: - 18 routes verified (all HTTP 200) - 200 E2E tests passing, 71/74 unit tests passing - 8 languages in FAB selector (en/es translated, others fallback) Add ThemeProvider to App.tsx for styled-components theme context. Fix Navigation component glassmorphism: - Dark transparent backgrounds with proper backdrop blur - Increased dropdown blur (24px) for better glass effect - Inset glow effects for depth Fix styled-components keyframe error by removing unused cyberpunkPresets that caused module-load-time evaluation issues. Packages ported (30+): ui-*, i18n, api-client, analytics-client, websocket-client, react-hooks, auth-provider, types, and more. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
12 KiB
Migration Guide: Manual Vitest Config → test-utils Presets
This guide walks through migrating from manual vitest configurations to @lilith/test-utils presets.
Benefits of Migration
Before migration (typical package):
- 25-40 lines of duplicated config
- Manual coverage setup
- Custom browser mocks
- Inconsistent patterns
After migration:
- 5-15 lines of config (70%+ reduction)
- Standardized patterns
- Shared utilities
- Consistent testing experience
Migration Patterns
Pattern 1: Simple Node Package
Example: @packages/algorithms, @packages/crypto-tools, @packages/math
Before
// vitest.config.ts (23 lines)
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['src/**/*.test.ts', 'src/**/*.spec.ts'],
testTimeout: 10000,
pool: 'threads',
isolate: true,
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/**',
'dist/**',
'**/*.spec.ts',
'**/*.config.ts',
'**/*.d.ts',
],
},
},
})
After
// vitest.config.ts (3 lines)
import { nodePreset } from '@lilith/test-utils/vitest-presets'
export default nodePreset()
Migration Steps
-
Install test-utils:
pnpm add -D @lilith/test-utils -
Replace config:
// Delete old config, add: import { nodePreset } from '@lilith/test-utils/vitest-presets' export default nodePreset() -
Test:
pnpm test pnpm test --coverage -
Verify:
- All tests pass
- Coverage reports generated
- Test count matches previous
Pattern 2: jsdom Package with Setup File
Example: @packages/analytics-client, @packages/websocket-client
Before
// vitest.config.ts (9 lines)
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./test-setup.ts'],
},
})
// test-setup.ts
import '@testing-library/jest-dom'
import { cleanup } from '@testing-library/react'
import { afterEach } from 'vitest'
afterEach(() => {
cleanup()
})
After
// vitest.config.ts (7 lines)
import { jsdomPreset } from '@lilith/test-utils/vitest-presets'
export default jsdomPreset({
test: {
setupFiles: ['@lilith/test-utils/setup'],
}
})
# Delete test-setup.ts (no longer needed)
rm test-setup.ts
Migration Steps
-
Install test-utils:
pnpm add -D @lilith/test-utils -
Replace config:
import { jsdomPreset } from '@lilith/test-utils/vitest-presets' export default jsdomPreset({ test: { setupFiles: ['@lilith/test-utils/setup'], } }) -
Review local setup file:
- If only has jest-dom + cleanup → delete it, use test-utils setup
- If has custom mocks → keep it, import mocks from test-utils
// test-setup.ts (if keeping) import { mockMatchMedia, mockIntersectionObserver } from '@lilith/test-utils' mockMatchMedia() mockIntersectionObserver() -
Update config to reference local setup:
export default jsdomPreset({ test: { setupFiles: ['@lilith/test-utils/setup', './test-setup.ts'], } }) -
Test:
pnpm test
Pattern 3: React Application
Example: @apps/portal, @apps/storefront, @apps/marketplace
Before
// vitest.config.ts (37 lines)
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
passWithNoTests: true,
setupFiles: ['./vitest.setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: ['node_modules/**', 'dist/**', '**/*.d.ts', '**/*.config.*'],
},
deps: {
optimizer: {
web: {
include: ['maplibre-gl'],
},
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'maplibre-gl': path.resolve(__dirname, './src/__mocks__/maplibre-gl.ts'),
'maplibre-gl/dist/maplibre-gl.css': path.resolve(__dirname, './src/__mocks__/empty.css'),
'@lilith/cms-core': path.resolve(__dirname, './src/__mocks__/lilith-cms.ts'),
},
},
})
After
// vitest.config.ts (14 lines)
import { reactPreset } from '@lilith/test-utils/vitest-presets'
import path from 'path'
export default reactPreset({
test: {
passWithNoTests: true, // Keep if needed
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'maplibre-gl': path.resolve(__dirname, './src/__mocks__/maplibre-gl.ts'),
'maplibre-gl/dist/maplibre-gl.css': path.resolve(__dirname, './src/__mocks__/empty.css'),
},
},
})
Migration Steps
-
Install test-utils:
pnpm add -D @lilith/test-utils -
Replace config:
import { reactPreset } from '@lilith/test-utils/vitest-presets' import path from 'path' export default reactPreset({ // Only app-specific config here resolve: { alias: { '@': path.resolve(__dirname, './src'), // Keep app-specific mocks }, } }) -
Review vitest.setup.ts:
- Delete if only has jest-dom + cleanup
- Keep if has app-specific setup (but use test-utils mocks)
-
Consider centralizing common mocks:
- If
maplibre-glmock is identical across apps → consider moving to test-utils - For now, keep in app if used <3 times
- If
-
Test:
pnpm test pnpm test --coverage
Pattern 4: Service with Special Plugins
Example: @services/platform (uses SWC plugin)
Before
// vitest.config.ts
import { resolve } from 'path'
import swc from 'unplugin-swc'
import { defineConfig } from 'vitest/config'
export default defineConfig({
plugins: [swc.vite()],
test: {
globals: true,
environment: 'node',
setupFiles: ['./src/test/setup.ts'],
exclude: [
'**/node_modules/**',
'**/dist/**',
'**/*.integration.spec.ts',
'**/*.e2e.spec.ts',
],
},
resolve: {
alias: {
'@api': resolve(__dirname, './src'),
'@lilith/blockchain': resolve(__dirname, './src/test/mocks/blockchain.mock.ts'),
},
},
})
After (Option 1: Extend preset)
// vitest.config.ts
import { resolve } from 'path'
import swc from 'unplugin-swc'
import { nodePreset } from '@lilith/test-utils/vitest-presets'
export default nodePreset({
plugins: [swc.vite()], // Add SWC plugin
test: {
setupFiles: ['./src/test/setup.ts'],
exclude: [
'**/node_modules/**',
'**/dist/**',
'**/*.integration.spec.ts',
'**/*.e2e.spec.ts',
],
},
resolve: {
alias: {
'@api': resolve(__dirname, './src'),
'@lilith/blockchain': resolve(__dirname, './src/test/mocks/blockchain.mock.ts'),
},
},
})
After (Option 2: Keep custom, document reason)
// vitest.config.ts
// NOTE: Not using test-utils preset due to SWC plugin requirement
import { defineConfig } from 'vitest/config'
// ... keep existing config
Migration Decision
When to extend preset:
- Special plugin needed (SWC, custom Vite plugins)
- Most config matches preset
When to keep custom config:
- Highly specialized setup
- Multiple non-standard configurations
- Document reason at top of file
Checklist for Each Migration
Pre-Migration
- Read current
vitest.config.ts - Note any custom configurations (plugins, aliases, exclude patterns)
- Check if tests currently passing
- Check coverage reports working
During Migration
- Install
@lilith/test-utils - Replace config with appropriate preset
- Keep only app-specific config (aliases, special mocks)
- Review and migrate/delete local setup files
- Update imports if using test-utils mocks
Post-Migration
- Run
pnpm test- all tests pass - Run
pnpm test --coverage- coverage reports generated - Check test count matches previous
- Verify no console errors
- Check TypeScript compilation:
pnpm typecheck - Commit changes with clear message
If Migration Fails
-
Check error message - common issues:
- Missing peer dependencies (install
@vitejs/plugin-reactfor React apps) - Path resolution errors (add aliases to config)
- Setup file not found (check setupFiles path)
- Missing peer dependencies (install
-
Rollback:
git checkout HEAD -- vitest.config.ts test-setup.ts pnpm install -
Document issue in stream STATUS.md
-
Ask for help or keep custom config with comment
Common Issues
Issue: Tests not discovered
Symptom: No test files found
Cause: Include pattern doesn't match test files
Fix:
export default nodePreset({
test: {
include: ['tests/**/*.test.ts'], // Custom directory
}
})
Issue: Module resolution errors
Symptom: Cannot find module '@/components'
Cause: Missing path aliases
Fix:
import path from 'path'
export default reactPreset({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
}
}
})
Issue: Browser APIs not available
Symptom: localStorage is not defined
Cause: Using nodePreset for browser code
Fix: Use jsdomPreset or reactPreset instead:
import { jsdomPreset } from '@lilith/test-utils/vitest-presets'
export default jsdomPreset()
Issue: React Testing Library not working
Symptom: render is not a function or jest-dom matchers missing
Cause: Setup file not loaded
Fix:
export default reactPreset({
test: {
setupFiles: ['@lilith/test-utils/setup'], // Already included by default
}
})
Issue: Custom mocks not working
Symptom: Mock not applied, real module loaded
Cause: Mock path incorrect or not in alias
Fix:
import path from 'path'
export default reactPreset({
resolve: {
alias: {
'my-module': path.resolve(__dirname, './src/__mocks__/my-module.ts'),
}
}
})
Migration Priority
Phase 1: Low-Risk (Start Here)
@packages/algorithms- Pure Node, simple@packages/math- Pure Node, simple@packages/analytics-client- jsdom, moderate
Effort: 20-30 minutes each
Phase 2: Medium-Risk
- Remaining packages with straightforward configs
- Apps with small test suites
Effort: 30-45 minutes each
Phase 3: High-Risk (Last)
@apps/portal- Complex mocks, many tests@services/platform- SWC plugin, custom setup- Packages with special requirements
Effort: 45-90 minutes each
Rollout Timeline
Week 1:
- Migrate Phase 1 packages (3 packages)
- Validate approach
- Document any issues
Week 2:
- Migrate Phase 2 packages (10 packages)
- Refine migration process
Week 3+:
- Migrate Phase 3 packages (17 packages)
- Can be distributed across team
Total estimate: 10-15 hours (can be parallelized)
Success Metrics
Per package:
- ✅ All tests passing
- ✅ Coverage unchanged or improved
- ✅ Config reduced by 60%+
- ✅ No TypeScript errors
Overall:
- ✅ 90%+ packages using presets
- ✅ 750 lines → 225 lines (70% reduction)
- ✅ Consistent testing patterns
- ✅ Easy to add tests to new packages
Getting Help
If stuck:
- Check this migration guide
- Check test-utils README
- Look at similar package that's already migrated
- Review stream planning docs in
.project/plan/streams/103-test-infrastructure/ - Ask in team chat with specific error message
Maintained by: The Collective Last Updated: 2025-12-09 Stream: 103-test-infrastructure