File size: 2,542 Bytes
79ed62f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
 * syncTriggers — reconnect/online wiring (H1).
 *
 * Verifies the previously-dead refetch path is wired: on WS reconnect and on the
 * `online` event the active trip's store is re-hydrated (after the queue flush).
 */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

const flush = vi.fn(() => Promise.resolve());
const syncAll = vi.fn(() => Promise.resolve());
const hydrate = vi.fn(() => Promise.resolve());

let refetchCb: ((tripId: string) => void) | null = null;
let preReconnect: (() => Promise<void>) | null = null;

vi.mock('../../../src/sync/mutationQueue', () => ({
  mutationQueue: { flush: () => flush() },
}));
vi.mock('../../../src/sync/tripSyncManager', () => ({
  tripSyncManager: { syncAll: () => syncAll() },
}));
vi.mock('../../../src/api/websocket', () => ({
  setPreReconnectHook: (fn: (() => Promise<void>) | null) => { preReconnect = fn; },
  setRefetchCallback: (fn: ((tripId: string) => void) | null) => { refetchCb = fn; },
  getActiveTrips: () => ['7'],
}));
vi.mock('../../../src/store/tripStore', () => ({
  useTripStore: { getState: () => ({ hydrateActiveTrip: hydrate }) },
}));

import { registerSyncTriggers, unregisterSyncTriggers } from '../../../src/sync/syncTriggers';

const flushMicrotasks = async () => {
  for (let i = 0; i < 5; i++) await Promise.resolve();
};

beforeEach(() => {
  flush.mockClear(); syncAll.mockClear(); hydrate.mockClear();
  refetchCb = null; preReconnect = null;
  Object.defineProperty(navigator, 'onLine', { value: true, writable: true, configurable: true });
});

afterEach(() => {
  unregisterSyncTriggers();
});

describe('syncTriggers', () => {
  it('registers a refetch callback that hydrates the active trip', () => {
    registerSyncTriggers();
    expect(refetchCb).toBeTypeOf('function');
    refetchCb!('7');
    expect(hydrate).toHaveBeenCalledWith('7');
  });

  it('also registers the pre-reconnect flush hook', () => {
    registerSyncTriggers();
    expect(preReconnect).toBeTypeOf('function');
  });

  it('clears both reconnect hooks on unregister', () => {
    registerSyncTriggers();
    unregisterSyncTriggers();
    expect(refetchCb).toBeNull();
    expect(preReconnect).toBeNull();
  });

  it('online event flushes, then re-seeds Dexie and re-hydrates active trips', async () => {
    registerSyncTriggers();
    window.dispatchEvent(new Event('online'));
    await flushMicrotasks();

    expect(flush).toHaveBeenCalled();
    expect(syncAll).toHaveBeenCalled();
    expect(hydrate).toHaveBeenCalledWith('7');
  });
});