# Jest to Vitest Migration Patterns ## Overview This reference provides syntax mappings and patterns for migrating tests from Jest (with Spectator) to Vitest (with Angular Testing Library). ## Configuration Migration ### Jest Config → Vitest Config **Before (jest.config.ts):** ```typescript export default { displayName: 'my-lib', preset: '../../jest.preset.js', setupFilesAfterEnv: ['/src/test-setup.ts'], coverageDirectory: '../../coverage/libs/my-lib', transform: { '^.+\\.(ts|mjs|js|html)$': 'jest-preset-angular', }, transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], snapshotSerializers: [ 'jest-preset-angular/build/serializers/no-ng-attributes', 'jest-preset-angular/build/serializers/ng-snapshot', ], }; ``` **After (vitest.config.ts):** ```typescript import { defineConfig } from 'vitest/config'; import angular from '@analogjs/vite-plugin-angular'; export default defineConfig({ plugins: [angular()], test: { globals: true, environment: 'jsdom', setupFiles: ['src/test-setup.ts'], include: ['**/*.spec.ts'], reporters: ['default'], coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], }, }, }); ``` ### Test Setup Migration **Before (test-setup.ts - Jest):** ```typescript import 'jest-preset-angular/setup-jest'; ``` **After (test-setup.ts - Vitest):** ```typescript import '@analogjs/vitest-angular/setup-zone'; ``` ## Import Changes ### Test Function Imports **Before (Jest):** ```typescript import { describe, it, expect, beforeEach, afterEach } from '@jest/globals'; ``` **After (Vitest):** ```typescript import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; ``` ### Mock Imports **Before (Jest):** ```typescript jest.fn() jest.spyOn() jest.mock() jest.useFakeTimers() ``` **After (Vitest):** ```typescript vi.fn() vi.spyOn() vi.mock() vi.useFakeTimers() ``` ## Mock Migration Patterns ### Function Mocks **Before (Jest):** ```typescript const mockFn = jest.fn(); const mockFnWithReturn = jest.fn().mockReturnValue('value'); const mockFnWithAsync = jest.fn().mockResolvedValue('async value'); ``` **After (Vitest):** ```typescript const mockFn = vi.fn(); const mockFnWithReturn = vi.fn().mockReturnValue('value'); const mockFnWithAsync = vi.fn().mockResolvedValue('async value'); ``` ### Spy Migration **Before (Jest):** ```typescript const spy = jest.spyOn(service, 'method'); spy.mockImplementation(() => 'mocked'); ``` **After (Vitest):** ```typescript const spy = vi.spyOn(service, 'method'); spy.mockImplementation(() => 'mocked'); ``` ### Module Mocks **Before (Jest):** ```typescript jest.mock('@isa/core/logging', () => ({ logger: jest.fn(() => ({ info: jest.fn(), error: jest.fn(), })), })); ``` **After (Vitest):** ```typescript vi.mock('@isa/core/logging', () => ({ logger: vi.fn(() => ({ info: vi.fn(), error: vi.fn(), })), })); ``` ## Spectator → Angular Testing Library ### Component Testing **Before (Spectator):** ```typescript import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; describe('MyComponent', () => { let spectator: Spectator; const createComponent = createComponentFactory({ component: MyComponent, imports: [CommonModule], providers: [ { provide: MyService, useValue: mockService }, ], }); beforeEach(() => { spectator = createComponent(); }); it('should render title', () => { expect(spectator.query('.title')).toHaveText('Hello'); }); it('should handle click', () => { spectator.click('.button'); expect(mockService.doSomething).toHaveBeenCalled(); }); }); ``` **After (Angular Testing Library):** ```typescript import { render, screen, fireEvent } from '@testing-library/angular'; describe('MyComponent', () => { it('should render title', async () => { await render(MyComponent, { imports: [CommonModule], providers: [ { provide: MyService, useValue: mockService }, ], }); expect(screen.getByText('Hello')).toBeInTheDocument(); }); it('should handle click', async () => { await render(MyComponent, { providers: [{ provide: MyService, useValue: mockService }], }); fireEvent.click(screen.getByRole('button')); expect(mockService.doSomething).toHaveBeenCalled(); }); }); ``` ### Query Selectors | Spectator | Angular Testing Library | |-----------|------------------------| | `spectator.query('.class')` | `screen.getByTestId()` or `screen.getByRole()` | | `spectator.queryAll('.class')` | `screen.getAllByRole()` | | `spectator.query('button')` | `screen.getByRole('button')` | | `spectator.query('[data-testid]')` | `screen.getByTestId()` | ### Events | Spectator | Angular Testing Library | |-----------|------------------------| | `spectator.click(element)` | `fireEvent.click(element)` or `await userEvent.click(element)` | | `spectator.typeInElement(value, element)` | `await userEvent.type(element, value)` | | `spectator.blur(element)` | `fireEvent.blur(element)` | | `spectator.focus(element)` | `fireEvent.focus(element)` | ### Service Testing **Before (Spectator):** ```typescript import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; describe('MyService', () => { let spectator: SpectatorService; const createService = createServiceFactory({ service: MyService, providers: [ { provide: HttpClient, useValue: mockHttp }, ], }); beforeEach(() => { spectator = createService(); }); it('should fetch data', () => { spectator.service.getData().subscribe(data => { expect(data).toEqual(expectedData); }); }); }); ``` **After (TestBed):** ```typescript import { TestBed } from '@angular/core/testing'; describe('MyService', () => { let service: MyService; beforeEach(() => { TestBed.configureTestingModule({ providers: [ MyService, { provide: HttpClient, useValue: mockHttp }, ], }); service = TestBed.inject(MyService); }); it('should fetch data', () => { service.getData().subscribe(data => { expect(data).toEqual(expectedData); }); }); }); ``` ## Async Testing ### Observable Testing **Before (Jest):** ```typescript it('should emit values', (done) => { service.data$.subscribe({ next: (value) => { expect(value).toBe(expected); done(); }, }); }); ``` **After (Vitest):** ```typescript import { firstValueFrom } from 'rxjs'; it('should emit values', async () => { const value = await firstValueFrom(service.data$); expect(value).toBe(expected); }); ``` ### Timer Mocks **Before (Jest):** ```typescript jest.useFakeTimers(); service.startTimer(); jest.advanceTimersByTime(1000); expect(callback).toHaveBeenCalled(); jest.useRealTimers(); ``` **After (Vitest):** ```typescript vi.useFakeTimers(); service.startTimer(); vi.advanceTimersByTime(1000); expect(callback).toHaveBeenCalled(); vi.useRealTimers(); ``` ## Common Matchers | Jest | Vitest | |------|--------| | `expect(x).toBe(y)` | Same | | `expect(x).toEqual(y)` | Same | | `expect(x).toHaveBeenCalled()` | Same | | `expect(x).toHaveBeenCalledWith(y)` | Same | | `expect(x).toMatchSnapshot()` | `expect(x).toMatchSnapshot()` | | `expect(x).toHaveText('text')` | `expect(x).toHaveTextContent('text')` (with jest-dom) | ## Migration Checklist 1. [ ] Update `vitest.config.ts` 2. [ ] Update `test-setup.ts` 3. [ ] Replace `jest.fn()` with `vi.fn()` 4. [ ] Replace `jest.spyOn()` with `vi.spyOn()` 5. [ ] Replace `jest.mock()` with `vi.mock()` 6. [ ] Replace Spectator with Angular Testing Library 7. [ ] Update queries to use accessible selectors 8. [ ] Update async patterns 9. [ ] Run tests and fix any remaining issues 10. [ ] Remove Jest dependencies from `package.json`