Skip to content

Upgrade to Vitest and enhance testing framework#336

Merged
Starefossen merged 13 commits intomainfrom
no-more-jest
Feb 16, 2026
Merged

Upgrade to Vitest and enhance testing framework#336
Starefossen merged 13 commits intomainfrom
no-more-jest

Conversation

@Starefossen
Copy link
Member

@Starefossen Starefossen commented Feb 16, 2026

User description

Transition from Jest to Vitest for testing framework, including configuration updates and improved test coverage for various components. Cleaned up unnecessary spaces in environment variable assignments and mock implementations. Added comprehensive tests for proposal validation, video handling, and utility functions. Removed outdated tests and introduced new utility functions for ticket calculations.


PR Type

Tests, Enhancement


Description

  • Complete migration from Jest to Vitest testing framework across 50+ test files with updated imports, mock declarations, and environment directives

  • Added comprehensive test coverage for proposal validation, video handling, business utilities, and schema validation with 130+ new test cases

  • Enhanced test infrastructure with new Vitest configuration files (vitest.config.ts, vitest.setup.ts) and updated ESLint configuration

  • Improved test utilities including new helper functions (makeTier(), makeProposal(), createMockTransaction()) to reduce boilerplate and improve maintainability

  • Updated dependencies in package.json and pnpm-lock.yaml to replace Jest with Vitest and coverage tools

  • Added testing guidelines documentation in AGENTS.md covering Vitest best practices, test philosophy, and mocking strategies

  • Removed outdated tests (6 test files deleted) and cleaned up unnecessary test fixtures and mock implementations


Diagram Walkthrough

flowchart LR
  Jest["Jest Framework<br/>jest.fn, jest.mock<br/>jest.config.ts"]
  Vitest["Vitest Framework<br/>vi.fn, vi.mock<br/>vitest.config.ts"]
  Tests["50+ Test Files<br/>Updated imports<br/>New test cases"]
  Config["Configuration<br/>vitest.setup.ts<br/>ESLint updates"]
  Deps["Dependencies<br/>package.json<br/>pnpm-lock.yaml"]
  
  Jest -->|"Migrate to"| Vitest
  Vitest -->|"Applied to"| Tests
  Vitest -->|"Requires"| Config
  Vitest -->|"Updates"| Deps
  Tests -->|"Adds coverage for"| Proposal["Proposal validation<br/>Video handling<br/>Business utilities"]
Loading

File Walkthrough

Relevant files
Tests
43 files
config.test.ts
Migrate environment config tests from Jest to Vitest         

tests/lib/environment/config.test.ts

  • Replaced Jest imports and jest.resetModules() with Vitest equivalents
    (vi.resetModules())
  • Converted jest.isolateModules() pattern to async importFresh() helper
    function using dynamic imports
  • Made all test functions async to support the new module import pattern
  • Removed redundant comments and cleaned up test structure
+132/-186
bulk.test.ts
Migrate sponsor CRM bulk operations tests to Vitest           

tests/lib/sponsor-crm/bulk.test.ts

  • Replaced Jest mock declarations with Vitest vi.mock() calls
  • Extracted createMockTransaction() helper function to reduce
    duplication
  • Updated all jest.fn() calls to vi.fn() and jest.clearAllMocks() to
    vi.clearAllMocks()
  • Added new test cases for edge cases: empty sponsor lists, unchanged
    status, transaction failures, and assignee name resolution
  • Simplified test assertions and improved test descriptions
+151/-118
utils.test.ts
Enhance sponsor utilities tests with Vitest and new test cases

tests/lib/sponsor/utils.test.ts

  • Removed Jest imports and replaced with Vitest equivalents
  • Introduced makeTier() helper function to reduce test fixture
    boilerplate
  • Added comprehensive new tests for groupSponsorsByTier() and
    getDailySeed() functions
  • Expanded test coverage for edge cases: empty arrays, equal prices,
    missing prices, and input mutation
  • Reorganized tests into focused describe blocks with clearer test
    descriptions
+223/-102
validation.test.ts
Expand sponsor validation test coverage with Vitest           

tests/lib/sponsor/validation.test.ts

  • Removed Jest imports; tests now use Vitest globals implicitly
  • Added extensive validation tests for title, tagline, tierType, and
    conference fields
  • Added tests for negative prices, invalid currencies, and empty perk
    fields
  • Introduced tests for multiple validation errors reported
    simultaneously
  • Expanded sponsor validation with character limit, URL format, and SVG
    content checks
+224/-35
actions.test.ts
Migrate dashboard actions tests to Vitest with improved imports

tests/lib/dashboard/actions.test.ts

  • Replaced all jest.mock() calls with vi.mock() and jest.fn() with
    vi.fn()
  • Changed from require() to ES6 import for action functions (Vitest
    hoists mocks automatically)
  • Updated timer utilities from jest.useFakeTimers() to
    vi.useFakeTimers() and related methods
  • Replaced jest.clearAllMocks() with vi.clearAllMocks()
  • Simplified mock setup by removing manual require() workarounds
+44/-77 
state.test.ts
Migrate conference state tests to Vitest environment         

