sinonjs / fake-timers

Fake setTimeout and friends (collectively known as "timers"). Useful in your JavaScript tests. Extracted from Sinon.JS
BSD 3-Clause "New" or "Revised" License
797 stars 104 forks source link

Fix usage with TypeScript #365

Closed remcohaszing closed 3 years ago

remcohaszing commented 3 years ago

Purpose (TL;DR) - mandatory

Make generated TypeScript tyoe definitions usable. My goal was to make the types usable in a typical situation:

import { install, Clock } from '@sinonjs/fake-timers';

let clock: Clock;

beforeEach(() => {
  clock = install();
});

afterEach(() => {
  clock.uninstall();
});

My goal was not to provide perfect types. I.e. any and Function types are used and many docs are missing, because I’m not 100% sure what everything does.

Background (Problem in detail) - optional

Closes #356

Solution - optional

I added the Clock type definition. This is returned by both createClock() and install().


To aid reviewers, this is the generated .d.ts file after this merge request:

export type Clock = {
    now: number;
    timeouts: any;
    Date: any;
    loopLimit: number;
    requestIdleCallback: (func: Function, timeout: number) => any;
    cancelIdleCallback: (timerId: number) => any;
    setTimeout: (timerId: number, func: Function) => any;
    clearTimeout: (timerId: number) => any;
    nextTick: (func: Function) => any;
    queueMicrotask: (func: Function) => any;
    setInterval: (func: Function, timeout: number) => any;
    clearInterval: (timerId: number) => any;
    setImmediate: (func: Function) => any;
    clearImmediate: (timerId: number) => any;
    countTimers: () => number;
    requestAnimationFrame: (func: Function) => any;
    cancelAnimationFrame: (timerId: number) => any;
    runMicrotasks: () => void;
    tick: (tickValue: string | number) => number;
    next: () => number;
    nextAsync: (func: Function) => Promise<number>;
    runAll: (func: Function) => number;
    runToFrame: (func: Function) => number;
    runAllAsync: (func: Function) => Promise<number>;
    runToLast: () => number;
    runToLastAsync: () => Promise<number>;
    reset: () => void;
    setSystemTime: (systemTime: number | Date) => void;
    performance: any;
    hrTime: (prev: any) => number[];
    /**
     * Uninstall the clock.
     */
    uninstall: () => void;
    methods: any;
};
export namespace timers {
    export const setTimeout: any;
    export const clearTimeout: any;
    export const setInterval: any;
    export const clearInterval: any;
    const Date_1: any;
    export { Date_1 as Date };
}
/**
 * @param start {Date|number} the system time - non-integer values are floored
 * @param loopLimit {number}  maximum number of timers that will be run when calling runAll()
 * @returns {Clock}
 */
export function createClock(start: Date | number, loopLimit: number): Clock;
/**
 * Configuration object for the `install` method.
 *
 * @typedef {object} Config
 * @property [now] {number|Date}  a number (in milliseconds) or a Date object (default epoch)
 * @property [toFake] {string[]} names of the methods that should be faked.
 * @property [loopLimit] {number} the maximum number of timers that will be run when calling runAll()
 * @property [shouldAdvanceTime] {Boolean} tells FakeTimers to increment mocked time automatically (default false)
 * @property [advanceTimeDelta] {Number} increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
 */
/**
 * @param [config] {Config} optional config
 * @returns {Clock}
 */
export function install(config?: {
    /**
     * a number (in milliseconds) or a Date object (default epoch)
     */
    now?: number | Date;
    /**
     * names of the methods that should be faked.
     */
    toFake?: string[];
    /**
     * the maximum number of timers that will be run when calling runAll()
     */
    loopLimit?: number;
    /**
     * tells FakeTimers to increment mocked time automatically (default false)
     */
    shouldAdvanceTime?: boolean;
    /**
     * increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
     */
    advanceTimeDelta?: number;
}, ...args: any[]): Clock;
/**
 * @typedef {object} Clock
 * @property {number} now
 * @property {any} timeouts
 * @property {any} Date
 * @property {number} loopLimit
 * @property {(func: Function, timeout: number) => any} requestIdleCallback
 * @property {(timerId: number) => any} cancelIdleCallback
 * @property {(timerId: number, func: Function) => any} setTimeout
 * @property {(timerId: number) => any} clearTimeout
 * @property {(func: Function) => any} nextTick
 * @property {(func: Function) => any} queueMicrotask
 * @property {(func: Function, timeout: number) => any} setInterval
 * @property {(timerId: number) => any} clearInterval
 * @property {(func: Function) => any} setImmediate
 * @property {(timerId: number) => any} clearImmediate
 * @property {() => number} countTimers
 * @property {(func: Function) => any} requestAnimationFrame
 * @property {(timerId: number) => any} cancelAnimationFrame
 * @property {() => void} runMicrotasks
 * @property {(tickValue: string | number) => number} tick
 * @property {() => number} next
 * @property {(func: Function) => Promise<number>} nextAsync
 * @property {(func: Function) => number} runAll
 * @property {(func: Function) => number} runToFrame
 * @property {(func: Function) => Promise<number>} runAllAsync
 * @property {() => number} runToLast
 * @property {() => Promise<number>} runToLastAsync
 * @property {() => void} reset
 * @property {(systemTime: number | Date) => void} setSystemTime
 * @property {any} performance
 * @property {(prev: any) => number[]} hrTime
 * @property {() => void} uninstall Uninstall the clock.
 * @property {any} methods
 */
export function withGlobal(_global: any): {
    timers: {
        setTimeout: any;
        clearTimeout: any;
        setInterval: any;
        clearInterval: any;
        Date: any;
    };
    createClock: (start: Date | number, loopLimit: number) => Clock;
    install: (config?: {
        /**
         * a number (in milliseconds) or a Date object (default epoch)
         */
        now?: number | Date;
        /**
         * names of the methods that should be faked.
         */
        toFake?: string[];
        /**
         * the maximum number of timers that will be run when calling runAll()
         */
        loopLimit?: number;
        /**
         * tells FakeTimers to increment mocked time automatically (default false)
         */
        shouldAdvanceTime?: boolean;
        /**
         * increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
         */
        advanceTimeDelta?: number;
    }, ...args: any[]) => Clock;
    withGlobal: typeof withGlobal;
};
benjamingr commented 3 years ago

Thanks 🙏

fatso83 commented 3 years ago

Thanks @remcohaszing . I see you already contributed to the fake-timers types in DefinitelyTyped, so I guess we'll soon reach feature parity 😄 That being said, I did see several errors in the Sinon types (for core) in the DefinitelyTyped repo when I contributed some months back, so it's wise not to trust everything there (just noting it).

remcohaszing commented 3 years ago

Is there an ETA when this will be released? I think this is holding back a lot of people from uprading from 6.x.

mroderick commented 3 years ago

This has been published to the npm registry as @sinonjs/fake-timers@7.0.4

mroderick commented 3 years ago

Is there an ETA when this will be released? I think this is holding back a lot of people from uprading from 6.x.

I was enjoying a bit of vacation around Easter :)