From 7ab36b7ff67ae0fa5a7cd32dc8aa078c21ecfd40 Mon Sep 17 00:00:00 2001 From: Jy <1041207253@qq.com> Date: Sat, 23 Jan 2021 14:50:51 +0800 Subject: [PATCH 1/2] feat(composable): add a new composable --- docs/.vuepress/components/timeoutExample.vue | 35 ++++++++ docs/composable/web/timeout.md | 85 +++++++++++++++++++ .../__tests__/web/timeout.spec.ts | 72 ++++++++++++++++ packages/vue-composable/src/web/index.ts | 1 + packages/vue-composable/src/web/timeout.ts | 43 ++++++++++ 5 files changed, 236 insertions(+) create mode 100644 docs/.vuepress/components/timeoutExample.vue create mode 100644 docs/composable/web/timeout.md create mode 100644 packages/vue-composable/__tests__/web/timeout.spec.ts create mode 100644 packages/vue-composable/src/web/timeout.ts diff --git a/docs/.vuepress/components/timeoutExample.vue b/docs/.vuepress/components/timeoutExample.vue new file mode 100644 index 000000000..612ae0c0f --- /dev/null +++ b/docs/.vuepress/components/timeoutExample.vue @@ -0,0 +1,35 @@ + + diff --git a/docs/composable/web/timeout.md b/docs/composable/web/timeout.md new file mode 100644 index 000000000..03dad9c13 --- /dev/null +++ b/docs/composable/web/timeout.md @@ -0,0 +1,85 @@ +# Timeout + +> The [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout). + +## Parameters + +```js +import { useTimeout } from "vue-composable"; + +useTimeout(fn, delay); +``` + +| Parameters | Type | Required | Default | Description | +| ---------- | ---------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------- | +| fn | `Function` | `true` | | A function to be executed after the timer expires. | +| delay | `Number` | `false` | `0 ` | The time, in milliseconds (thousandths of a second), the timer should wait before the specified function is executed. | + +## State + +The `useTimeout` function exposes the following reactive state: + +```js +import { useTimeout } from "vue-composable"; + +const { ready } = useTimeout(fn, delay); +``` + +| State | Type | Description | +| ----- | ------------ | ----------- | ---------------------------------------------------------------------------------------------------- | +| ready | `Ref` | current timeout state:
 false - pending
 true - called
 null - canceled | + +## Methods + +The `useTimeout` function exposes the following methods: + +```js +import { useTimeout } from "vue-composable"; + +const { cancel } = useTimeout(fn, delay); +``` + +| Signature | Description | +| --------- | ------------------ | +| `cancel` | cancel the timeout | + +## Example + + + +### Code + +```vue + + +``` diff --git a/packages/vue-composable/__tests__/web/timeout.spec.ts b/packages/vue-composable/__tests__/web/timeout.spec.ts new file mode 100644 index 000000000..d7a605212 --- /dev/null +++ b/packages/vue-composable/__tests__/web/timeout.spec.ts @@ -0,0 +1,72 @@ +import { useTimeout } from "../../src"; +import { createVue } from "../utils"; +import { Ref, ref } from "../../src/api"; + +function sleep(duration: number): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(true); + }, duration); + }); +} + +describe("timeout", () => { + it("should be defined", () => { + expect(useTimeout).toBeDefined(); + }); + + it("should call passed function after given amount of time", async () => { + let count = 0; + useTimeout(() => { + count++; + }, 1000); + + expect(count).toBe(0); + await sleep(1000); + expect(count).toBe(1); + }); + + it("should set ready true after run callback", async () => { + const { ready } = useTimeout(() => {}, 1000); + + expect(ready.value).toBe(false); + await sleep(1000); + expect(ready.value).toBe(true); + }); + + it("should cancel function call when call cancel function", async () => { + let count = 0; + const { ready, cancel } = useTimeout(() => { + count++; + }, 1000); + + expect(ready.value).toBe(false); + expect(count).toBe(0); + + cancel(); + + await sleep(1000); + expect(ready.value).toBe(null); + expect(count).toBe(0); + }); + + it("should cancel on unMounted", async () => { + let ready: Ref = ref(false); + + const { mount, destroy } = createVue({ + template: `