tests/lib/conference/state.test.ts

  • Changed environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports; replaced all jest.* calls with vi.* equivalents
  • Updated timer methods: jest.useFakeTimers()vi.useFakeTimers(),
    jest.setSystemTime()vi.setSystemTime(), etc.
  • Maintained all test logic and assertions unchanged
+26/-34 
chapter9-test-vector.test.ts
Clean up OpenBadges test imports and documentation             

tests/lib/openbadges/chapter9-test-vector.test.ts

  • Removed Jest imports from test file
  • Updated documentation comment to be more concise
  • Removed redundant test vector explanation comments
+1/-157 
schemas.test.ts
Add comprehensive proposal schema validation tests             

tests/lib/proposal/schemas.test.ts

  • Removed Jest imports; tests now use Vitest globals implicitly
  • Added comprehensive tests for new schemas: ProposalActionSchema,
    ProposalAdminCreateSchema, ProposalUpdateSchema,
    InvitationCreateSchema, InvitationResponseSchema,
    InvitationCancelSchema, and AudienceFeedbackSchema
  • Added validation tests for enum values, required fields,
    transformations, and edge cases
  • Expanded test coverage for invitation workflows and audience feedback
    validation
+275/-2 
business-utils.test.ts
Add proposal business utilities test coverage                       

tests/lib/proposal/business-utils.test.ts

  • New test file for proposal business utility functions
  • Added tests for calculateAverageRating() covering undefined/empty
    reviews, single/multiple reviews, and edge cases
  • Added tests for getProposalSpeakerNames() covering single/multiple
    speakers, references, and fallback behavior
  • Includes helper functions makeProposal() and makeReview() for test
    fixture creation
+197/-0 
utils.test.ts
Add proposal utilities test coverage                                         

tests/lib/proposal/utils.test.ts

  • New test file for proposal utility functions
  • Added tests for extractSpeakersFromProposal() covering filtering,
    references, and null handling
  • Added tests for extractSpeakerIds() covering mixed input types and ID
    extraction
  • Added tests for calculateReviewScore() covering average calculations
    and missing fields
  • Includes makeProposal() helper for consistent test fixture creation
+205/-0 
validation.test.ts
Add proposal form validation test coverage                             

tests/lib/proposal/validation.test.ts

  • New test file for proposal validation functions
  • Added tests for validateProposalForm() covering required fields,
    format validation, and capacity requirements
  • Added tests for validateProposalForAdmin() covering speaker validation
    and combined errors
  • Tests cover edge cases: whitespace-only inputs, undefined values, and
    multiple simultaneous errors
  • Includes makeValidProposal() helper and validation message constants
+198/-0 
phase.test.ts
Migrate conference phase tests to Vitest environment         

tests/lib/conference/phase.test.ts

  • Changed environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports; replaced all jest.* calls with vi.* equivalents
  • Updated timer methods: jest.useFakeTimers()vi.useFakeTimers(),
    jest.setSystemTime()vi.setSystemTime(), etc.
  • Maintained all test logic and assertions unchanged
+18/-26 
provider.test.ts
Migrate contract signing provider tests to Vitest               

tests/lib/contract-signing/provider.test.ts

  • Changed environment directive from @jest-environment node to
    @vitest-environment node
  • Replaced all jest.fn() calls with vi.fn() for mock functions
  • Replaced jest.mock() with vi.mock() for module mocking
  • Updated timer methods: jest.useFakeTimers()vi.useFakeTimers(),
    jest.advanceTimersByTimeAsync()vi.advanceTimersByTimeAsync(), etc.
  • Replaced jest.clearAllMocks() with vi.clearAllMocks()
+22/-23 
grid-utils.test.ts
Migrate grid utils tests and add reflow function tests     

tests/lib/dashboard/grid-utils.test.ts

  • Changed environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports; tests now use Vitest globals implicitly
  • Added comprehensive tests for new reflowWidgetsForColumns() function
    covering desktop/tablet/mobile layouts
  • Tests cover widget stacking, column clamping, wrapping, sorting, and
    edge cases
+90/-2   
weekly-update.test.ts
Migrate weekly update cron tests to Vitest                             

tests/api/cron/weekly-update.test.ts

  • Changed environment directive from @jest-environment node to
    @vitest-environment node
  • Removed Jest imports; tests now use Vitest globals implicitly
+1/-50   
sanity-client.ts
Migrate Sanity client mock to Vitest                                         

tests/mocks/sanity-client.ts

  • Replaced Jest import with Vitest vi import
  • Updated all jest.fn() calls to vi.fn() throughout the mock factory
  • Maintained identical mock structure and behavior
+11/-11 
client.test.ts
Migrate Jest test to Vitest framework                                       

tests/lib/slack/client.test.ts

  • Replaced Jest imports and configuration with Vitest equivalents (vi
    instead of jest)
  • Updated environment variable assignment from Object.defineProperty to
    direct property assignment for better Vitest compatibility
  • Changed mock function declarations from jest.fn() to vi.fn() and
    updated type imports to use MockedFunction from vitest
  • Updated test lifecycle methods from jest.resetModules() and
    jest.restoreAllMocks() to Vitest equivalents
