cherry-studio/tests/__mocks__
fullex 81bb8e7981 feat: implement new pagination types and enhance API documentation
- Introduced `OffsetPaginationParams`, `CursorPaginationParams`, and their corresponding response types to standardize pagination handling across the API.
- Updated existing API types and hooks to support both offset and cursor-based pagination, improving data fetching capabilities.
- Enhanced documentation with detailed usage examples for pagination, including request parameters and response structures, to aid developers in implementing pagination effectively.
- Refactored related components to utilize the new pagination types, ensuring consistency and clarity in data management.
2026-01-04 21:12:41 +08:00
..
main feat: add shared cache functionality to MockMainCacheService 2026-01-04 10:33:01 +08:00
renderer feat: implement new pagination types and enhance API documentation 2026-01-04 21:12:41 +08:00
MainLoggerService.ts feat: use oxlint to speed up lint (#10168) 2025-09-15 19:42:13 +08:00
README.md feat: add test mocks path alias for improved import management 2026-01-04 11:06:03 +08:00
RendererLoggerService.ts feat: use oxlint to speed up lint (#10168) 2025-09-15 19:42:13 +08:00

Test Mocks

Unified test mocks for the project, organized by process type and globally configured in test setup files.

Overview

Available Mocks

Process Mock Description
Renderer CacheService Three-tier cache (memory/shared/persist)
Renderer DataApiService HTTP client for Data API
Renderer PreferenceService User preferences
Renderer useDataApi Data API hooks (useQuery, useMutation, etc.)
Renderer usePreference Preference hooks
Renderer useCache Cache hooks
Main CacheService Internal + shared cache
Main DataApiService API coordinator
Main PreferenceService Preference service

File Structure

tests/__mocks__/
├── renderer/
│   ├── CacheService.ts
│   ├── DataApiService.ts
│   ├── PreferenceService.ts
│   ├── useDataApi.ts
│   ├── usePreference.ts
│   └── useCache.ts
├── main/
│   ├── CacheService.ts
│   ├── DataApiService.ts
│   └── PreferenceService.ts
├── RendererLoggerService.ts
└── MainLoggerService.ts

Test Setup

Mocks are globally configured in setup files:

  • Renderer: tests/renderer.setup.ts
  • Main: tests/main.setup.ts

Import Path Alias

Use @test-mocks/* to import mock utilities:

import { MockCacheUtils } from '@test-mocks/renderer/CacheService'
import { MockMainCacheServiceUtils } from '@test-mocks/main/CacheService'

Renderer Mocks

CacheService

Three-tier cache system with type-safe and casual (dynamic key) methods.

Methods

Category Method Signature
Memory (typed) get <K>(key: K) => UseCacheSchema[K]
Memory (typed) set <K>(key: K, value, ttl?) => void
Memory (typed) has <K>(key: K) => boolean
Memory (typed) delete <K>(key: K) => boolean
Memory (typed) hasTTL <K>(key: K) => boolean
Memory (casual) getCasual <T>(key: string) => T | undefined
Memory (casual) setCasual <T>(key, value, ttl?) => void
Memory (casual) hasCasual (key: string) => boolean
Memory (casual) deleteCasual (key: string) => boolean
Memory (casual) hasTTLCasual (key: string) => boolean
Shared (typed) getShared <K>(key: K) => SharedCacheSchema[K]
Shared (typed) setShared <K>(key: K, value, ttl?) => void
Shared (typed) hasShared <K>(key: K) => boolean
Shared (typed) deleteShared <K>(key: K) => boolean
Shared (typed) hasSharedTTL <K>(key: K) => boolean
Shared (casual) getSharedCasual <T>(key: string) => T | undefined
Shared (casual) setSharedCasual <T>(key, value, ttl?) => void
Shared (casual) hasSharedCasual (key: string) => boolean
Shared (casual) deleteSharedCasual (key: string) => boolean
Shared (casual) hasSharedTTLCasual (key: string) => boolean
Persist getPersist <K>(key: K) => RendererPersistCacheSchema[K]
Persist setPersist <K>(key: K, value) => void
Persist hasPersist (key) => boolean
Hook mgmt registerHook (key: string) => void
Hook mgmt unregisterHook (key: string) => void
Ready state isSharedCacheReady () => boolean
Ready state onSharedCacheReady (callback) => () => void
Lifecycle subscribe (key, callback) => () => void
Lifecycle cleanup () => void

Usage

import { cacheService } from '@data/CacheService'
import { MockCacheUtils } from '@test-mocks/renderer/CacheService'

describe('Cache', () => {
  beforeEach(() => MockCacheUtils.resetMocks())

  it('basic usage', () => {
    cacheService.setCasual('key', { data: 'value' }, 5000)
    expect(cacheService.getCasual('key')).toEqual({ data: 'value' })
  })

  it('with test utilities', () => {
    MockCacheUtils.setInitialState({
      memory: [['key', 'value']],
      shared: [['shared.key', 'shared']],
      persist: [['persist.key', 'persist']]
    })
  })
})

DataApiService

HTTP client with subscriptions and retry configuration.

Methods

Method Signature
get (path, options?) => Promise<any>
post (path, options) => Promise<any>
put (path, options) => Promise<any>
patch (path, options) => Promise<any>
delete (path, options?) => Promise<any>
subscribe (options, callback) => () => void
configureRetry (options) => void
getRetryConfig () => RetryOptions
getRequestStats () => { pendingRequests, activeSubscriptions }

Usage

import { dataApiService } from '@data/DataApiService'
import { MockDataApiUtils } from '@test-mocks/renderer/DataApiService'

describe('API', () => {
  beforeEach(() => MockDataApiUtils.resetMocks())

  it('basic request', async () => {
    const response = await dataApiService.get('/topics')
    expect(response.topics).toBeDefined()
  })

  it('custom response', async () => {
    MockDataApiUtils.setCustomResponse('/topics', 'GET', { custom: true })
    const response = await dataApiService.get('/topics')
    expect(response.custom).toBe(true)
  })

  it('error simulation', async () => {
    MockDataApiUtils.setErrorResponse('/topics', 'GET', new Error('Failed'))
    await expect(dataApiService.get('/topics')).rejects.toThrow('Failed')
  })
})

useDataApi Hooks

React hooks for data operations.

Hooks

Hook Signature Returns
useQuery (path, options?) { data, loading, error, refetch, mutate }
useMutation (method, path, options?) { mutate, loading, error }
usePaginatedQuery (path, options?) { items, total, page, loading, error, hasMore, hasPrev, prevPage, nextPage, refresh, reset }
useInvalidateCache () (keys?) => Promise<any>

Usage

import { useQuery, useMutation } from '@data/hooks/useDataApi'
import { MockUseDataApiUtils } from '@test-mocks/renderer/useDataApi'

describe('Hooks', () => {
  beforeEach(() => MockUseDataApiUtils.resetMocks())

  it('useQuery', () => {
    const { data, loading } = useQuery('/topics')
    expect(loading).toBe(false)
    expect(data).toBeDefined()
  })

  it('useMutation', async () => {
    const { mutate } = useMutation('POST', '/topics')
    const result = await mutate({ body: { name: 'New' } })
    expect(result.created).toBe(true)
  })

  it('custom data', () => {
    MockUseDataApiUtils.mockQueryData('/topics', { custom: true })
    const { data } = useQuery('/topics')
    expect(data.custom).toBe(true)
  })
})

useCache Hooks

React hooks for cache operations.

Hook Signature Returns
useCache (key, initValue?) [value, setValue]
useSharedCache (key, initValue?) [value, setValue]
usePersistCache (key) [value, setValue]
import { useCache } from '@data/hooks/useCache'

const [value, setValue] = useCache('key', 'default')
setValue('new value')

usePreference Hooks

React hooks for preferences.

Hook Signature Returns
usePreference (key) [value, setValue]
useMultiplePreferences (keyMap) [values, setValues]
import { usePreference } from '@data/hooks/usePreference'

const [theme, setTheme] = usePreference('ui.theme')
await setTheme('dark')

Main Process Mocks

Main CacheService

Internal cache and cross-window shared cache.

Methods

Category Method Signature
Lifecycle initialize () => Promise<void>
Lifecycle cleanup () => void
Internal get <T>(key: string) => T | undefined
Internal set <T>(key, value, ttl?) => void
Internal has (key: string) => boolean
Internal delete (key: string) => boolean
Shared getShared <K>(key: K) => SharedCacheSchema[K] | undefined
Shared setShared <K>(key: K, value, ttl?) => void
Shared hasShared <K>(key: K) => boolean
Shared deleteShared <K>(key: K) => boolean
import { MockMainCacheServiceUtils } from '@test-mocks/main/CacheService'

beforeEach(() => MockMainCacheServiceUtils.resetMocks())

MockMainCacheServiceUtils.setCacheValue('key', 'value')
MockMainCacheServiceUtils.setSharedCacheValue('shared.key', 'shared')

Main DataApiService

API coordinator managing ApiServer and IpcAdapter.

Method Signature
initialize () => Promise<void>
shutdown () => Promise<void>
getSystemStatus () => object
getApiServer () => ApiServer
import { MockMainDataApiServiceUtils } from '@test-mocks/main/DataApiService'

beforeEach(() => MockMainDataApiServiceUtils.resetMocks())

MockMainDataApiServiceUtils.simulateInitializationError(new Error('Failed'))

Utility Functions

Each mock exports a MockXxxUtils object with testing utilities:

Utility Description
resetMocks() Reset all mock state and call counts
setXxxValue() Set specific values for testing
getXxxValue() Get current mock values
simulateXxx() Simulate specific scenarios (errors, expiration, etc.)
getMockCallCounts() Get call counts for debugging

Best Practices

  1. Use global mocks - Don't re-mock in individual tests unless necessary
  2. Reset in beforeEach - Call MockXxxUtils.resetMocks() to ensure test isolation
  3. Use utility functions - Prefer MockXxxUtils over direct mock manipulation
  4. Type safety - Mocks match actual service interfaces

Troubleshooting

Issue Solution
Mock not applied Check test runs in correct process (renderer/main in vitest.config.ts)
Type errors Ensure mock matches actual interface, use type assertions if needed
State pollution Call resetMocks() in beforeEach
Import issues Use path aliases (@data/CacheService) not relative paths