artemis-kit/src/promise/index.test.ts

224 lines
5.5 KiB
TypeScript

import { describe, it, expect, afterEach, vi, beforeEach } from "vitest";
import { WaitUntil, debounce, throttle, measureTime } from "./index";
describe("WaitUntil", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should resolve when condition becomes true", async () => {
let flag = false;
setTimeout(() => {
flag = true;
}, 1000);
const promise = WaitUntil(() => flag);
await vi.advanceTimersByTimeAsync(1000);
await promise; // Should resolve without throwing
});
it("should reject on timeout", async () => {
let rejected = false;
const promise = WaitUntil(() => false).catch((err) => {
expect(err.message).toBe("Wait Timeout");
rejected = true;
});
await vi.advanceTimersByTimeAsync(5000);
await promise;
expect(rejected).toBe(true);
});
it("should respect custom timeout", async () => {
let rejected = false;
const promise = WaitUntil(() => false, { timeout: 2000 }).catch((err) => {
expect(err.message).toBe("Wait Timeout");
rejected = true;
});
await vi.advanceTimersByTimeAsync(2000);
await promise;
expect(rejected).toBe(true);
});
it("should check condition at specified interval", async () => {
const mockCondition = vi.fn(() => false);
let rejected = false;
const promise = WaitUntil(mockCondition, { interval: 200 }).catch((err) => {
expect(err.message).toBe("Wait Timeout");
rejected = true;
});
await vi.advanceTimersByTimeAsync(1000);
expect(mockCondition).toHaveBeenCalledTimes(5); // 1000ms / 200ms = 5 calls
await vi.advanceTimersByTimeAsync(4000);
await promise;
expect(rejected).toBe(true);
});
});
describe("debounce", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should delay function execution", () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 1000);
debouncedFn();
expect(mockFn).not.toBeCalled();
vi.advanceTimersByTime(1000);
expect(mockFn).toHaveBeenCalledTimes(1);
});
it("should only execute once for multiple calls within wait period", () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 1000);
debouncedFn();
debouncedFn();
debouncedFn();
vi.advanceTimersByTime(1000);
expect(mockFn).toHaveBeenCalledTimes(1);
});
it("should pass correct arguments to the debounced function", () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 1000);
debouncedFn("test", 123);
vi.advanceTimersByTime(1000);
expect(mockFn).toHaveBeenCalledWith("test", 123);
});
it("should reset timer on subsequent calls", () => {
const mockFn = vi.fn();
const debouncedFn = debounce(mockFn, 1000);
debouncedFn();
vi.advanceTimersByTime(500);
debouncedFn();
vi.advanceTimersByTime(500);
expect(mockFn).not.toBeCalled();
vi.advanceTimersByTime(500);
expect(mockFn).toHaveBeenCalledTimes(1);
});
});
describe("throttle", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should execute immediately on first call", () => {
const mockFn = vi.fn();
const throttledFn = throttle(mockFn, 1000);
throttledFn();
expect(mockFn).toHaveBeenCalledTimes(1);
});
it("should ignore calls within throttle period", () => {
const mockFn = vi.fn();
const throttledFn = throttle(mockFn, 1000);
throttledFn();
throttledFn();
throttledFn();
expect(mockFn).toHaveBeenCalledTimes(1);
});
it("should allow execution after throttle period", () => {
const mockFn = vi.fn();
const throttledFn = throttle(mockFn, 1000);
throttledFn();
expect(mockFn).toHaveBeenCalledTimes(1);
vi.advanceTimersByTime(1000);
throttledFn();
expect(mockFn).toHaveBeenCalledTimes(2);
});
it("should pass correct arguments to the throttled function", () => {
const mockFn = vi.fn();
const throttledFn = throttle(mockFn, 1000);
throttledFn("test", 123);
expect(mockFn).toHaveBeenCalledWith("test", 123);
});
});
describe("measureTime", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should measure execution time of async function", async () => {
const delay = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));
const fn = async () => {
await delay(100);
return "result";
};
const measurePromise = measureTime(fn);
await vi.advanceTimersByTimeAsync(100);
const { result, duration } = await measurePromise;
expect(result).toBe("result");
expect(duration).toBeGreaterThan(0);
});
it("should handle rejected promises", async () => {
const fn = async () => {
throw new Error("Test error");
};
await expect(measureTime(fn)).rejects.toThrow("Test error");
});
it("should return precise timing for fast operations", async () => {
const fn = async () => "quick result";
const { duration } = await measureTime(fn);
expect(duration).toBeLessThan(100); // Should be nearly instant
});
it("should handle functions returning different types", async () => {
const numberFn = async () => 42;
const { result: numResult } = await measureTime(numberFn);
expect(numResult).toBe(42);
const objectFn = async () => ({ key: "value" });
const { result: objResult } = await measureTime(objectFn);
expect(objResult).toEqual({ key: "value" });
});
});