+15/-25 
video-and-types.test.ts
Add tests for proposal video and format utilities               

tests/lib/proposal/video-and-types.test.ts

  • New comprehensive test file for proposal video handling and format
    type checking
  • Added 130 lines of test coverage for getProposalVideoUrl,
    hasProposalVideo, and isWorkshopFormat functions
  • Tests cover edge cases including undefined attachments, empty arrays,
    multiple recordings, and various format types
+130/-0 
delete.test.ts
Migrate sponsor CRM delete tests to Vitest                             

tests/lib/sponsor-crm/delete.test.ts

  • Removed Jest imports and replaced all jest.mock() calls with vi.mock()
  • Updated all mock function declarations from jest.fn() to vi.fn()
  • Changed jest.clearAllMocks() to vi.clearAllMocks() in beforeEach hook
+21/-23 
utils.test.ts
Migrate ticket utils tests and add new utility tests         

tests/lib/tickets/utils.test.ts

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports from @jest/globals
  • Added new test suites for calculateSponsorTickets and
    createDefaultAnalysis utility functions with comprehensive edge case
    coverage
  • New tests validate sponsor ticket grouping, tier allocation, and
    default analysis structure
+96/-2   
delete.test.ts
Migrate sponsor delete tests to Vitest                                     

tests/lib/sponsor/delete.test.ts

  • Removed Jest imports and replaced all jest.mock() calls with vi.mock()
  • Updated all mock function declarations from jest.fn() to vi.fn()
  • Changed jest.clearAllMocks() to vi.clearAllMocks() in beforeEach hook
+18/-20 
contract-reminders.test.ts
Migrate contract reminders cron test to Vitest                     

tests/api/cron/contract-reminders.test.ts

  • Updated environment directive from @jest-environment node to
    @vitest-environment node
  • Removed Jest imports and replaced all jest.mock() and jest.fn() calls
    with Vitest equivalents
  • Changed jest.clearAllMocks() to vi.clearAllMocks() in beforeEach hook
+16/-24 
adobe-sign.test.ts
Migrate Adobe Sign webhook test to Vitest                               

tests/api/webhooks/adobe-sign.test.ts

  • Updated environment directive from @jest-environment node to
    @vitest-environment node
  • Removed Jest imports and replaced all jest.mock() and jest.fn() calls
    with Vitest equivalents
  • Changed jest.clearAllMocks() to vi.clearAllMocks() and jest.spyOn() to
    vi.spyOn()
+13/-21 
weeklyUpdate.test.ts
Migrate Slack weekly update test to Vitest                             

tests/lib/slack/weeklyUpdate.test.ts

  • Updated environment directive from @jest-environment node to
    @vitest-environment node
  • Removed Jest imports and replaced all jest references with vi
    equivalents
  • Updated environment variable assignment from Object.defineProperty to
    direct property assignment
  • Changed mock function type from jest.MockedFunction to MockedFunction
    from vitest
+9/-24   
notify.test.ts
Migrate Slack notify test to Vitest                                           

tests/lib/slack/notify.test.ts

  • Updated environment directive from @jest-environment node to
    @vitest-environment node
  • Removed Jest imports and replaced all jest.mock() and jest.fn() calls
    with Vitest equivalents
  • Updated mock function type from jest.MockedFunction to MockedFunction
    from vitest
  • Changed jest.restoreAllMocks() to vi.restoreAllMocks() and
    jest.spyOn() to vi.spyOn()
+8/-15   
registration.test.ts
Migrate sponsor CRM registration test to Vitest                   

tests/lib/sponsor-crm/registration.test.ts

  • Removed Jest imports and replaced all jest.mock() and jest.fn() calls
    with Vitest equivalents
  • Updated type import from jest.Mock to Mock from vitest
  • Changed jest.clearAllMocks() to vi.clearAllMocks()
+11/-11 
validation.test.ts
Migrate attachment validation test to Vitest                         

tests/lib/attachment/validation.test.ts

  • Removed Jest imports from @jest/globals
  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Optimized large file creation tests by replacing string array with
    Uint8Array for better performance
+2/-3     
client.test.ts
Migrate Adobe Sign client test to Vitest                                 

tests/lib/adobe-sign/client.test.ts

  • Updated environment directive from @jest-environment node to
    @vitest-environment node
  • Removed Jest imports and replaced jest.fn() with vi.fn()
  • Updated mock function type from jest.MockedFunction to MockedFunction
    from vitest
  • Changed jest.resetModules() to vi.resetModules()
+4/-11   
next-auth.ts
Update next-auth mock for Vitest                                                 

tests/mocks/next-auth.ts

  • Replaced Jest import with Vitest vi import
  • Updated all jest.fn() calls to vi.fn() throughout the mock
    implementation
+6/-6     
openbadges.test.ts
Migrate openbadges test to Vitest                                               

tests/lib/openbadges/openbadges.test.ts

  • Removed Jest imports and replaced jest.unmock() calls with vi.unmock()
  • Removed redundant describe, it, expect imports from @jest/globals
+2/-4     
sanity-image-url.ts
Update Sanity image URL mock for Vitest                                   

