import { fireEvent, render, screen } from "@testing-library/react"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { KEYBOARD_RESIZE_STEP, ResizeHandle, widthAfterKey, } from "./ResizeHandle"; // jsdom has no PointerEvent; a MouseEvent carrying a pointerId is enough for // React's pointer handlers and gives the tests real clientX values. beforeAll(() => { if (typeof window.PointerEvent === "undefined") { class PointerEventPolyfill extends MouseEvent { readonly pointerId: number; constructor(type: string, init: PointerEventInit = {}) { super(type, init); this.pointerId = init.pointerId ?? 0; } } Object.defineProperty(window, "PointerEvent", { value: PointerEventPolyfill, configurable: true, }); } }); const onResize = vi.fn(); const onReset = vi.fn(); function renderHandle(width = 200) { render( , ); return screen.getByRole("separator", { name: "Resize the file tree" }); } describe("ResizeHandle", () => { beforeEach(() => { onResize.mockReset(); onReset.mockReset(); }); it("exposes the width through the separator pattern", () => { const handle = renderHandle(300); expect(handle.getAttribute("aria-orientation")).toBe("vertical"); expect(handle.getAttribute("aria-valuenow")).toBe("300"); expect(handle.getAttribute("aria-valuemin")).toBe("120"); expect(handle.getAttribute("aria-valuemax")).toBe("800"); }); it("reports the width while dragging and stops on pointer up", () => { const handle = renderHandle(200); fireEvent.pointerDown(handle, { clientX: 500, pointerId: 1 }); expect(handle.classList.contains("resize-handle-active")).toBe(true); fireEvent.pointerMove(handle, { clientX: 540, pointerId: 1 }); fireEvent.pointerMove(handle, { clientX: 470, pointerId: 1 }); expect(onResize.mock.calls).toEqual([[240], [170]]); fireEvent.pointerUp(handle, { clientX: 470, pointerId: 1 }); expect(handle.classList.contains("resize-handle-active")).toBe(false); fireEvent.pointerMove(handle, { clientX: 600, pointerId: 1 }); expect(onResize).toHaveBeenCalledTimes(2); }); it("ignores pointer moves that did not start on the handle", () => { const handle = renderHandle(200); fireEvent.pointerMove(handle, { clientX: 600, pointerId: 1 }); expect(onResize).not.toHaveBeenCalled(); }); it("resizes step by step with the arrow keys and jumps with Home / End", () => { const handle = renderHandle(200); fireEvent.keyDown(handle, { key: "ArrowRight" }); fireEvent.keyDown(handle, { key: "ArrowLeft" }); fireEvent.keyDown(handle, { key: "Home" }); fireEvent.keyDown(handle, { key: "End" }); expect(onResize.mock.calls).toEqual([ [200 + KEYBOARD_RESIZE_STEP], [200 - KEYBOARD_RESIZE_STEP], [120], [800], ]); }); it("leaves other keys alone", () => { const handle = renderHandle(200); fireEvent.keyDown(handle, { key: "Enter" }); fireEvent.keyDown(handle, { key: "Tab" }); expect(onResize).not.toHaveBeenCalled(); }); it("resets on double-click", () => { const handle = renderHandle(200); fireEvent.doubleClick(handle); expect(onReset).toHaveBeenCalledTimes(1); }); }); describe("widthAfterKey", () => { it("maps the resize keys and returns null otherwise", () => { expect(widthAfterKey("ArrowRight", 200, 120, 800)).toBe(216); expect(widthAfterKey("ArrowLeft", 200, 120, 800)).toBe(184); expect(widthAfterKey("Home", 200, 120, 800)).toBe(120); expect(widthAfterKey("End", 200, 120, 800)).toBe(800); expect(widthAfterKey("a", 200, 120, 800)).toBeNull(); }); });