UNPKG

wxt-zustand

Version:

High-performance Zustand state management for WXT web extensions with seamless cross-tab synchronization and sub-10ms React re-renders

97 lines 3.89 kB
import { beforeEach, describe, expect, mock, test } from 'bun:test'; // Mock @webext-core/proxy-service before importing modules that depend on it const serviceObj = {}; const mockDefineProxyService = mock((_name, _factory) => { // We ignore the factory here because connect helpers only use getService() // which returns our mocked service object. return [ mock(() => { }), // registerService (unused in frontend connect) mock(() => serviceObj), // getService returns our shared service object ]; }); mock.module('@webext-core/proxy-service', () => ({ defineProxyService: mockDefineProxyService, })); // Now import the module under test const connectModule = await import('./connect'); const { getBackendServiceWithRetry, getBackendService } = connectModule; describe('frontend connect helpers', () => { beforeEach(() => { mockDefineProxyService.mockClear(); // Reset service object between tests for (const k of Object.keys(serviceObj)) delete serviceObj[k]; }); test('getBackendService returns raw service without probing', () => { serviceObj.fetchInitialState = mock(async () => ({ count: 1 })); const svc = getBackendService('testStore'); expect(typeof svc.fetchInitialState).toBe('function'); // Ensure no probe was executed expect(serviceObj.fetchInitialState.mock.calls.length).toBe(0); }); test('getBackendServiceWithRetry resolves when fetchInitialState succeeds', async () => { serviceObj.fetchInitialState = mock(async () => ({ count: 1 })); const svc = await getBackendServiceWithRetry('testStore', { retries: 0, }); expect(svc).toBe(serviceObj); expect(serviceObj.fetchInitialState).toHaveBeenCalledTimes(1); }); test('getBackendServiceWithRetry retries on failure then succeeds', async () => { const err = new Error('not ready'); serviceObj.fetchInitialState = mock(async () => { // Fail first two attempts, then succeed if (serviceObj.fetchInitialState.mock.calls.length < 2) throw err; return { count: 2 }; }); const svc = await getBackendServiceWithRetry('testStore', { retries: 5, baseMs: 1, }); expect(svc).toBe(serviceObj); // At least one retry should have happened expect(serviceObj.fetchInitialState.mock.calls.length).toBeGreaterThanOrEqual(2); }); test('getBackendServiceWithRetry throws after exceeding retries', async () => { const err = new Error('still not ready'); serviceObj.fetchInitialState = mock(async () => { throw err; }); let thrown; try { await getBackendServiceWithRetry('testStore', { retries: 1, baseMs: 1, }); } catch (e) { thrown = e; } expect(thrown).toBe(err); expect(serviceObj.fetchInitialState.mock.calls.length).toBeGreaterThanOrEqual(2); }); test('getBackendServiceWithRetry reloads on context invalidation', async () => { const err = new Error('Extension context invalidated.'); serviceObj.fetchInitialState = mock(async () => { throw err; }); const originalWin = globalThis.window; const reload = mock(() => { }); globalThis.window = { location: { reload } }; let thrown; try { await getBackendServiceWithRetry('testStore', { retries: 0, baseMs: 1, }); } catch (e) { thrown = e; } expect(thrown).toBe(err); expect(reload).toHaveBeenCalledTimes(1); globalThis.window = originalWin; }); }); //# sourceMappingURL=connect.test.js.map