`, + setup() { + ready = useTimeout(() => {}, 1000).ready; + }, + }); + + mount(); + + expect(ready.value).toBe(false); + await sleep(1000); + expect(ready.value).toBe(true); + + destroy(); + expect(ready.value).toBe(null); + }); +}); diff --git a/packages/vue-composable/src/web/index.ts b/packages/vue-composable/src/web/index.ts index 7147045aa..cf304be7c 100644 --- a/packages/vue-composable/src/web/index.ts +++ b/packages/vue-composable/src/web/index.ts @@ -11,4 +11,5 @@ export * from "./cssVariables"; export * from "./worker"; export * from "./share"; export * from "./clipboard"; +export * from "./timeout"; export { useWorkerFunction, WebWorkerFunctionOptions } from "./workerFunction"; diff --git a/packages/vue-composable/src/web/timeout.ts b/packages/vue-composable/src/web/timeout.ts new file mode 100644 index 000000000..358f337b2 --- /dev/null +++ b/packages/vue-composable/src/web/timeout.ts @@ -0,0 +1,43 @@ +import { ref, Ref, onUnmounted } from "../api"; + +interface UseTimeoutReturn { + /** + * current timeout state: + * false - pending + * true - called + * null - canceled + */ + ready: Ref; + /** + * cancel the timeout + */ + cancel: () => void; +} +/** + * @param fn setTimeout callback + * @param delay If this parameter is omitted, a value of 0 is used + * (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) + */ +export function useTimeout( + fn: () => void, + delay: number = 0 +): UseTimeoutReturn { + let ready: Ref = ref(false); + + const timeoutId = setTimeout(() => { + ready.value = true; + fn(); + }, delay); + + const cancel = () => { + ready.value = null; + clearTimeout(timeoutId); + }; + + onUnmounted(cancel); + + return { + ready, + cancel, + }; +} From 0636f68f6584357014d329ca4501f635da1acaf8 Mon Sep 17 00:00:00 2001 From: pikax Date: Sat, 23 Jan 2021 08:34:10 +0000 Subject: [PATCH 2/2] chore: update tests to use fake timers --- .../__tests__/web/timeout.spec.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/vue-composable/__tests__/web/timeout.spec.ts b/packages/vue-composable/__tests__/web/timeout.spec.ts index d7a605212..7ea1dac93 100644 --- a/packages/vue-composable/__tests__/web/timeout.spec.ts +++ b/packages/vue-composable/__tests__/web/timeout.spec.ts @@ -2,15 +2,8 @@ import { useTimeout } from "../../src"; import { createVue } from "../utils"; import { Ref, ref } from "../../src/api"; -function sleep(duration: number): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(true); - }, duration); - }); -} - describe("timeout", () => { + jest.useFakeTimers(); it("should be defined", () => { expect(useTimeout).toBeDefined(); }); @@ -22,7 +15,11 @@ describe("timeout", () => { }, 1000); expect(count).toBe(0); - await sleep(1000); + jest.advanceTimersByTime(900); + // should not be resolved + expect(count).toBe(0); + + jest.advanceTimersByTime(100); expect(count).toBe(1); }); @@ -30,7 +27,7 @@ describe("timeout", () => { const { ready } = useTimeout(() => {}, 1000); expect(ready.value).toBe(false); - await sleep(1000); + jest.advanceTimersByTime(1000); expect(ready.value).toBe(true); }); @@ -45,7 +42,7 @@ describe("timeout", () => { cancel(); - await sleep(1000); + jest.advanceTimersByTime(1000); expect(ready.value).toBe(null); expect(count).toBe(0); }); @@ -63,10 +60,21 @@ describe("timeout", () => { mount(); expect(ready.value).toBe(false); - await sleep(1000); - expect(ready.value).toBe(true); + jest.advanceTimersByTime(500); + expect(ready.value).toBe(false); destroy(); expect(ready.value).toBe(null); }); + + it("should default the delay to 0", () => { + let count = 0; + useTimeout(() => { + count++; + }); + + expect(count).toBe(0); + jest.runOnlyPendingTimers(); + expect(count).toBe(1); + }); });