tests/mocks/sanity-image-url.ts

  • Replaced Jest import with Vitest vi import
  • Updated all jest.fn() calls to vi.fn() in the mock implementation
+4/-4     
public.test.ts
Migrate ticket public test to Vitest                                         

tests/lib/tickets/public.test.ts

  • Removed Jest imports and replaced jest.mock() calls with vi.mock()
  • Updated mock function declarations from jest.fn() to vi.fn()
+3/-5     
placement-utils.test.ts
Migrate dashboard placement utils test to Vitest                 

tests/lib/dashboard/placement-utils.test.ts

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports from @jest/globals
+1/-2     
utils-deduplication.test.ts
Migrate ticket deduplication test to Vitest                           

tests/lib/tickets/utils-deduplication.test.ts

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports from @jest/globals
+1/-2     
widget-registry.test.ts
Migrate dashboard widget registry test to Vitest                 

tests/lib/dashboard/widget-registry.test.ts

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports from @jest/globals
+1/-2     
presets.test.ts
Migrate dashboard presets test to Vitest                                 

tests/lib/dashboard/presets.test.ts

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports from @jest/globals
+1/-2     
utils.test.ts
Migrate speaker utils test to Vitest                                         

tests/lib/speaker/utils.test.ts

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports from @jest/globals
+1/-2     
time.test.ts
Migrate time utilities test to Vitest                                       

tests/lib/time.test.ts

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports from @jest/globals
+1/-2     
processor.test.ts
Migrate ticket processor test to Vitest                                   

tests/lib/tickets/processor.test.ts

  • Updated environment directive from @jest-environment node to
    @vitest-environment node
  • Removed Jest imports from @jest/globals
+1/-2     
ContractSigningPage.test.tsx
Migrate contract signing page test to Vitest                         

tests/components/sponsor/ContractSigningPage.test.tsx

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports and replaced all jest.mock() and jest.fn() calls
    with Vitest equivalents
  • Removed @testing-library/jest-dom import (now handled by
    vitest.setup.ts)
  • Simplified test by using Vitest's automatic mock hoisting instead of
    require() workaround
+20/-30 
SponsorContractView.test.tsx
Migrate sponsor contract view test to Vitest                         

tests/components/admin/sponsor-crm/SponsorContractView.test.tsx

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports and replaced all jest.mock() and jest.fn() calls
    with Vitest equivalents
  • Removed @testing-library/jest-dom import (now handled by
    vitest.setup.ts)
  • Simplified test by using Vitest's automatic mock hoisting instead of
    require() workaround
+17/-27 
BoardViewSwitcher.test.tsx
Migrate board view switcher test to Vitest                             

tests/components/admin/sponsor-crm/BoardViewSwitcher.test.tsx

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports and replaced jest.fn() with vi.fn()
  • Removed @testing-library/jest-dom import (now handled by
    vitest.setup.ts)
  • Refactored test assertions to be more specific and behavior-focused
+13/-26 
OrganizerSignatureCapture.test.tsx
Migrate organizer signature capture test to Vitest             

tests/components/admin/sponsor-crm/OrganizerSignatureCapture.test.tsx

  • Updated environment directive from @jest-environment jsdom to
    @vitest-environment jsdom
  • Removed Jest imports and replaced all jest.mock() and jest.fn() calls
    with Vitest equivalents
  • Removed @testing-library/jest-dom import (now handled by
    vitest.setup.ts)
  • Simplified test by using Vitest's automatic mock hoisting instead of
    require() workaround
+6/-16   
Configuration changes
3 files
vitest.config.ts
Add Vitest configuration file                                                       

vitest.config.ts

  • New Vitest configuration file with path aliases matching Jest setup
  • Configured test environment as node with jsdom support via environment
    directive
  • Added module aliases for @, jose, @noble/ed25519, next-auth,
    next-sanity, and other dependencies
  • Configured test discovery patterns and setup files
+53/-0   
vitest.setup.ts
Add Vitest setup configuration file                                           

vitest.setup.ts

  • New Vitest setup file that imports testing library DOM matchers
  • Configures dotenv to load environment variables from .env, .env.local,
    and .env.test files
+4/-0     
eslint.config.js
Update ESLint config for Vitest files                                       

eslint.config.js

  • Updated ESLint configuration to recognize vitest.config.ts and
    vitest.setup.ts instead of Jest equivalents
+2/-2     
Documentation
2 files
noble-ed25519.ts
Update noble-ed25519 mock documentation                                   

tests/mocks/noble-ed25519.ts

  • Updated comment to remove Jest-specific reference, noting that
    @noble/ed25519 v3 is pure ESM
+1/-2     
AGENTS.md
Add comprehensive testing guidelines documentation             

AGENTS.md

  • Added comprehensive testing guidelines section covering Vitest and
    @testing-library/react
  • Documented test philosophy emphasizing behavior-driven testing and
    real source code validation
  • Provided guidance on what to test, what not to test, and best
    practices for mocking
  • Included examples of test structure, environment directives, and
    performance considerations
+87/-0   
Dependencies
2 files
package.json
Update package.json for Vitest migration                                 

