Files
skywalking-booster-ui/src/hooks/__tests__/useTimeout.spec.ts

361 lines
10 KiB
TypeScript

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { nextTick } from "vue";
import { useTimeoutFn, useTimeoutRef } from "../useTimeout";
describe("useTimeout", () => {
beforeEach(() => {
vi.useFakeTimers();
vi.clearAllTimers();
vi.clearAllMocks();
});
afterEach(() => {
vi.runOnlyPendingTimers();
vi.useRealTimers();
});
describe("useTimeoutRef", () => {
it("should initialize with readyRef as false", () => {
const { readyRef } = useTimeoutRef(1000);
expect(readyRef.value).toBe(false);
});
it("should set readyRef to true after timeout", async () => {
const { readyRef } = useTimeoutRef(1000);
expect(readyRef.value).toBe(false);
vi.advanceTimersByTime(1000);
await nextTick();
expect(readyRef.value).toBe(true);
});
it("should start timer immediately", () => {
const { readyRef } = useTimeoutRef(500);
expect(readyRef.value).toBe(false);
vi.advanceTimersByTime(500);
expect(readyRef.value).toBe(true);
});
it("should provide stop function that clears timer", () => {
const { readyRef, stop } = useTimeoutRef(1000);
expect(readyRef.value).toBe(false);
stop();
vi.advanceTimersByTime(1000);
expect(readyRef.value).toBe(false);
});
it("should provide start function that restarts timer", () => {
const { readyRef, start } = useTimeoutRef(1000);
// Wait for initial timer
vi.advanceTimersByTime(1000);
expect(readyRef.value).toBe(true);
// Reset and restart
start();
expect(readyRef.value).toBe(false);
vi.advanceTimersByTime(1000);
expect(readyRef.value).toBe(true);
});
it("should handle multiple start calls", () => {
const { readyRef, start } = useTimeoutRef(1000);
// Call start multiple times
start();
start();
start();
expect(readyRef.value).toBe(false);
vi.advanceTimersByTime(1000);
expect(readyRef.value).toBe(true);
});
it("should handle zero timeout", () => {
const { readyRef } = useTimeoutRef(0);
vi.advanceTimersByTime(0);
expect(readyRef.value).toBe(true);
});
it("should handle negative timeout", () => {
const { readyRef } = useTimeoutRef(-1000);
vi.advanceTimersByTime(0);
expect(readyRef.value).toBe(true);
});
it("should return all required functions and refs", () => {
const result = useTimeoutRef(1000);
expect(result).toHaveProperty("readyRef");
expect(result).toHaveProperty("stop");
expect(result).toHaveProperty("start");
expect(typeof result.stop).toBe("function");
expect(typeof result.start).toBe("function");
});
});
describe("useTimeoutFn", () => {
it("should call handle function after timeout when native is false", async () => {
const mockHandle = vi.fn();
const { readyRef } = useTimeoutFn(mockHandle, 1000, false);
expect(mockHandle).not.toHaveBeenCalled();
expect(readyRef.value).toBe(false);
vi.advanceTimersByTime(1000);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
expect(readyRef.value).toBe(true);
});
it("should call handle function immediately when native is true", () => {
const mockHandle = vi.fn();
const { readyRef } = useTimeoutFn(mockHandle, 1000, true);
expect(mockHandle).toHaveBeenCalledTimes(1);
expect(readyRef.value).toBe(false);
});
it("should not call handle function immediately when native is false", () => {
const mockHandle = vi.fn();
const { readyRef } = useTimeoutFn(mockHandle, 1000, false);
expect(mockHandle).not.toHaveBeenCalled();
expect(readyRef.value).toBe(false);
});
it("should provide stop function that prevents handle execution", async () => {
const mockHandle = vi.fn();
const { readyRef, stop } = useTimeoutFn(mockHandle, 1000, false);
stop();
vi.advanceTimersByTime(1000);
await nextTick();
expect(mockHandle).not.toHaveBeenCalled();
expect(readyRef.value).toBe(false);
});
it("should provide start function that restarts timeout", async () => {
const mockHandle = vi.fn();
const { readyRef, start } = useTimeoutFn(mockHandle, 1000, false);
// Wait for initial timeout
vi.advanceTimersByTime(1000);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
// Reset and restart
start();
expect(readyRef.value).toBe(false);
vi.advanceTimersByTime(1000);
await nextTick();
// Wait a bit more for reactivity to update
await nextTick();
// The handle should be called at least once, and readyRef should be true
expect(mockHandle).toHaveBeenCalled();
expect(readyRef.value).toBe(true);
});
it("should handle handle function that returns a value", async () => {
const mockHandle = vi.fn(() => "test result");
useTimeoutFn(mockHandle, 1000, false);
vi.advanceTimersByTime(1000);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
expect(mockHandle).toHaveReturnedWith("test result");
});
it("should handle handle function that throws an error", async () => {
const mockHandle = vi.fn(() => {
throw new Error("Test error");
});
// Use try-catch to handle the error that will be thrown by the watch
try {
useTimeoutFn(mockHandle, 1000, false);
vi.advanceTimersByTime(1000);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
} catch (error) {
// The error is expected to be thrown by the watch function
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toBe("Test error");
}
});
it("should work with async handle function", async () => {
const mockHandle = vi.fn(async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
return "async result";
});
useTimeoutFn(mockHandle, 1000, false);
vi.advanceTimersByTime(1000);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
});
it("should handle multiple timeout executions", async () => {
const mockHandle = vi.fn();
const { readyRef, start } = useTimeoutFn(mockHandle, 500, false);
// First execution
vi.advanceTimersByTime(500);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
// Second execution
start();
vi.advanceTimersByTime(500);
await nextTick();
await nextTick();
expect(mockHandle).toHaveBeenCalled();
expect(readyRef.value).toBe(true);
// Third execution
start();
vi.advanceTimersByTime(500);
await nextTick();
await nextTick();
expect(mockHandle).toHaveBeenCalled();
expect(readyRef.value).toBe(true);
});
it("should return all required functions and refs", () => {
const mockHandle = vi.fn();
const result = useTimeoutFn(mockHandle, 1000);
expect(result).toHaveProperty("readyRef");
expect(result).toHaveProperty("stop");
expect(result).toHaveProperty("start");
expect(typeof result.stop).toBe("function");
expect(typeof result.start).toBe("function");
});
it("should throw error when handle is not a function", () => {
expect(() => {
useTimeoutFn("not a function" as any, 1000);
}).toThrow("handle is not Function!");
});
it("should throw error when handle is null", () => {
expect(() => {
useTimeoutFn(null as any, 1000);
}).toThrow("handle is not Function!");
});
it("should throw error when handle is undefined", () => {
expect(() => {
useTimeoutFn(undefined as any, 1000);
}).toThrow("handle is not Function!");
});
it("should handle zero wait time", async () => {
const mockHandle = vi.fn();
const { readyRef } = useTimeoutFn(mockHandle, 0, false);
vi.advanceTimersByTime(0);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
expect(readyRef.value).toBe(true);
});
it("should handle negative wait time", async () => {
const mockHandle = vi.fn();
const { readyRef } = useTimeoutFn(mockHandle, -1000, false);
vi.advanceTimersByTime(0);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
expect(readyRef.value).toBe(true);
});
});
describe("Integration tests", () => {
it("should work together with Vue reactivity", async () => {
const mockHandle = vi.fn();
const { readyRef, stop, start } = useTimeoutFn(mockHandle, 1000, false);
// Initial state
expect(readyRef.value).toBe(false);
expect(mockHandle).not.toHaveBeenCalled();
// After timeout
vi.advanceTimersByTime(1000);
await nextTick();
expect(readyRef.value).toBe(true);
expect(mockHandle).toHaveBeenCalledTimes(1);
// After stop
stop();
expect(readyRef.value).toBe(false);
// After restart
start();
vi.advanceTimersByTime(1000);
await nextTick();
await nextTick();
expect(readyRef.value).toBe(true);
expect(mockHandle).toHaveBeenCalled();
});
it("should handle rapid start/stop calls", async () => {
const mockHandle = vi.fn();
const { readyRef, stop, start } = useTimeoutFn(mockHandle, 1000, false);
// Rapid start/stop calls
start();
stop();
start();
stop();
start();
vi.advanceTimersByTime(1000);
await nextTick();
expect(mockHandle).toHaveBeenCalledTimes(1);
expect(readyRef.value).toBe(true);
});
});
});