From 62334a684032ae2d57af19210bbf739d0d799118 Mon Sep 17 00:00:00 2001 From: Edward Gigolaev <49155506+movpushmov@users.noreply.github.com> Date: Sat, 9 Dec 2023 21:48:24 +0300 Subject: [PATCH 1/5] feat: store with time functions implementation Now debounce, throttle, delay, interval use time functions implementations, which can set by user manually. By default in store used time functions from environment. --- src/debounce/index.ts | 11 ++-- src/delay/index.ts | 12 +++-- src/interval/index.ts | 13 ++--- src/throttle/index.ts | 7 +-- src/timers/index.ts | 25 +++++++++ src/timers/timers.test.ts | 104 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 153 insertions(+), 19 deletions(-) create mode 100644 src/timers/index.ts create mode 100644 src/timers/timers.test.ts diff --git a/src/debounce/index.ts b/src/debounce/index.ts index b56ad775..ec329adb 100644 --- a/src/debounce/index.ts +++ b/src/debounce/index.ts @@ -1,5 +1,4 @@ import { - createEffect, createEvent, createStore, is, @@ -11,6 +10,7 @@ import { UnitTargetable, EventAsReturnType, } from 'effector'; +import { $timers } from '../timers' export function debounce(_: { source: Unit; @@ -51,13 +51,14 @@ export function debounce({ const timerFx = attach({ name: name || `debounce(${(source as any)?.shortName || source.kind}) effect`, - source: $canceller, - effect([ timeoutId, rejectPromise ], timeout: number) { - if (timeoutId) clearTimeout(timeoutId); + source: { canceller: $canceller, timers: $timers }, + effect({ canceller: [ timeoutId, rejectPromise ], timers }, timeout: number) { + if (timeoutId) timers.clearTimeout(timeoutId); if (rejectPromise) rejectPromise(); + return new Promise((resolve, reject) => { saveCancel([ - setTimeout(resolve, timeout), + timers.setTimeout(resolve, timeout), reject ]) }); diff --git a/src/delay/index.ts b/src/delay/index.ts index 44b08001..d4703358 100644 --- a/src/delay/index.ts +++ b/src/delay/index.ts @@ -12,6 +12,7 @@ import { UnitValue, UnitTargetable, } from 'effector'; +import { $timers, Timers } from '../timers' type TimeoutType = ((payload: Payload) => number) | Store | number; @@ -47,24 +48,25 @@ export function delay< const ms = validateTimeout(timeout); const timerFx = createEffect< - { payload: UnitValue; milliseconds: number }, + { payload: UnitValue; milliseconds: number; timers: Timers }, UnitValue >( - ({ payload, milliseconds }) => + ({ payload, milliseconds, timers }) => new Promise((resolve) => { - setTimeout(resolve, milliseconds, payload); + timers.setTimeout(resolve, milliseconds, payload); }), ); sample({ // ms can be Store | number // converts object of stores or object of values to store - source: combine({ milliseconds: ms }), + source: combine({ milliseconds: ms, timers: $timers }), clock: source as Unit, - fn: ({ milliseconds }, payload) => ({ + fn: ({ milliseconds, timers }, payload) => ({ payload, milliseconds: typeof milliseconds === 'function' ? milliseconds(payload) : milliseconds, + timers, }), target: timerFx, }); diff --git a/src/interval/index.ts b/src/interval/index.ts index 4964cc5a..e577b07e 100644 --- a/src/interval/index.ts +++ b/src/interval/index.ts @@ -1,4 +1,5 @@ import { Event, Store, createEvent, createStore, sample, attach, is } from 'effector'; +import { $timers } from '../timers' export function interval(config: { timeout: number | Store; @@ -51,24 +52,24 @@ export function interval({ ); const timeoutFx = attach({ - source: { timeout: $timeout, running: $isRunning }, - effect: ({ timeout, running }) => { + source: { timeout: $timeout, running: $isRunning, timers: $timers }, + effect: ({ timeout, running, timers }) => { if (!running) { return Promise.reject(); } return new Promise((resolve, reject) => { - const timeoutId = setTimeout(resolve, timeout); + const timeoutId = timers.setTimeout(resolve, timeout); saveTimeout({ timeoutId, reject }); }); }, }); const cleanupFx = attach({ - source: { timeoutId: $timeoutId, rejecter: $rejecter }, - effect: ({ timeoutId, rejecter }) => { + source: { timeoutId: $timeoutId, rejecter: $rejecter, timers: $timers }, + effect: ({ timeoutId, rejecter, timers }) => { rejecter(); - if (timeoutId) clearTimeout(timeoutId); + if (timeoutId) timers.clearTimeout(timeoutId); }, }); diff --git a/src/throttle/index.ts b/src/throttle/index.ts index 687a0073..aa850a40 100644 --- a/src/throttle/index.ts +++ b/src/throttle/index.ts @@ -9,6 +9,7 @@ import { Unit, UnitTargetable, } from 'effector'; +import { $timers, Timers } from '../timers'; type EventAsReturnType = any extends Payload ? Event : never; @@ -37,9 +38,9 @@ export function throttle({ const $timeout = toStoreNumber(timeout); - const timerFx = createEffect({ + const timerFx = createEffect<{ timeout: number; timers: Timers }, void>({ name: `throttle(${(source as Event).shortName || source.kind}) effect`, - handler: (timeout) => new Promise((resolve) => setTimeout(resolve, timeout)), + handler: ({ timeout, timers }) => new Promise((resolve) => timers.setTimeout(resolve, timeout)), }); // It's ok - nothing will ever start unless source is triggered @@ -61,7 +62,7 @@ export function throttle({ }); sample({ - source: $timeout, + source: { timeout: $timeout, timers: $timers }, clock: triggerTick as Unit, target: timerFx, }); diff --git a/src/timers/index.ts b/src/timers/index.ts new file mode 100644 index 00000000..b8f1b178 --- /dev/null +++ b/src/timers/index.ts @@ -0,0 +1,25 @@ +import { createEvent, createStore, sample } from 'effector' +import { clearTimeout } from 'timers' + +type Handler = (...args: any[]) => void; + +export interface Timers { + setTimeout(handler: Handler, timeout?: number, ...args: any[]): NodeJS.Timeout; + clearTimeout(handle: NodeJS.Timeout): void; + now(): number; +} + +export const $timers = createStore({ + setTimeout: (handler, timeout, ...args) => setTimeout(handler, timeout, ...args) as unknown as any, + clearTimeout: (handle) => clearTimeout(handle), + now: () => Date.now(), +}); + +export const setupTimers = createEvent>(); + +sample({ + clock: setupTimers, + source: $timers, + fn: (timers, overrides) => ({ ...timers, ...overrides }), + target: $timers, +}); diff --git a/src/timers/timers.test.ts b/src/timers/timers.test.ts new file mode 100644 index 00000000..b8f1c4fa --- /dev/null +++ b/src/timers/timers.test.ts @@ -0,0 +1,104 @@ +import 'regenerator-runtime/runtime'; +import { + createEvent, + fork, allSettled +} from 'effector'; +import { wait, watch } from '../../test-library'; +import { setupTimers, Timers } from '.' +import { throttle } from '../throttle'; +import { delay } from '../delay'; +import { interval } from '../interval'; +import { debounce } from '../debounce'; + +const customSetTimeout: Timers['setTimeout'] = (handler, _, ...args) => setTimeout(handler, 50, ...args) as unknown as NodeJS.Timeout; + +describe('Custom time functions', () => { + test('interval', async () => { + const scope = fork(); + const mockedSetTimeout = jest.fn(customSetTimeout); + + await allSettled(setupTimers, { scope, params: { setTimeout: mockedSetTimeout } }); + + const start = createEvent(); + const stop = createEvent(); + const { tick } = interval({ timeout: 500, start, stop }); + const fn = watch(tick); + + allSettled(start, { scope }); + + expect(mockedSetTimeout).toBeCalled(); + await wait(52); + + expect(fn).toBeCalled(); + }); + + test('throttle', async () => { + const scope = fork(); + const mockedSetTimeout = jest.fn(customSetTimeout); + + await allSettled(setupTimers, { scope, params: { setTimeout: mockedSetTimeout } }); + + const watcher = jest.fn(); + + const trigger = createEvent(); + const throttled = throttle({ source: trigger, timeout: 500 }); + + throttled.watch(watcher); + + allSettled(trigger, { scope }); + allSettled(trigger, { scope }); + allSettled(trigger, { scope }); + + expect(watcher).not.toBeCalled(); + expect(mockedSetTimeout).toBeCalled(); + + await wait(52); + + expect(watcher).toBeCalledTimes(1); + }); + + test('delay', async () => { + const scope = fork(); + const mockedSetTimeout = jest.fn(customSetTimeout); + + await allSettled(setupTimers, { scope, params: { setTimeout: mockedSetTimeout } }); + + const trigger = createEvent(); + const triggered = delay({ source: trigger, timeout: 500 }); + + const fn = watch(triggered); + + allSettled(trigger, { scope }); + + expect(mockedSetTimeout).toBeCalled(); + expect(fn).not.toBeCalled(); + + await wait(52); + + expect(fn).toBeCalled(); + }); + + test('debounce', async () => { + const scope = fork(); + const mockedSetTimeout = jest.fn(customSetTimeout); + + await allSettled(setupTimers, { scope, params: { setTimeout: mockedSetTimeout } }); + + const watcher = jest.fn(); + + const trigger = createEvent(); + const debounced = debounce({ source: trigger, timeout: 500 }); + + debounced.watch(watcher); + + allSettled(trigger, { scope }); + allSettled(trigger, { scope }); + allSettled(trigger, { scope }); + + expect(watcher).not.toBeCalled(); + expect(mockedSetTimeout).toBeCalled(); + + await wait(52); + expect(watcher).toBeCalledTimes(1); + }); +}); From 73a8c506bd8c6a90463e4182494d40c4e149b610 Mon Sep 17 00:00:00 2001 From: Edward Gigolaev <49155506+movpushmov@users.noreply.github.com> Date: Sat, 9 Dec 2023 23:18:40 +0300 Subject: [PATCH 2/5] fix: add stop await for interval test --- src/interval/index.ts | 2 +- src/timers/timers.test.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/interval/index.ts b/src/interval/index.ts index e577b07e..61ecaf35 100644 --- a/src/interval/index.ts +++ b/src/interval/index.ts @@ -59,7 +59,7 @@ export function interval({ } return new Promise((resolve, reject) => { - const timeoutId = timers.setTimeout(resolve, timeout); + const timeoutId = timers.setTimeout(resolve, timeout); saveTimeout({ timeoutId, reject }); }); }, diff --git a/src/timers/timers.test.ts b/src/timers/timers.test.ts index b8f1c4fa..309db9d4 100644 --- a/src/timers/timers.test.ts +++ b/src/timers/timers.test.ts @@ -27,7 +27,9 @@ describe('Custom time functions', () => { allSettled(start, { scope }); expect(mockedSetTimeout).toBeCalled(); + await wait(52); + await allSettled(stop, { scope }); expect(fn).toBeCalled(); }); From db2a24e8f88b0195a28715ebb20ee2d44543be5d Mon Sep 17 00:00:00 2001 From: Edward Gigolaev <49155506+movpushmov@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:03:38 +0300 Subject: [PATCH 3/5] add readme --- src/timers/index.ts | 2 +- src/timers/readme.md | 64 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/timers/readme.md diff --git a/src/timers/index.ts b/src/timers/index.ts index b8f1b178..910ae0e2 100644 --- a/src/timers/index.ts +++ b/src/timers/index.ts @@ -10,7 +10,7 @@ export interface Timers { } export const $timers = createStore({ - setTimeout: (handler, timeout, ...args) => setTimeout(handler, timeout, ...args) as unknown as any, + setTimeout: (handler, timeout, ...args) => setTimeout(handler, timeout, ...args) as unknown as NodeJS.Timeout, clearTimeout: (handle) => clearTimeout(handle), now: () => Date.now(), }); diff --git a/src/timers/readme.md b/src/timers/readme.md new file mode 100644 index 00000000..7adcf7a0 --- /dev/null +++ b/src/timers/readme.md @@ -0,0 +1,64 @@ +# setupTimers + +```ts +import { setupTimers } from 'patronum'; +// or +import { setupTimers } from 'patronum/timers'; +``` + +### Motivation + +The event allow to manually set implementations of setTimeout/clearTimeout, which used in delay/interval/throttle/debounce + +### Formulae + +```ts +setupTimers({ setTimeout: () => {} }) +``` + +- Reassign implementations of setTimeout/clearTimeout in current scope +- If doesn't called then delay/interval/throttle/debounce uses global setTimeout/clearTimeout + +### Arguments + +```typescript +Partial<{ + setTimeout: (handler: (...args: any[]) => void, timeout?: number, ...args: any[]) => NodeJS.Timeout; + clearTimeout: (handle: NodeJS.Timeout) => void; + now: () => number; +}> +``` + +### Example + +```ts +import { setupTimers, interval } from 'patronum'; +import { createEvent, sample } from 'effector'; + +const fakeTimers = { + setTimeout: (handler, timeout) => setTimeout(handler, timeout * 2), // your fake implementation + clearTimeout: clearTimeout, // your fake implementation + now: () => Date.now() * 2 // your fake implementation +}; + +sample({ + clock: appStarted, + fn: () => fakeTimers, + target: setupTimers, +}); + +// ... + +const startInterval = createEvent(); +const stopInterval = createEvent(); +const tick = interval({ start: startInterval, stop: stopInterval, timeout: 400 }); + +tick.watch(() => console.log('Called after 800ms')); + +const someUIEvent = createEvent(); + +sample({ + clock: someUIEvent, + target: startInterval, +}); +``` From ed602d9486d41ec191e1f49ed5411fb81c7d87ce Mon Sep 17 00:00:00 2001 From: Edward Gigolaev <49155506+movpushmov@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:02:05 +0300 Subject: [PATCH 4/5] fix build & add 'now' method --- rollup.config.js | 6 +-- scripts/build.js | 4 +- scripts/libraries.js | 19 ++++++-- src/babel-plugin-factories.json | 8 +++- src/index.md | 6 +++ src/now/index.ts | 25 ++++++++++ src/now/readme.md | 56 +++++++++++++++++++++++ src/testing-library/index.ts | 11 +++++ src/{timers => testing-library}/readme.md | 4 +- src/timers/index.ts | 11 +---- src/timers/timers.test.ts | 45 +++++++++++++++++- 11 files changed, 170 insertions(+), 25 deletions(-) create mode 100644 src/now/index.ts create mode 100644 src/now/readme.md create mode 100644 src/testing-library/index.ts rename src/{timers => testing-library}/readme.md (93%) diff --git a/rollup.config.js b/rollup.config.js index b08e71c2..b578788c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -14,7 +14,7 @@ const input = 'dist/index.cjs'; export default [ { input: 'dist/index.js', - external: ['effector'], + external: ['effector', 'timers'], plugins, output: { file: './dist/patronum.js', @@ -25,7 +25,7 @@ export default [ }, { input, - external: ['effector'], + external: ['effector', 'timers'], plugins, output: { file: './dist/patronum.cjs', @@ -38,7 +38,7 @@ export default [ }, { input, - external: ['effector'], + external: ['effector', 'timers'], plugins, output: { name: 'patronum', diff --git a/scripts/build.js b/scripts/build.js index 973bbd44..e49107ee 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -53,12 +53,14 @@ async function main() { ), ); + await directory.write('index.cjs', createCommonJsIndex(names)); await directory.write('index.js', createMjsIndex(names)); await directory.write('index.d.ts', createTypingsIndex(names)); await directory.write('macro.d.ts', 'export * from "./index";'); - const productionMethods = names.filter((method) => method !== 'debug'); + const productionMethods = names.filter((method) => method !== 'debug' && method !== 'testing-library'); + const factoriesJson = createFactoriesJson(library, productionMethods); const fileName = 'babel-plugin-factories.json'; await writeFile(`./src/${fileName}`, JSON.stringify(factoriesJson, null, 2)); diff --git a/scripts/libraries.js b/scripts/libraries.js index 8242cd07..eb400731 100644 --- a/scripts/libraries.js +++ b/scripts/libraries.js @@ -20,10 +20,20 @@ function resolveMethods() { return names; } +function resolveExportName(name) { + switch (name) { + case 'timers': + return '$timers'; + case 'testing-library': + return 'setupTimers'; + default: + return camelCase(name); + } +} + function createCommonJsIndex(names) { const imports = names.sort().map((name) => { - const camel = camelCase(name); - return `module.exports.${camel} = require('./${name}/index.cjs').${camel};`; + return `module.exports.${resolveExportName(name)} = require('./${name}/index.cjs').${resolveExportName(name)};`; }); return imports.join('\n') + '\n'; @@ -31,8 +41,7 @@ function createCommonJsIndex(names) { function createMjsIndex(names) { const imports = names.sort().map((name) => { - const camel = camelCase(name); - return `export { ${camel} } from './${name}/index.js'`; + return `export { ${resolveExportName(name)} } from './${name}/index.js'`; }); return imports.join('\n') + '\n'; @@ -41,7 +50,7 @@ function createMjsIndex(names) { function createTypingsIndex(names) { const types = names .sort() - .map((name) => `export { ${camelCase(name)} } from './${name}';`); + .map((name) => `export { ${resolveExportName(name)} } from './${name}';`); return types.join('\n') + '\n'; } diff --git a/src/babel-plugin-factories.json b/src/babel-plugin-factories.json index 8fc1e510..82093865 100644 --- a/src/babel-plugin-factories.json +++ b/src/babel-plugin-factories.json @@ -14,6 +14,7 @@ "patronum/in-flight", "patronum/interval", "patronum/not", + "patronum/now", "patronum/once", "patronum/or", "patronum/pending", @@ -25,7 +26,8 @@ "patronum/spread", "patronum/status", "patronum/throttle", - "patronum/time" + "patronum/time", + "patronum/timers" ], "mapping": { "and": "and", @@ -41,6 +43,7 @@ "inFlight": "in-flight", "interval": "interval", "not": "not", + "now": "now", "once": "once", "or": "or", "pending": "pending", @@ -52,6 +55,7 @@ "spread": "spread", "status": "status", "throttle": "throttle", - "time": "time" + "time": "time", + "timers": "timers" } } \ No newline at end of file diff --git a/src/index.md b/src/index.md index 632a8312..ba6961ba 100644 --- a/src/index.md +++ b/src/index.md @@ -33,6 +33,7 @@ All methods split into categories. - [interval](./interval/readme.md) — Creates a dynamic interval with any timeout. - [throttle](./throttle/readme.md) — Creates event which triggers at most once per timeout. - [time](./time/readme.md) — Allows reading current timestamp by triggering clock. +- [now](./now/readme.md) — Allows reading current timestamp by triggering clock from [timers](testing-library/readme.md). ### Combination/Decomposition @@ -46,3 +47,8 @@ All methods split into categories. ### Debug - [debug](./debug/readme.md) — Log triggers of passed units. + + +### Testing + +- [setupTimers](testing-library/readme.md) — Provide custom implementations of setTimeout/clearTimeout diff --git a/src/now/index.ts b/src/now/index.ts new file mode 100644 index 00000000..badd415c --- /dev/null +++ b/src/now/index.ts @@ -0,0 +1,25 @@ +import { + combine, + createStore, + sample, + Store, + Unit +} from 'effector' +import { $timers } from '../timers' + +export function now({ clock }: { clock?: Unit } = {}): Store { + if (clock) { + const $counter = createStore(0); + + sample({ + clock, + source: $counter, + fn: (count) => count + 1, + target: $counter, + }); + + return combine({ timers: $timers, $counter }, ({ timers }) => timers.now()); + } + + return $timers.map((timers) => timers.now()); +} diff --git a/src/now/readme.md b/src/now/readme.md new file mode 100644 index 00000000..1fdef216 --- /dev/null +++ b/src/now/readme.md @@ -0,0 +1,56 @@ +# now + +```ts +import { now } from 'patronum'; +// or +import { now } from 'patronum/now'; +``` + +### Motivation + +The method allow to read current time from +custom timers implementation just as an effector method. + +Use this for easy testing of business logic based on time. + +### Formulae + +```ts +$now = now({ clock }); +// or +$now = now(); +``` + +- Initialize `$now` with `$timers.now` +- When `clock` is triggered, call `$timers.now` to update `$now` with results + +### Arguments + +1. `clock: Event | Effect | Store` — The unit triggers time reading and updates `$now` store + +### Returns + +`$now: Store` — Store contains unix timestamp snapshot, updates when `clock` triggers. + +### Example + +```ts +import { now } from 'patronum'; +import { setupTimers } from 'patronum/testing-library' +import { allSettled, fork } from 'effector' + +const tick = createEvent(); +const $now = now({ clock: tick }); + +const scope = fork(); + +await allSettled(setupTimers, { scope, params: { now: () => 1000 } }); + +$now.watch((time) => console.log('time', time)); +// => time 1000 + +await allSettled(setupTimers, { scope, params: { now: () => 2000 } }); +await allSettled(tick, { scope }); + +// => time 2000 +``` diff --git a/src/testing-library/index.ts b/src/testing-library/index.ts new file mode 100644 index 00000000..f6672fce --- /dev/null +++ b/src/testing-library/index.ts @@ -0,0 +1,11 @@ +import { createEvent, sample } from 'effector' +import { $timers, Timers } from '../timers' + +export const setupTimers = createEvent>(); + +sample({ + clock: setupTimers, + source: $timers, + fn: (timers, overrides) => ({ ...timers, ...overrides }), + target: $timers, +}); diff --git a/src/timers/readme.md b/src/testing-library/readme.md similarity index 93% rename from src/timers/readme.md rename to src/testing-library/readme.md index 7adcf7a0..71bc1007 100644 --- a/src/timers/readme.md +++ b/src/testing-library/readme.md @@ -1,9 +1,7 @@ # setupTimers ```ts -import { setupTimers } from 'patronum'; -// or -import { setupTimers } from 'patronum/timers'; +import { setupTimers } from 'patronum/testing'; ``` ### Motivation diff --git a/src/timers/index.ts b/src/timers/index.ts index 910ae0e2..8088d766 100644 --- a/src/timers/index.ts +++ b/src/timers/index.ts @@ -1,4 +1,4 @@ -import { createEvent, createStore, sample } from 'effector' +import { createStore } from 'effector' import { clearTimeout } from 'timers' type Handler = (...args: any[]) => void; @@ -14,12 +14,3 @@ export const $timers = createStore({ clearTimeout: (handle) => clearTimeout(handle), now: () => Date.now(), }); - -export const setupTimers = createEvent>(); - -sample({ - clock: setupTimers, - source: $timers, - fn: (timers, overrides) => ({ ...timers, ...overrides }), - target: $timers, -}); diff --git a/src/timers/timers.test.ts b/src/timers/timers.test.ts index 309db9d4..182eb26f 100644 --- a/src/timers/timers.test.ts +++ b/src/timers/timers.test.ts @@ -4,11 +4,13 @@ import { fork, allSettled } from 'effector'; import { wait, watch } from '../../test-library'; -import { setupTimers, Timers } from '.' import { throttle } from '../throttle'; import { delay } from '../delay'; import { interval } from '../interval'; import { debounce } from '../debounce'; +import { Timers } from '.' +import { setupTimers } from '../testing-library' +import { now } from '../now' const customSetTimeout: Timers['setTimeout'] = (handler, _, ...args) => setTimeout(handler, 50, ...args) as unknown as NodeJS.Timeout; @@ -103,4 +105,45 @@ describe('Custom time functions', () => { await wait(52); expect(watcher).toBeCalledTimes(1); }); + + test('now with clock', async () => { + let lastTime = 0; + const scope = fork(); + await allSettled(setupTimers, { + scope, + params: { + now: () => { + lastTime += 1000; + return lastTime; + } + } + }); + + const tick = createEvent(); + const $now = now({ clock: tick }); + + expect(scope.getState($now)).toBe(1000); + await allSettled(tick, { scope }); + expect(scope.getState($now)).toBe(2000); + }); + + test('now without clock', async () => { + let lastTime = 0; + const scope = fork(); + await allSettled(setupTimers, { + scope, + params: { + now: () => { + lastTime += 1000; + return lastTime; + } + } + }); + + const $now = now(); + + expect(scope.getState($now)).toBe(1000); + // check what $now used cached value from previous $timers.now call + expect(scope.getState($now)).toBe(1000); + }); }); From a0765a3c2c450994aeb67991c295d3556a33efb4 Mon Sep 17 00:00:00 2001 From: Edward Gigolaev <49155506+movpushmov@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:10:57 +0300 Subject: [PATCH 5/5] feat: add typings tests --- test-typings/now.ts | 45 +++++++++++++++++++++++++++++++++ test-typings/testing-library.ts | 37 +++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 test-typings/now.ts create mode 100644 test-typings/testing-library.ts diff --git a/test-typings/now.ts b/test-typings/now.ts new file mode 100644 index 00000000..329f6ac2 --- /dev/null +++ b/test-typings/now.ts @@ -0,0 +1,45 @@ +import { now } from '../dist/now'; +import { createEvent, Store } from 'effector'; +import { expectType } from 'tsd'; + +// Always returns the store +{ + const $a = now(); + const $b = now({ clock: createEvent() }); + + expectType>($a); + expectType>($b); +} + +// Should not receive non-unit as argument in clock +{ + // @ts-expect-error + now(1); + + // @ts-expect-error + now(true); + + // @ts-expect-error + now(''); + + // @ts-expect-error + now({ a: 123 }); + + // @ts-expect-error + now([]); + + // @ts-expect-error + now({ clock: 1 }); + + // @ts-expect-error + now({ clock: true }); + + // @ts-expect-error + now({ clock: '' }); + + // @ts-expect-error + now({ clock: { a: 123 } }); + + // @ts-expect-error + now({ clock: [] }); +} diff --git a/test-typings/testing-library.ts b/test-typings/testing-library.ts new file mode 100644 index 00000000..0f392429 --- /dev/null +++ b/test-typings/testing-library.ts @@ -0,0 +1,37 @@ +import { setupTimers } from '../dist/testing-library'; + +// Should not receive setTimeout which returns not number or NodeJS.Timeout +{ + // @ts-expect-error + setupTimers({ setTimeout: () => '' }); + + // @ts-expect-error + setupTimers({ setTimeout: () => [] }); + + // @ts-expect-error + setupTimers({ setTimeout: () => {} }); + + // @ts-expect-error + setupTimers({ setTimeout: () => ({}) }); + + // @ts-expect-error + setupTimers({ setTimeout: () => true }); +} + +// Should not receive now which returns not number +{ + // @ts-expect-error + setupTimers({ now: () => '' }); + + // @ts-expect-error + setupTimers({ now: () => [] }); + + // @ts-expect-error + setupTimers({ now: () => {} }); + + // @ts-expect-error + setupTimers({ now: () => ({}) }); + + // @ts-expect-error + setupTimers({ now: () => true }); +}