package.json

  • Replaced Jest test scripts with Vitest equivalents (vitest run,
    vitest, etc.)
  • Removed Jest and ts-jest dependencies (@jest/globals, jest,
    jest-environment-jsdom, ts-jest)
  • Added Vitest and coverage provider dependencies (vitest,
    @vitest/coverage-v8)
+6/-8     
pnpm-lock.yaml
Migrate testing framework from Jest to Vitest with coverage support

pnpm-lock.yaml

  • Removed Jest-related dependencies (@jest/globals, jest,
    jest-environment-jsdom, ts-jest) from devDependencies
  • Added Vitest testing framework (vitest@4.0.18) and coverage tool
    (@vitest/coverage-v8@4.0.18) as devDependencies
  • Updated dependency resolution chains across multiple packages to
    include vitest instead of Jest-related packages
  • Removed transitive dependencies no longer needed with Vitest migration
    (handlebars, bs-logger, neo-async, uglify-js, lodash.memoize)
  • Added new Vitest-related packages and utilities (@vitest/mocker,
    @vitest/runner, @vitest/snapshot, tinybench, tinyexec,
    why-is-node-running)
+286/-177
Additional files
43 files
badge-e2e.test.ts +0/-1     
cleanup-orphaned-blobs.test.ts +0/-147 
proposal-attachment-route.test.ts +0/-200 
layout.test.tsx +0/-79   
MissingAvatar.test.tsx +1/-2     
SponsorLogo.test.tsx +0/-53   
SponsorProspectus.test.tsx +5/-8     
Sponsors.test.tsx +0/-219 
filters.test.ts +0/-1     
auth-impersonation.test.ts +0/-122 
validator-compliance.test.ts +0/-1     
speaker.test.ts.skip +0/-92   
format.test.ts +0/-1     
controller-validation.test.ts +0/-1     
golden-data.test.ts +0/-32   
golden-roundtrip.test.ts +0/-1     
spec-compliance.test.ts +0/-1     
attestation-page.test.ts +0/-1     
marker-detection.test.ts +0/-1     
signature-smoke.test.ts +0/-1     
state-machine.test.ts +0/-1     
action-items.test.ts +0/-1     
contract-readiness.test.ts +0/-1     
contract-schemas.test.ts +0/-1     
contract-status-schemas.test.ts +0/-1     
contract-variables.test.ts +0/-1     
crm-utils.test.ts +0/-1     
pipeline.test.ts +0/-1     
registration-schemas.test.ts +0/-1     
signing-pdf.test.ts +0/-257 
sponsorForConference.test.ts +0/-1     
templates.test.ts +0/-1     
schedule-utils.test.ts +0/-1     
svg.test.ts +0/-1     
capacity.test.ts +0/-1     
status.test.ts +0/-1     
utils.test.ts +0/-1     
impersonation-security.test.ts +0/-138 
jest.config.ts +0/-25   
jest.setup.ts +0/-10   
knip.json +0/-1     
service.test.ts +0/-1     
tsconfig.json +1/-0     

Greptile Summary

This PR successfully migrates the testing framework from Jest to Vitest across 94 files. The migration is comprehensive and well-executed:

Core Changes:

  • Removed Jest dependencies (jest, ts-jest, jest-environment-jsdom, @jest/globals)
  • Added Vitest dependencies (vitest, @vitest/coverage-v8)
  • Updated all test scripts in package.json to use Vitest commands
  • Created vitest.config.ts with proper module aliases for mocking external dependencies
  • Simplified vitest.setup.ts by removing TextEncoder/TextDecoder polyfills (Vitest provides these natively)

Test File Updates:

  • All 70+ test files migrated from Jest to Vitest syntax
  • Changed @jest-environment jsdom to @vitest-environment jsdom docblocks
  • Replaced jest.fn() with vi.fn() throughout mocks
  • Updated jest.mock() to vi.mock() and jest.unmock() to vi.unmock()
  • Removed explicit imports of describe, it, expect from @jest/globals (now globals via Vitest)
  • Migrated from jest.isolateModules() to vi.resetModules() + dynamic imports pattern

New Test Coverage:

  • Added comprehensive proposal validation tests (validation.test.ts)
  • Added video handling tests (video-and-types.test.ts)
  • Enhanced sponsor tier validation with boundary conditions
  • Added new utility function tests (calculateSponsorTickets, createDefaultAnalysis)

Documentation:

  • Updated AGENTS.md with extensive Vitest testing guidelines
  • Updated ESLint and knip configs to reflect the change

The migration follows Vitest best practices and maintains all existing test coverage while improving the testing infrastructure.

Confidence Score: 5/5

  • Safe to merge - comprehensive and well-executed testing framework migration
  • Clean migration with consistent patterns across all files, proper configuration, new test coverage added, and documentation updated to match
  • No files require special attention

Important Files Changed

