Skip to content

Commit f12b1bd

Browse files
committed
test(module-state): cover visible-interval pull mode
Adds a test proving 'visible-interval' pull mode skips interval ticks while the tab is hidden, then triggers exactly one catch-up pull on returning to visible (not one per missed tick). Also picks up biome's quote-style fix on an existing test title in this file.
1 parent bf91b75 commit f12b1bd

1 file changed

Lines changed: 65 additions & 1 deletion

File tree

packages/modules/state/src/__tests__/PouchDbSyncStorage.test.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('PouchDbSyncStorage', () => {
4141
storage[Symbol.dispose]();
4242
});
4343

44-
it("surfaces a remote-only write via a scheduled pull, without needing a continuous pull connection", async () => {
44+
it('surfaces a remote-only write via a scheduled pull, without needing a continuous pull connection', async () => {
4545
const storage = new PouchDbSyncStorage({
4646
localDb: { name_or_instance: localDb },
4747
remoteDb: { name_or_instance: remoteDb },
@@ -93,6 +93,70 @@ describe('PouchDbSyncStorage', () => {
9393
});
9494
});
9595

96+
describe('pull.mode "visible-interval"', () => {
97+
const setVisibility = (state: DocumentVisibilityState) => {
98+
Object.defineProperty(document, 'visibilityState', { value: state, configurable: true });
99+
document.dispatchEvent(new Event('visibilitychange'));
100+
};
101+
102+
afterEach(() => {
103+
// Other describe blocks in this file assume a visible tab by default.
104+
setVisibility('visible');
105+
});
106+
107+
it('skips interval ticks while hidden, then pulls exactly once on returning to visible', async () => {
108+
// Captures each replication's registered handlers so the test can drive 'complete'
109+
// itself - deterministic, instead of racing real PouchDB I/O against the interval timer.
110+
const fakeReplications: Array<{ complete: () => void }> = [];
111+
const replicateFrom = vi.spyOn(localDb.replicate, 'from').mockImplementation(() => {
112+
const handlers: Record<string, Array<() => void>> = {};
113+
const replication = {
114+
on: vi.fn((event: string, handler: () => void) => {
115+
if (!handlers[event]) handlers[event] = [];
116+
handlers[event].push(handler);
117+
}),
118+
removeListener: vi.fn(),
119+
// biome-ignore lint/suspicious/noThenProperty: mocking PouchDB's Replication, which is genuinely thenable.
120+
then: vi.fn(),
121+
cancel: vi.fn(),
122+
};
123+
fakeReplications.push({
124+
complete: () => {
125+
handlers.complete?.forEach((handler) => {
126+
handler();
127+
});
128+
},
129+
});
130+
return replication as unknown as ReturnType<typeof localDb.replicate.from>;
131+
});
132+
133+
setVisibility('hidden');
134+
const storage = new PouchDbSyncStorage({
135+
localDb: { name_or_instance: localDb },
136+
remoteDb: { name_or_instance: remoteDb },
137+
syncOptions: {},
138+
pull: { mode: 'visible-interval', intervalMs: 20, refreshOnFocus: true },
139+
});
140+
141+
await storage.initialize();
142+
expect(replicateFrom).toHaveBeenCalledTimes(1); // the always-runs initial pull
143+
144+
// Several interval ticks pass while hidden - the schedule should skip every one of them.
145+
await new Promise((resolve) => setTimeout(resolve, 100));
146+
expect(replicateFrom).toHaveBeenCalledTimes(1);
147+
148+
// Release the initial pull, so the upcoming focus trigger isn't skipped as already in flight.
149+
fakeReplications[0].complete();
150+
151+
setVisibility('visible');
152+
// Returning to visible triggers exactly one catch-up pull, not one per missed tick.
153+
expect(replicateFrom).toHaveBeenCalledTimes(2);
154+
155+
replicateFrom.mockRestore();
156+
storage[Symbol.dispose]();
157+
});
158+
});
159+
96160
describe('pull watchdog', () => {
97161
it('cancels a hung pull replication and releases it for the next scheduled pull', async () => {
98162
vi.useFakeTimers();

0 commit comments

Comments
 (0)