Skip to content

@msflib/testing

Shared testing utilities for msflib modules - focus on eliminating repeated mock definitions.

Install

pnpm add -D @msflib/testing

Peer Dependencies

  • vitest >= 4

Main Feature: Hoisted Mocks

The core feature is createHoisedMocks() - a factory function that generates reusable mock objects for tests.

Problem It Solves

Before, every test file had to define the same mocks repeatedly:

const mocks = vi.hoisted(() => {
  return {
    storage: {
      setItem: vi.fn(),
      getItem: vi.fn(),
      removeItem: vi.fn(),
    },
    coreConfig: {
      baseURL: 'https://api.example.com',
      accessTokenKey: 'access_token',
      endpoints: {
        profile: { profile: '/profile' },
      },
    },
    api: {
      get: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
    },
  };
});

Solution: Shared Factory

Now use the shared factory in your test files:

import { createHoisedMocks } from '@msflib/testing';

const mocks = vi.hoisted(() =>
  createHoisedMocks({
    apiMethods: ['get', 'create', 'update', 'delete'],
    coreConfig: {
      endpoints: {
        profile: {
          profile: '/profile',
        },
      },
    },
  }),
);

vi.mock('@msflib/core', () => ({
  storage: mocks.storage,
  getApplicationConfig: () => mocks.coreConfig,
}));

vi.mock('../api/profile.api', () => ({
  createProfileApi: () => mocks.api,
}));

API

createHoisedMocks(options?)

Creates a complete mock object for use in vi.hoisted().

Parameters:

{
  apiMethods?: string[];      // API method names to mock (e.g., ['get', 'create'])
  coreConfig?: {
    baseURL?: string;         // Defaults to 'https://api.example.com'
    accessTokenKey?: string;  // Defaults to 'access_token'
    endpoints?: Record<string, any>;  // Custom endpoint overrides
  }
}

Returns:

{
  storage: {
    (setItem, getItem, removeItem);
  } // Mock storage (vi.fn)
  coreConfig: {
    (baseURL, accessTokenKey, endpoints);
  } // Mock config
  api: Record<string, Mock>; // API methods specified in apiMethods
}

Example:

const mocks = vi.hoisted(() =>
  createHoisedMocks({
    apiMethods: ['get', 'create', 'update', 'delete', 'uploadAvatar'],
    coreConfig: {
      baseURL: 'https://custom.api.com',
      endpoints: {
        profile: { profile: '/profile', avatar: '/profile/avatar' },
      },
    },
  }),
);

mocks.api.get; // vi.fn
mocks.api.create; // vi.fn
mocks.api.uploadAvatar; // vi.fn
mocks.storage.getItem; // vi.fn
mocks.coreConfig.baseURL; // 'https://custom.api.com'

createMockStorage()

Creates just the storage mock independently.

import { createMockStorage } from '@msflib/testing';

const storage = createMockStorage();
storage.getItem('access_token');
storage.setItem('access_token', 'token123');

createMockCoreConfig(config?)

Creates just the core config mock independently.

import { createMockCoreConfig } from '@msflib/testing';

const config = createMockCoreConfig({
  endpoints: { auth: { login: '/login' } },
});

Vitest Configurations

Pre-configured Vitest setups are also available:

// For React component testing (jsdom)
import { createJsdomConfig } from '@msflib/testing';
export default createJsdomConfig();

// For Node/utility testing
import { createNodeConfig } from '@msflib/testing';
export default createNodeConfig();

Benefits

  • ✅ Single source of truth for mock definitions
  • ✅ Less boilerplate in test files
  • ✅ Consistent mocking patterns across modules
  • ✅ Type-safe with TypeScript
  • ✅ Easy to extend for new modules