Filename Overview
package.json Replaced Jest dependencies with Vitest and updated test scripts
vitest.config.ts New Vitest configuration with module aliases for mocks and test settings
vitest.setup.ts Simplified setup, removed TextEncoder/TextDecoder polyfills (Vitest provides these)
AGENTS.md Added comprehensive testing guidelines section for Vitest migration
tests/mocks/next-auth.ts Updated mock from jest.fn() to vi.fn() for Vitest compatibility
tests/mocks/jose.ts New mock for jose library supporting RSA keys for tests
tests/lib/environment/config.test.ts Migrated from jest.isolateModules to vi.resetModules + dynamic imports
tests/lib/proposal/validation.test.ts New comprehensive tests for proposal validation with factory helper
tests/lib/tickets/utils.test.ts Added new utility functions tests: calculateSponsorTickets, createDefaultAnalysis

Flowchart

flowchart TD
    A[Jest Testing Framework] --> B[Remove Dependencies]
    B --> C[jest, ts-jest, jest-environment-jsdom]
    
    A --> D[Add Vitest]
    D --> E[vitest and coverage-v8]
    
    D --> F[Create vitest.config.ts]
    F --> G[Module Aliases for Mocks]
    G --> H[jose, noble-ed25519, uuid, next-auth, next-sanity]
    
    D --> I[Update Test Scripts]
    I --> J[test: vitest run]
    I --> K[test:debug: vitest run verbose]
    I --> L[test:watch: vitest]
    
    D --> M[Migrate All Tests]
    M --> N[70+ Test Files]
    N --> O[jest.fn to vi.fn]
    N --> P[jest.mock to vi.mock]
    N --> Q[Environment docblocks]
    N --> R[Module isolation pattern]
    
    D --> S[Add New Tests]
    S --> T[Proposal Validation]
    S --> U[Video Handling]
    S --> V[Ticket Utils]
    
    D --> W[Update Documentation]
    W --> X[AGENTS.md Testing Guidelines]
    W --> Y[ESLint and Knip Config]
    
    D --> Z[Vitest Benefits]
    Z --> AA[Native ESM Support]
    Z --> AB[Faster Execution]
    Z --> AC[Built-in Globals]
Loading

Last reviewed commit: 1a735d8

Context used:

  • Context from dashboard - AGENTS.md (source)

…ariables

- Removed Jest imports from service tests and replaced with Vitest configuration.
- Added Vitest configuration file to set up aliases and test environment.
- Configured TypeScript to recognize Vitest globals.
- Created setup file for Vitest to load environment variables from .env files.
@vercel
Copy link

vercel bot commented Feb 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cloudnativedays Ready Ready Preview, Comment Feb 16, 2026 11:31am

Request Review

@Starefossen Starefossen marked this pull request as ready for review February 16, 2026 09:45
@Starefossen Starefossen requested a review from a team as a code owner February 16, 2026 09:45
@Starefossen Starefossen linked an issue Feb 16, 2026 that may be closed by this pull request
5 tasks
@qodo-code-review
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

The test modifies process.env.NODE_ENV via assignment and restores it by assigning undefined when it was previously unset. In Node.js, process.env values are strings and assigning undefined can result in the literal string "undefined" rather than removing the variable, which can make environment-dependent logic behave incorrectly and cause test order dependence. Consider deleting the key when it should be absent instead of assigning undefined.

  ;(process.env as Record<string, string | undefined>).NODE_ENV = opts.nodeEnv
  if (opts.botToken !== undefined) {
    process.env.SLACK_BOT_TOKEN = opts.botToken
  } else {
    delete process.env.SLACK_BOT_TOKEN
  }
}

