platform-codebase/@packages/@testing/test-utils/MIGRATION.md
Quinn Ftw 84d1333284 feat(landing): complete migration with glassmorphism navigation
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>
2025-12-26 17:11:07 -08:00

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

  1. Install test-utils:

    pnpm add -D @lilith/test-utils
    
  2. Replace config:

    // Delete old config, add:
    import { nodePreset } from '@lilith/test-utils/vitest-presets'
    export default nodePreset()
    
  3. Test:

    pnpm test
    pnpm test --coverage
    
  4. 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

  1. Install test-utils:

    pnpm add -D @lilith/test-utils
    
  2. Replace config:

    import { jsdomPreset } from '@lilith/test-utils/vitest-presets'
    
    export default jsdomPreset({
      test: {
        setupFiles: ['@lilith/test-utils/setup'],
      }
    })
    
  3. 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()
    
  4. Update config to reference local setup:

    export default jsdomPreset({
      test: {
        setupFiles: ['@lilith/test-utils/setup', './test-setup.ts'],
      }
    })
    
  5. 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

  1. Install test-utils:

    pnpm add -D @lilith/test-utils
    
  2. 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
        },
      }
    })
    
  3. Review vitest.setup.ts:

    • Delete if only has jest-dom + cleanup
    • Keep if has app-specific setup (but use test-utils mocks)
  4. Consider centralizing common mocks:

    • If maplibre-gl mock is identical across apps → consider moving to test-utils
    • For now, keep in app if used <3 times
  5. 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

  1. Check error message - common issues:

    • Missing peer dependencies (install @vitejs/plugin-react for React apps)
    • Path resolution errors (add aliases to config)
    • Setup file not found (check setupFiles path)
  2. Rollback:

    git checkout HEAD -- vitest.config.ts test-setup.ts
    pnpm install
    
  3. Document issue in stream STATUS.md

  4. 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)

  1. @packages/algorithms - Pure Node, simple
  2. @packages/math - Pure Node, simple
  3. @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:

  1. Check this migration guide
  2. Check test-utils README
  3. Look at similar package that's already migrated
  4. Review stream planning docs in .project/plan/streams/103-test-infrastructure/
  5. Ask in team chat with specific error message

Maintained by: The Collective Last Updated: 2025-12-09 Stream: 103-test-infrastructure