-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
feat(query-core): add timeoutManager to allow changing setTimeout/setInterval #9612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
8fc4d3d
2fb1f1f
acb595c
96813b6
530504e
223371b
d3d7d1a
a953c70
0fac7b2
47962fd
62b1dd0
8d5e050
fea6cce
1606b58
fc9092b
5d6fe4d
ad1fb2b
932c3a2
a6d38f8
81d35ac
b6fffb4
948c646
08a2c5f
841ac54
fb22c67
09a787e
7fb57b1
4b1e8af
b6ca80d
73014f5
3f452ea
cca861f
d58e28f
7922966
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' | ||
import { | ||
TimeoutManager, | ||
defaultTimeoutProvider, | ||
managedClearInterval, | ||
managedClearTimeout, | ||
managedSetInterval, | ||
managedSetTimeout, | ||
systemSetTimeoutZero, | ||
timeoutManager, | ||
} from '../timeoutManager' | ||
|
||
describe('timeoutManager', () => { | ||
function createMockProvider(name: string = 'custom') { | ||
return { | ||
name, | ||
setTimeout: vi.fn(() => 123), | ||
clearTimeout: vi.fn(), | ||
setInterval: vi.fn(() => 456), | ||
clearInterval: vi.fn(), | ||
} | ||
} | ||
|
||
beforeEach(() => { | ||
vi.spyOn(console, 'warn') | ||
}) | ||
|
||
afterEach(() => { | ||
vi.restoreAllMocks() | ||
}) | ||
|
||
describe('TimeoutManager', () => { | ||
let manager: TimeoutManager | ||
|
||
beforeEach(() => { | ||
manager = new TimeoutManager() | ||
}) | ||
|
||
it('by default proxies calls to globalThis setTimeout/clearTimeout', () => { | ||
const setTimeoutSpy = vi.spyOn(globalThis, 'setTimeout') | ||
const clearTimeoutSpy = vi.spyOn(globalThis, 'clearTimeout') | ||
const setIntervalSpy = vi.spyOn(globalThis, 'setInterval') | ||
const clearIntervalSpy = vi.spyOn(globalThis, 'clearInterval') | ||
|
||
const callback = vi.fn() | ||
const timeoutId = manager.setTimeout(callback, 100) | ||
expect(setTimeoutSpy).toHaveBeenCalledWith(callback, 100) | ||
clearTimeout(timeoutId) | ||
|
||
manager.clearTimeout(200) | ||
expect(clearTimeoutSpy).toHaveBeenCalledWith(200) | ||
|
||
const intervalId = manager.setInterval(callback, 300) | ||
expect(setIntervalSpy).toHaveBeenCalledWith(callback, 300) | ||
clearInterval(intervalId) | ||
|
||
manager.clearInterval(400) | ||
expect(clearIntervalSpy).toHaveBeenCalledWith(400) | ||
}) | ||
|
||
describe('setTimeoutProvider', () => { | ||
it('proxies calls to the configured timeout provider', () => { | ||
const customProvider = createMockProvider() | ||
manager.setTimeoutProvider(customProvider) | ||
|
||
const callback = vi.fn() | ||
|
||
manager.setTimeout(callback, 100) | ||
expect(customProvider.setTimeout).toHaveBeenCalledWith(callback, 100) | ||
|
||
manager.clearTimeout(999) | ||
expect(customProvider.clearTimeout).toHaveBeenCalledWith(999) | ||
|
||
manager.setInterval(callback, 200) | ||
expect(customProvider.setInterval).toHaveBeenCalledWith(callback, 200) | ||
|
||
manager.clearInterval(888) | ||
expect(customProvider.clearInterval).toHaveBeenCalledWith(888) | ||
}) | ||
|
||
it('warns when switching providers after making call', () => { | ||
// 1. switching before making any calls does not warn | ||
const customProvider = createMockProvider() | ||
manager.setTimeoutProvider(customProvider) | ||
expect(console.warn).not.toHaveBeenCalled() | ||
|
||
// Make a call. The next switch should warn | ||
manager.setTimeout(vi.fn(), 100) | ||
|
||
// 2. switching after making a call should warn | ||
const customProvider2 = createMockProvider('custom2') | ||
manager.setTimeoutProvider(customProvider2) | ||
expect(console.warn).toHaveBeenCalledWith( | ||
'[timeoutManager]: Switching to custom2 provider after calls to custom provider might result in unexpected behavior.', | ||
) | ||
|
||
// 3. Switching again with no intermediate calls should not warn | ||
vi.mocked(console.warn).mockClear() | ||
const customProvider3 = createMockProvider('custom3') | ||
manager.setTimeoutProvider(customProvider3) | ||
expect(console.warn).not.toHaveBeenCalled() | ||
}) | ||
}) | ||
|
||
it('throws if provider returns non-convertible value from setTimeout/setInterval', () => { | ||
const invalidValue = { invalid: true } as any | ||
const customProvider = createMockProvider('badProvider') | ||
customProvider.setTimeout = vi.fn(() => invalidValue) | ||
customProvider.setInterval = vi.fn(() => invalidValue) | ||
manager.setTimeoutProvider(customProvider) | ||
|
||
const callback = vi.fn() | ||
|
||
expect(() => manager.setTimeout(callback, 100)).toThrow( | ||
'TimeoutManager: could not convert badProvider provider timeout ID to valid number', | ||
) | ||
|
||
expect(() => manager.setInterval(callback, 100)).toThrow( | ||
'TimeoutManager: could not convert badProvider provider timeout ID to valid number', | ||
) | ||
}) | ||
}) | ||
|
||
describe('globalThis timeoutManager instance', () => { | ||
it('should be an instance of TimeoutManager', () => { | ||
expect(timeoutManager).toBeInstanceOf(TimeoutManager) | ||
}) | ||
}) | ||
|
||
describe('exported functions', () => { | ||
let provider: ReturnType<typeof createMockProvider> | ||
let callNumber = 0 | ||
beforeEach(() => { | ||
callNumber = 0 | ||
provider = createMockProvider() | ||
timeoutManager.setTimeoutProvider(provider) | ||
}) | ||
afterEach(() => { | ||
timeoutManager.setTimeoutProvider(defaultTimeoutProvider) | ||
}) | ||
|
||
const callbackArgs = () => [vi.fn(), 100 * ++callNumber] as const | ||
|
||
describe('managedSetTimeout', () => { | ||
it('should call timeoutManager.setTimeout', () => { | ||
const spy = vi.spyOn(timeoutManager, 'setTimeout') | ||
const args = callbackArgs() | ||
|
||
const result = managedSetTimeout(...args) | ||
|
||
expect(spy).toHaveBeenCalledWith(...args) | ||
expect(result).toBe(123) | ||
}) | ||
}) | ||
|
||
describe('managedClearTimeout', () => { | ||
it('should call timeoutManager.clearTimeout', () => { | ||
const spy = vi.spyOn(timeoutManager, 'clearTimeout') | ||
const timeoutId = 123 | ||
|
||
managedClearTimeout(timeoutId) | ||
|
||
expect(spy).toHaveBeenCalledWith(timeoutId) | ||
|
||
spy.mockRestore() | ||
}) | ||
}) | ||
|
||
describe('managedSetInterval', () => { | ||
it('should call timeoutManager.setInterval', () => { | ||
const spy = vi.spyOn(timeoutManager, 'setInterval') | ||
const args = callbackArgs() | ||
|
||
const result = managedSetInterval(...args) | ||
|
||
expect(spy).toHaveBeenCalledWith(...args) | ||
expect(result).toBe(456) | ||
}) | ||
}) | ||
|
||
describe('managedClearInterval', () => { | ||
it('should call timeoutManager.clearInterval', () => { | ||
const spy = vi.spyOn(timeoutManager, 'clearInterval') | ||
const intervalId = 456 | ||
|
||
managedClearInterval(intervalId) | ||
|
||
expect(spy).toHaveBeenCalledWith(intervalId) | ||
}) | ||
}) | ||
|
||
describe('systemSetTimeoutZero', () => { | ||
it('should use globalThis setTimeout with 0 delay', () => { | ||
const spy = vi.spyOn(globalThis, 'setTimeout') | ||
|
||
const callback = vi.fn() | ||
systemSetTimeoutZero(callback) | ||
|
||
expect(spy).toHaveBeenCalledWith(callback, 0) | ||
clearTimeout(spy.mock.results[0]?.value) | ||
}) | ||
}) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
// TYPES | ||
|
||
import { systemSetTimeoutZero } from './timeoutManager' | ||
|
||
type NotifyCallback = () => void | ||
|
||
type NotifyFunction = (callback: () => void) => void | ||
|
@@ -10,7 +12,8 @@ type BatchCallsCallback<T extends Array<unknown>> = (...args: T) => void | |
|
||
type ScheduleFunction = (callback: () => void) => void | ||
|
||
export const defaultScheduler: ScheduleFunction = (cb) => setTimeout(cb, 0) | ||
export const defaultScheduler: ScheduleFunction = (cb) => | ||
|
||
systemSetTimeoutZero(cb) | ||
justjake marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
export function createNotifyManager() { | ||
let queue: Array<NotifyCallback> = [] | ||
|
Uh oh!
There was an error while loading. Please reload this page.