function restoreEnv() {
  if (savedNodeEnv !== undefined) {
    ;(process.env as Record<string, string | undefined>).NODE_ENV = savedNodeEnv
  } else {
    ;(process.env as Record<string, string | undefined>).NODE_ENV = undefined
  }
Test Flakiness

Fake timers are enabled inside individual tests and only reset to real timers at the end of those tests. If an assertion fails or an exception is thrown before the reset, timers may leak into subsequent tests. Prefer an afterEach cleanup that always calls vi.useRealTimers() (and potentially vi.clearAllTimers()), or wrap timer usage with try/finally.

it('uploads document, creates agreement, and returns ID + signing URL', async () => {
  vi.useFakeTimers()
  mockUploadTransientDocument.mockResolvedValue({
    transientDocumentId: 'td-123',
  })
  mockCreateAgreement.mockResolvedValue({ id: 'agr-abc' })
  mockGetSigningUrls.mockResolvedValue({
    signingUrls: [
      { email: 'signer@test.com', esignUrl: 'https://sign.example.com' },
    ],
  })

  const promise = provider.sendForSigning({
    pdf: Buffer.from('fake-pdf'),
    filename: 'contract.pdf',
    signerEmail: 'signer@test.com',
    agreementName: 'Test Agreement',
    message: 'Please sign',
  })

  // Advance through the first retry delay
  await vi.advanceTimersByTimeAsync(10_000)

  const result = await promise

  expect(result.agreementId).toBe('agr-abc')
  expect(result.signingUrl).toBe('https://sign.example.com')
  expect(mockUploadTransientDocument).toHaveBeenCalledWith(
    testSession,
    expect.any(Buffer),
    'contract.pdf',
  )
  expect(mockCreateAgreement).toHaveBeenCalledWith(testSession, {
    name: 'Test Agreement',
    participantEmail: 'signer@test.com',
    message: 'Please sign',
    fileInfos: [{ transientDocumentId: 'td-123' }],
  })
  vi.useRealTimers()
})

it('throws when session is not connected', async () => {
  mockGetAdobeSignSession.mockResolvedValue(null)

  await expect(
    provider.sendForSigning({
      pdf: Buffer.from('pdf'),
      filename: 'test.pdf',
      signerEmail: 'test@test.com',
      agreementName: 'Test',
    }),
  ).rejects.toThrow(/not connected/i)
})

it('throws when transient document upload returns no ID', async () => {
  mockUploadTransientDocument.mockResolvedValue({})

  await expect(
    provider.sendForSigning({
      pdf: Buffer.from('pdf'),
      filename: 'test.pdf',
      signerEmail: 'test@test.com',
      agreementName: 'Test',
    }),
  ).rejects.toThrow(/no transient document/i)
})

it('throws when agreement creation returns no ID', async () => {
  mockUploadTransientDocument.mockResolvedValue({
    transientDocumentId: 'td-1',
  })
  mockCreateAgreement.mockResolvedValue({})

  await expect(
    provider.sendForSigning({
      pdf: Buffer.from('pdf'),
      filename: 'test.pdf',
      signerEmail: 'test@test.com',
      agreementName: 'Test',
    }),
  ).rejects.toThrow(/no agreement/i)
})

it('returns without signing URL when getSigningUrls fails', async () => {
  vi.useFakeTimers()
  mockUploadTransientDocument.mockResolvedValue({
    transientDocumentId: 'td-1',
  })
  mockCreateAgreement.mockResolvedValue({ id: 'agr-1' })
  mockGetSigningUrls.mockRejectedValue(new Error('timeout'))

  const promise = provider.sendForSigning({
    pdf: Buffer.from('pdf'),
    filename: 'test.pdf',
    signerEmail: 'test@test.com',
    agreementName: 'Test',
  })

  // Advance through the retry delays (2s, 4s, 6s)
  for (let i = 0; i < 3; i++) {
    await vi.advanceTimersByTimeAsync(10_000)
  }

  const result = await promise

  expect(result.agreementId).toBe('agr-1')
  expect(result.signingUrl).toBeUndefined()
  vi.useRealTimers()
})
Mock Coverage

The Jest-era mock for @/components/admin/sponsor-crm/utils (e.g., formatStatusName) was removed. If bulkUpdateSponsors indirectly imports and uses that utility, the tests may now pull in real implementation and additional dependencies, potentially causing brittle tests or environment-specific failures. Validate that all indirect dependencies are either safe to load in the test environment or explicitly mocked.

// Mock clientWrite BEFORE importing the module that uses it
vi.mock('@/lib/sanity/client', () => ({
  clientWrite: {
    fetch: vi.fn(),
    transaction: vi.fn(() => ({
      patch: vi.fn().mockReturnThis(),
      create: vi.fn().mockReturnThis(),
      delete: vi.fn().mockReturnThis(),
      // @ts-ignore - Mocking commit which returns a promise
      commit: vi.fn().mockResolvedValue({}),
    })),
  },
}))

import { bulkUpdateSponsors, bulkDeleteSponsors } from '@/lib/sponsor-crm/bulk'

@qodo-code-review
Copy link

qodo-code-review bot commented Feb 16, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix incorrect test assertion logic

Correct the assertion in the skips non-object reviews test, as the expected
average rating is incorrect due to a flawed calculation that includes invalid
review entries.

tests/lib/proposal/business-utils.test.ts [87-97]

 it('skips non-object reviews', () => {
   const proposal = makeProposal({
     reviews: [
       makeReview({ content: 5, relevance: 5, speaker: 5 }),
       'not-a-review' as unknown as Review,
     ],
   })
-  // only first review counts: 15 score, but totalPossible = 30 (2 items)
-  // (15/30)*5 = 2.5
-  expect(calculateAverageRating(proposal)).toBe(2.5)
+  // The invalid review is filtered out, so only the first review is considered.
+  // (15/15)*5 = 5.0
+  expect(calculateAverageRating(proposal)).toBe(5)
 })
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a logical flaw in the test's assertion, which is testing an incorrect implementation of the calculateAverageRating function. Correcting the test as suggested enforces the correct business logic, thus highlighting a bug in the application code.

Medium
Restore env and fetch after tests

Add an afterEach hook to restore environment variables and the global fetch
mock, ensuring proper test isolation and preventing state leakage.

tests/lib/slack/client.test.ts [35-43]

 describe('Slack client', () => {
   beforeEach(() => {
     vi.resetModules()
     vi.restoreAllMocks()
     mockFetch.mockReset()
     savedNodeEnv = process.env.NODE_ENV
     savedBotToken = process.env.SLACK_BOT_TOKEN
     savedFetch = global.fetch
     global.fetch = mockFetch
   })
+  afterEach(() => {
+    restoreEnv()
+    global.fetch = savedFetch
+  })
 })

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that test state (environment variables and global.fetch mock) is not being cleaned up, and proposes an afterEach hook to fix it, which is critical for test suite reliability.

Medium
Correctly restore environment variables
Suggestion Impact:Replaced setting process.env.NODE_ENV to undefined with deleting the NODE_ENV property in restoreEnv.

code diff:

@@ -23,7 +23,7 @@
   if (savedNodeEnv !== undefined) {
     ;(process.env as Record<string, string | undefined>).NODE_ENV = savedNodeEnv
   } else {
-    ;(process.env as Record<string, string | undefined>).NODE_ENV = undefined
+    delete (process.env as Record<string, string | undefined>).NODE_ENV
   }

In restoreEnv, use delete process.env.NODE_ENV instead of assigning undefined to
correctly unset the environment variable and prevent test state pollution.

tests/lib/slack/client.test.ts [22-33]

 function restoreEnv() {
   if (savedNodeEnv !== undefined) {
     ;(process.env as Record<string, string | undefined>).NODE_ENV = savedNodeEnv
   } else {
-    ;(process.env as Record<string, string | undefined>).NODE_ENV = undefined
+    delete (process.env as Record<string, string | undefined>).NODE_ENV
   }
   if (savedBotToken !== undefined) {
     process.env.SLACK_BOT_TOKEN = savedBotToken
   } else {
     delete process.env.SLACK_BOT_TOKEN
   }
 }

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that assigning undefined to process.env.NODE_ENV results in the string 'undefined', and proposes using delete to properly unset it, which prevents test state leakage.

Medium
General
Clear mocks after each test

Add an afterEach block with vi.clearAllMocks() to reset mocks after each test,
ensuring test isolation.

tests/components/sponsor/ContractSigningPage.test.tsx [61-65]

 // vi.mock calls are hoisted automatically by Vitest
 import { ContractSigningPage } from '@/components/sponsor/ContractSigningPage'
+
+afterEach(() => {
+  vi.clearAllMocks()
+})
 
 const MOCK_CONTRACT = {
   status: 'pending',
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: This suggestion improves test reliability by ensuring mocks are cleared after each test, which is a best practice for test isolation.

Low
  • Update

@Starefossen
Copy link
Member Author

Summary of Changes

Jest → Vitest Migration

Full migration from Jest to Vitest across 71 test files (1,531 tests passing, 5 skipped). Key infrastructure changes:

  • Replaced jest.config.ts / jest.setup.ts with vitest.config.ts / vitest.setup.ts
  • Updated tsconfig.json with Vitest globals type
  • Updated package.json dependencies (vitest, @vitest/coverage-v8, removed all Jest packages)
  • Updated eslint.config.js and knip.json for Vitest
  • Migrated all jest.fn()vi.fn(), jest.mock()vi.mock(), jest.spyOn()vi.spyOn(), etc.

Test Quality Audit

Systematic domain-by-domain audit covering sponsors, proposals, dashboard, tickets, openbadges, and remaining domains. Changes fall into three categories:

Deleted (tautological / no source imports):

  • Sponsors.test.tsx — reimplemented grouping/sorting inline without importing source
  • layout.test.tsx — reimplemented generateMetadata with content drift bug
  • SponsorLogo.test.tsx, cleanup-orphaned-blobs.test.ts, proposal-attachment-route.test.ts, auth-impersonation.test.ts, impersonation-security.test.ts, signing-pdf.test.ts — tautological tests that didn't exercise real code
  • speaker.test.ts.skip — disabled test file
  • Tautological test sections removed from weekly-update.test.ts (Ticket Splitting), chapter9-test-vector.test.ts (5 describe blocks asserting on in-file constants), golden-data.test.ts (3 tautological tests)

New test files (134 tests):

  • business-utils.test.tscalculateAverageRating, getProposalSpeakerNames
  • validation.test.tsvalidateProposalSubmission with 40+ edge cases
  • utils.test.tsgetStatusColor, getStatusLabel, sortProposals, filterProposals
  • video-and-types.test.tsgetProposalVideo, ProposalFormat validation

Expanded existing tests:

  • sponsor/validation.test.ts: 10 → 32 tests
  • sponsor/bulk.test.ts: 5 → 11 tests
  • sponsor/utils.test.ts: 6 → 21 tests
  • tickets/utils.test.ts: +8 tests (calculateSponsorTickets, createDefaultAnalysis)
  • dashboard/grid-utils.test.ts: +9 tests (reflowWidgetsForColumns)
  • schemas.test.ts: +25 tests (review/proposal schema validation)

PR Review Fixes

Addressed code review suggestions:

  • Fixed process.env.NODE_ENV restoration in slack client (use delete instead of = undefined)
  • Added afterEach(() => vi.useRealTimers()) to contract-signing provider tests to prevent fake timer leaks
  • Added afterEach(() => vi.clearAllMocks()) to ContractSigningPage tests

Testing Guidelines

Added comprehensive Testing Guidelines section to AGENTS.md covering test philosophy, what to test/not test, mocking strategy, environment directives, and performance guidance.

Stats

  • 97 files changed, +3,563 / -2,760 lines
  • 71 test files, 1,531 tests passing, 5 skipped
  • Duration: ~5s

@Starefossen Starefossen merged commit e1c9318 into main Feb 16, 2026
9 checks passed
@Starefossen Starefossen deleted the no-more-jest branch February 16, 2026 11:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace Jest with Vitest

1 participant