mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-26 01:44:53 +10:00
6a01207b6b
Adds ~1000 tests to bring the previously-untested packages and apps under coverage: - packages/utils — string, color, date, file, level, locale, sanitize, field, html, network-icons, rate-limit, url, url-security, monorepo, resume/patch, resume/docx/link-utils, style helpers (~97% on the testable utility files) - packages/ui — 28 component test files plus the use-controlled-state, use-mobile, use-confirm, use-prompt hooks (95% statements) - packages/pdf — shared template helpers (filtering, columns, picture, metrics, section-links, page-size, rich-text-html, section-title) - packages/schema — resumeDataSchema, page, templates, default - packages/fonts — expanded coverage on font helpers - packages/ai — resume sanitize and extraction template - packages/api — resume-access-policy - apps/web — error-message, locale, theme, pwa, dialogs/store, and the resume/move-item / section-actions / make-section-item helpers Adds jsdom polyfills (ResizeObserver, IntersectionObserver, scrollIntoView, matchMedia) and an explicit React Testing Library cleanup hook to vitest.setup.ts so portal- and overlay-based components work without per-test setup.
79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
import { act, renderHook } from "@testing-library/react";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { useControlledState } from "./use-controlled-state";
|
|
|
|
describe("useControlledState", () => {
|
|
it("returns the defaultValue when uncontrolled", () => {
|
|
const { result } = renderHook(() => useControlledState({ defaultValue: 0 }));
|
|
expect(result.current[0]).toBe(0);
|
|
});
|
|
|
|
it("returns the value when controlled", () => {
|
|
const { result } = renderHook(() => useControlledState({ value: 42, defaultValue: 0 }));
|
|
expect(result.current[0]).toBe(42);
|
|
});
|
|
|
|
it("updates internal state when value prop changes (controlled)", () => {
|
|
const { result, rerender } = renderHook(({ value }: { value: number }) => useControlledState({ value }), {
|
|
initialProps: { value: 1 },
|
|
});
|
|
|
|
expect(result.current[0]).toBe(1);
|
|
rerender({ value: 2 });
|
|
expect(result.current[0]).toBe(2);
|
|
});
|
|
|
|
it("updates state via setter when uncontrolled", () => {
|
|
const { result } = renderHook(() => useControlledState<number>({ defaultValue: 0 }));
|
|
act(() => {
|
|
result.current[1](10);
|
|
});
|
|
expect(result.current[0]).toBe(10);
|
|
});
|
|
|
|
it("calls onChange when state is updated", () => {
|
|
const onChange = vi.fn();
|
|
const { result } = renderHook(() => useControlledState<string>({ defaultValue: "a", onChange }));
|
|
|
|
act(() => {
|
|
result.current[1]("b");
|
|
});
|
|
|
|
expect(onChange).toHaveBeenCalledWith("b");
|
|
});
|
|
|
|
it("forwards extra args to onChange", () => {
|
|
const onChange = vi.fn();
|
|
const { result } = renderHook(() => useControlledState<string, [number, boolean]>({ defaultValue: "a", onChange }));
|
|
|
|
act(() => {
|
|
result.current[1]("b", 42, true);
|
|
});
|
|
|
|
expect(onChange).toHaveBeenCalledWith("b", 42, true);
|
|
});
|
|
|
|
it("does not call onChange when value prop changes from outside", () => {
|
|
const onChange = vi.fn();
|
|
const { rerender } = renderHook(({ value }: { value: number }) => useControlledState<number>({ value, onChange }), {
|
|
initialProps: { value: 1 },
|
|
});
|
|
|
|
rerender({ value: 2 });
|
|
// onChange is only called via setState, not the controlled-value sync effect.
|
|
expect(onChange).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("returns the same setter reference when onChange is stable", () => {
|
|
const onChange = vi.fn();
|
|
const { result, rerender } = renderHook(
|
|
({ value }: { value: number }) => useControlledState<number>({ value, onChange }),
|
|
{ initialProps: { value: 1 } },
|
|
);
|
|
|
|
const initialSetter = result.current[1];
|
|
rerender({ value: 2 });
|
|
expect(result.current[1]).toBe(initialSetter);
|
|
});
|
|
});
|