diff --git a/types/lolex/index.d.ts b/types/lolex/index.d.ts index 1727c97ad3..35c4880a5b 100644 --- a/types/lolex/index.d.ts +++ b/types/lolex/index.d.ts @@ -1,10 +1,76 @@ -// Type definitions for lolex 2.1 +// Type definitions for lolex 3 // Project: https://github.com/sinonjs/lolex // Definitions by: Wim Looman // Josh Goldberg // Rogier Schouten +// Yishai Zehavi // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +/** + * Names of clock methods that may be faked by install. + */ +type FakeMethod = "setTimeout" | "clearTimeout" | "setImmediate" | "clearImmediate" | "setInterval" | "clearInterval" | "Date" | "nextTick" | "hrtime"; + +/** + * Global methods avaliable to every clock and also as standalone methods (inside `timers` global object). + */ +export interface GlobalTimers { + /** + * Schedules a callback to be fired once timeout milliseconds have ticked by. + * + * @param callback Callback to be fired. + * @param timeout How many ticks to wait to run the callback. + * @param args Any extra arguments to pass to the callback. + * @returns Time identifier for cancellation. + */ + setTimeout: (callback: () => void, timeout: number, ...args: any[]) => TTimerId; + + /** + * Clears a timer, as long as it was created using setTimeout. + * + * @param id Timer ID or object. + */ + clearTimeout: (id: TimerId) => void; + + /** + * Schedules a callback to be fired every time timeout milliseconds have ticked by. + * + * @param callback Callback to be fired. + * @param timeout How many ticks to wait between callbacks. + * @param args Any extra arguments to pass to the callback. + * @returns Time identifier for cancellation. + */ + setInterval: (callback: () => void, timeout: number, ...args: any[]) => TTimerId; + + /** + * Clears a timer, as long as it was created using setInterval. + * + * @param id Timer ID or object. + */ + clearInterval: (id: TTimerId) => void; + + /** + * Schedules the callback to be fired once 0 milliseconds have ticked by. + * + * @param callback Callback to be fired. + * @remarks You'll still have to call clock.tick() for the callback to fire. + * @remarks If called during a tick the callback won't fire until 1 millisecond has ticked by. + */ + setImmediate: (callback: () => void) => TTimerId; + + /** + * Clears a timer, as long as it was created using setImmediate. + * + * @param id Timer ID or object. + */ + clearImmediate: (id: TTimerId) => void; + + /** + * Implements the Date object but using this clock to provide the correct time. + */ + Date: typeof Date; +} + /** * Timer object used in node. */ @@ -25,102 +91,47 @@ export interface NodeTimer { */ export type TimerId = number | NodeTimer; -/** - * Lolex clock for a browser environment. - */ -type BrowserClock = LolexClock; - -/** - * Lolex clock for a Node environment. - */ -type NodeClock = LolexClock & { - /** - * Mimicks process.hrtime(). - * - * @param prevTime Previous system time to calculate time elapsed. - * @returns High resolution real time as [seconds, nanoseconds]. - */ - hrtime(prevTime?: [number, number]): [number, number]; -}; - -/** - * Clock object created by lolex. - */ -type Clock = BrowserClock | NodeClock; - -/** - * Names of clock methods that may be faked by install. - */ -type FakeMethod = "setTimeout" | "clearTimeout" | "setImmediate" | "clearImmediate" | "setInterval" | "clearInterval" | "Date" | "nextTick" | "hrtime"; - /** * Controls the flow of time. */ -export interface LolexClock { +export interface LolexClock extends GlobalTimers { /** * Current clock time. */ now: number; /** - * Implements the Date object but using this clock to provide the correct time. + * Don't know what this prop is for, but it was included in the clocks that `createClock` or + * `install` return (it is never used in the code, for now). */ - Date: typeof Date; + timeouts: {}; /** - * Schedules a callback to be fired once timeout milliseconds have ticked by. + * Maximum number of timers that will be run when calling runAll(). + */ + loopLimit: number; + + /** + * Schedule callback to run in the next animation frame. * * @param callback Callback to be fired. - * @param timeout How many ticks to wait to run the callback. - * @param args Any extra arguments to pass to the callback. - * @returns Time identifier for cancellation. + * @returns Request id. */ - setTimeout: (callback: () => any, timeout: number, ...args: any[]) => TTimerId; + requestAnimationFrame: (callback: (time: number) => void) => TTimerId; /** - * Clears a timer, as long as it was created using setTimeout. + * Cancel animation frame request. * - * @param id Timer ID or object. + * @param id The id returned from requestAnimationFrame method. */ - clearTimeout: (id: TTimerId) => void; + cancelAnimationFrame: (id: TTimerId) => void; - /** - * Schedules a callback to be fired every time timeout milliseconds have ticked by. - * - * @param callback Callback to be fired. - * @param timeout How many ticks to wait between callbacks. - * @param args Any extra arguments to pass to the callback. - * @returns Time identifier for cancellation. - */ - setInterval: (callback: () => any, timeout: number, ...args: any[]) => TTimerId; - - /** - * Clears a timer, as long as it was created using setInterval. - * - * @param id Timer ID or object. - */ - clearInterval: (id: TTimerId) => void; - - /** - * Schedules the callback to be fired once 0 milliseconds have ticked by. - * - * @param callback Callback to be fired. - * @remarks You'll still have to call clock.tick() for the callback to fire. - * @remarks If called during a tick the callback won't fire until 1 millisecond has ticked by. - */ - setImmediate: (callback: () => any) => TTimerId; - - /** - * Clears a timer, as long as it was created using setImmediate. - * - * @param id Timer ID or object. - */ - clearImmediate: (id: TTimerId) => void; - - /** - * Simulates process.nextTick(); - */ - nextTick: (callback: () => void) => void; + /** + * Get the number of waiting timers. + * + * @returns number of waiting timers. + */ + countTimers: () => number; /** * Advances the clock to the the moment of the first scheduled timer, firing it. @@ -134,6 +145,11 @@ export interface LolexClock { */ tick: (time: number | string) => void; + /** + * Removes all timers and tick without firing them and restore now to its original value. + */ + reset: () => void; + /** * Runs all pending timers until there are none remaining. * @@ -141,6 +157,11 @@ export interface LolexClock { */ runAll: () => void; + /** + * Advanced the clock to the next animation frame while firing all scheduled callbacks. + */ + runToFrame: () => void; + /** * Takes note of the last scheduled timer when it is run, and advances the clock to * that time firing callbacks as necessary. @@ -154,13 +175,72 @@ export interface LolexClock { * @remarks This affects the current time but it does not in itself cause timers to fire. */ setSystemTime: (now?: number | Date) => void; +} +/** + * Lolex clock for a browser environment. + */ +type BrowserClock = LolexClock & { + /** + * Mimics performance.now(). + */ + performance: { + now: () => number; + } +}; + +/** + * Lolex clock for a Node environment. + */ +type NodeClock = LolexClock & { + /** + * Mimicks process.hrtime(). + * + * @param prevTime Previous system time to calculate time elapsed. + * @returns High resolution real time as [seconds, nanoseconds]. + */ + hrtime(prevTime?: [number, number]): [number, number]; + + /** + * Mimics process.nextTick() explicitly dropping additional arguments. + */ + queueMicrotask: (callback: () => void) => void; + + /** + * Simulates process.nextTick(). + */ + nextTick: (callback: () => void) => void; + + /** + * Run all pending microtasks scheduled with nextTick. + */ + runMicrotasks: () => void; +}; + +/** + * Clock object created by lolex. + */ +type Clock = BrowserClock | NodeClock; + +/** + * Additional methods that installed clock have. + */ +type InstalledMethods = { /** * Restores the original methods on the context that was passed to lolex.install, * or the native timers if no context was given. */ uninstall: () => void; -} + + methods: FakeMethod[]; +}; + +/** + * Clock object created by calling `install();`. + * + * @type TClock type of base clock (e.g BrowserClock). + */ +type InstalledClock = TClock & InstalledMethods; /** * Creates a clock. @@ -174,7 +254,6 @@ export interface LolexClock { */ export declare function createClock(now?: number | Date, loopLimit?: number): TClock; - export interface LolexInstallOpts { /** * Installs lolex onto the specified target context (default: global) @@ -217,4 +296,20 @@ export interface LolexInstallOpts { * @param toFake Names of methods that should be faked. * @type TClock Type of clock to create. */ -export declare function install(opts?: LolexInstallOpts): TClock; +export declare function install(opts?: LolexInstallOpts): InstalledClock; + +export interface LolexWithContext { + timers: GlobalTimers; + createClock: (now?: number | Date, loopLimit?: number) => TClock; + install: (opts?: LolexInstallOpts) => InstalledClock; + withGlobal: (global: Object) => LolexWithContext; +} + +/** + * Apply new context to lolex. + * + * @param global New context to apply like `window` (in browsers) or `global` (in node). + */ +export declare function withGlobal(global: Object): LolexWithContext; + +export declare const timers: GlobalTimers; diff --git a/types/lolex/lolex-tests.ts b/types/lolex/lolex-tests.ts index f698eac3cc..2a01dd5916 100644 --- a/types/lolex/lolex-tests.ts +++ b/types/lolex/lolex-tests.ts @@ -1,5 +1,17 @@ import lolex = require("lolex"); +const global: lolex.LolexWithContext = lolex.withGlobal({}); +const timers: lolex.GlobalTimers = lolex.timers; + +const lolexTimeout: lolex.TimerId = timers.setTimeout(() => {}, 42); +const lolexInterval: lolex.TimerId = timers.setInterval(() => {}, 42); +const lolexImmediate: lolex.TimerId = timers.setImmediate(() => {}); +const lolexDate: Date = new timers.Date(); + +timers.clearTimeout(lolexTimeout); +timers.clearInterval(lolexInterval); +timers.clearImmediate(lolexImmediate); + let browserClock: lolex.BrowserClock = lolex.createClock() as lolex.BrowserClock; let nodeClock: lolex.NodeClock = lolex.createClock() as lolex.NodeClock; @@ -16,7 +28,7 @@ lolex.createClock(new Date()); lolex.createClock(7, 9001); lolex.createClock(new Date(), 9001); -lolex.install({ +const browserInstalledClock = lolex.install({ advanceTimeDelta: 20, loopLimit: 10, now: 0, @@ -25,7 +37,7 @@ lolex.install({ toFake: ["setTimeout", "nextTick", "hrtime"] }); -lolex.install({ +const nodeInstalledClock = lolex.install({ advanceTimeDelta: 20, loopLimit: 10, now: new Date(0), @@ -35,7 +47,10 @@ lolex.install({ }); const browserNow: number = browserClock.now; +const browserTimeouts: Object = browserClock.timeouts; +const browserLoopLimit: number = browserClock.loopLimit; const browserDate: Date = new browserClock.Date(); +const browserPerformanceNow: number = browserClock.performance.now(); const nodeNow: number = nodeClock.now; const nodeDate: Date = new nodeClock.Date(); @@ -43,30 +58,45 @@ const nodeDate: Date = new nodeClock.Date(); const browserTimeout: number = browserClock.setTimeout(() => {}, 7); const browserInterval: number = browserClock.setInterval(() => {}, 7); const browserImmediate: number = browserClock.setImmediate(() => {}); +const browserAnimationFrame: number = browserClock.requestAnimationFrame(() => {}); const nodeTimeout: lolex.NodeTimer = nodeClock.setTimeout(() => {}, 7); const nodeInterval: lolex.NodeTimer = nodeClock.setInterval(() => {}, 7); const nodeImmediate: lolex.NodeTimer = nodeClock.setImmediate(() => {}); +const nodeAnimationFrame: lolex.NodeTimer = nodeClock.requestAnimationFrame(() => {}); + +nodeTimeout.ref(); +nodeTimeout.unref(); browserClock.clearTimeout(browserTimeout); browserClock.clearInterval(browserInterval); browserClock.clearImmediate(browserImmediate); +browserClock.cancelAnimationFrame(browserAnimationFrame); nodeClock.clearTimeout(nodeTimeout); nodeClock.clearInterval(nodeInterval); nodeClock.clearImmediate(nodeImmediate); +nodeClock.cancelAnimationFrame(nodeAnimationFrame); browserClock.tick(7); browserClock.tick("08"); nodeClock.tick(7); -nodeClock.tick("08"); +nodeClock.tick("08:03"); browserClock.next(); nodeClock.next(); +browserClock.reset(); +nodeClock.reset(); + browserClock.runAll(); nodeClock.runAll(); +nodeClock.runMicrotasks(); + +browserClock.runToFrame(); +nodeClock.runToFrame(); + browserClock.runToLast(); nodeClock.runToLast(); @@ -79,9 +109,23 @@ nodeClock.setSystemTime(7); nodeClock.setSystemTime(new Date()); nodeClock.nextTick(() => undefined); +nodeClock.queueMicrotask(() => {}); -browserClock.uninstall(); -nodeClock.uninstall(); +const browserTimersCount: number = browserClock.countTimers(); +const nodeTimersCount: number = nodeClock.countTimers(); + +let [secs, nanos] = nodeClock.hrtime([0, 0]); +[secs, nanos] = nodeClock.hrtime(); + +// shows that typescript successfully infer the return values as numbers. +secs.toFixed(); +nanos.toExponential(); + +browserInstalledClock.performance.now(); +nodeInstalledClock.nextTick(() => {}); + +browserInstalledClock.uninstall(); +nodeInstalledClock.uninstall(); // Clocks should be typed to have unbound method signatures that can be passed around const { clearTimeout } = browserClock;