diff --git a/index.d.ts b/index.d.ts index c6925e7..83196af 100644 --- a/index.d.ts +++ b/index.d.ts @@ -15,7 +15,7 @@ export interface Options { signal?: AbortSignal } -declare const delay: { +type Delay = { /** * Create a promise which resolves after the specified `milliseconds`. * @@ -46,6 +46,10 @@ declare const delay: { /** Value to reject in the returned promise. */ value?: any }): ClearablePromise; +} + +declare const delay: Delay & { + createWithTimers(timers: {clearTimeout: typeof clearTimeout, setTimeout: typeof setTimeout}): Delay }; export default delay; diff --git a/index.js b/index.js index 5f865d7..d00173c 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ const createAbortError = () => { return error; }; -const createDelay = willResolve => (ms, {value, signal} = {}) => { +const createDelay = ({clearTimeout: clear = clearTimeout, setTimeout: set = setTimeout, willResolve}) => (ms, {value, signal} = {}) => { if (signal && signal.aborted) { return Promise.reject(createAbortError()); } @@ -16,7 +16,7 @@ const createDelay = willResolve => (ms, {value, signal} = {}) => { let rejectFn; const signalListener = () => { - clearTimeout(timeoutId); + clear(timeoutId); rejectFn(createAbortError()); }; @@ -36,7 +36,7 @@ const createDelay = willResolve => (ms, {value, signal} = {}) => { } }; rejectFn = reject; - timeoutId = setTimeout(settle, ms); + timeoutId = set(settle, ms); }); if (signal) { @@ -44,7 +44,7 @@ const createDelay = willResolve => (ms, {value, signal} = {}) => { } delayPromise.clear = () => { - clearTimeout(timeoutId); + clear(timeoutId); timeoutId = null; cleanup(); settle(); @@ -53,7 +53,12 @@ const createDelay = willResolve => (ms, {value, signal} = {}) => { return delayPromise; }; -const delay = createDelay(true); -delay.reject = createDelay(false); +const delay = createDelay({willResolve: true}); +delay.reject = createDelay({willResolve: false}); +delay.createWithTimers = ({clearTimeout, setTimeout}) => { + const delay = createDelay({clearTimeout, setTimeout, willResolve: true}); + delay.reject = createDelay({clearTimeout, setTimeout, willResolve: false}); + return delay; +}; module.exports = delay; module.exports.default = delay; diff --git a/index.test-d.ts b/index.test-d.ts index 2950545..e4fbfd7 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -8,3 +8,12 @@ expectType(await delay(200, {value: 0})); expectType(await delay.reject(200, {value: '🦄'})); expectType(await delay.reject(200, {value: 0})); + +const customDelay = delay.createWithTimers({clearTimeout, setTimeout}) +expectType(await customDelay(200)); + +expectType(await customDelay(200, {value: '🦄'})); +expectType(await customDelay(200, {value: 0})); + +expectType(await customDelay.reject(200, {value: '🦄'})); +expectType(await customDelay.reject(200, {value: 0})); diff --git a/readme.md b/readme.md index c7db9aa..3d62fba 100644 --- a/readme.md +++ b/readme.md @@ -62,6 +62,9 @@ The returned promise will be rejected with an AbortError if the signal is aborte Clears the delay and settles the promise. +### delay.createWithTimers({clearTimeout, setTimeout}) + +Creates a new `delay` instance using the provided functions for clearing and setting timeouts. Useful if you're about to stub timers globally, but you still want to use `delay` to manage your tests. ## Advanced usage @@ -137,6 +140,22 @@ const delay = require('delay'); })(); ``` +Create a new instance that is unaffected by libraries such as [lolex](https://github.com/sinonjs/lolex/): + +```js +const delay = require('delay'); + +const customDelay = delay.createWithTimers({clearTimeout, setTimeout}); + +(async() => { + const result = await customDelay(100, {value: '🦄'}); + + // Executed after 100 milliseconds + console.log(result); + //=> '🦄' +})(); +``` + ## Related diff --git a/test.js b/test.js index 88492d0..ed1b298 100644 --- a/test.js +++ b/test.js @@ -124,3 +124,42 @@ test('rejects with AbortError if AbortSignal is already aborted', async t => { ); t.true(end() < 30); }); + +test('can create a new instance with fixed timeout methods', async t => { + const cleared = []; + const callbacks = []; + const custom = m.createWithTimers({ + clearTimeout(handle) { + cleared.push(handle); + }, + + setTimeout(callback, ms) { + const handle = Symbol('handle'); + callbacks.push({callback, handle, ms}); + return handle; + } + }); + + const first = custom(50, {value: 'first'}); + t.is(callbacks.length, 1); + t.is(callbacks[0].ms, 50); + callbacks[0].callback(); + t.is(await first, 'first'); + + const second = custom.reject(40, {value: 'second'}); + t.is(callbacks.length, 2); + t.is(callbacks[1].ms, 40); + callbacks[1].callback(); + try { + await second; + } catch (error) { + t.is(error, 'second'); + } + + const third = custom(60); + t.is(callbacks.length, 3); + t.is(callbacks[2].ms, 60); + third.clear(); + t.is(cleared.length, 1); + t.is(cleared[0], callbacks[2].handle); +});