/* eslint-disable jsx-a11y/no-autofocus */ import * as React from "react"; import {act, fireEvent, render} from "@testing-library/react"; import {Time, ZonedDateTime} from "@internationalized/date"; import {TimeValue} from "@react-types/datepicker"; import {pointerMap, triggerPress} from "@nextui-org/test-utils"; import userEvent from "@testing-library/user-event"; import {TimeInput as TimeInputBase, TimeInputProps} from "../src"; /** * Custom date-input to disable animations and avoid issues with react-motion and jest */ const TimeInput = React.forwardRef((props: TimeInputProps, ref: React.Ref) => { return ; }); TimeInput.displayName = "TimeInput"; describe("TimeInput", () => { let user; beforeAll(() => { user = userEvent.setup({delay: null, pointerMap}); jest.useFakeTimers(); }); describe("Basics", () => { it("should render correctly", () => { const wrapper = render(); expect(() => wrapper.unmount()).not.toThrow(); }); it("ref should be forwarded", () => { const ref = React.createRef(); render(); expect(ref.current).not.toBeNull(); }); it("should support autoFocus", function () { let {getAllByRole} = render(); expect(document.activeElement).toBe(getAllByRole("spinbutton")[0]); }); it("should pass through data attributes", function () { let {getByTestId} = render(); const input = getByTestId("foo"); expect(input).toHaveAttribute("role", "group"); }); it("should include a selected value description", function () { let {getByRole, getAllByRole} = render(); let group = getByRole("group"); expect(group).toHaveAttribute("aria-describedby"); // @ts-ignore let description = group .getAttribute("aria-describedby") .split(" ") // @ts-ignore .map((d) => document.getElementById(d).textContent) .join(" "); expect(description).toBe("Selected Time: 8:45 AM"); let segments = getAllByRole("spinbutton"); expect(segments[0]).toHaveAttribute( "aria-describedby", group.getAttribute("aria-describedby"), ); for (let segment of segments.slice(1)) { expect(segment).not.toHaveAttribute("aria-describedby"); } }); }); describe("Labelling", () => { it("should support labeling", function () { let {getAllByRole, getByText} = render(); let label = getByText("Time"); let combobox = getAllByRole("group")[0]; expect(combobox).toHaveAttribute("aria-labelledby", label.id); let segments = getAllByRole("spinbutton"); for (let segment of segments) { expect(segment).toHaveAttribute("id"); let segmentId = segment.getAttribute("id"); expect(segment).toHaveAttribute("aria-labelledby", `${segmentId} ${label.id}`); } }); it("should support labeling with aria-label", function () { let {getByRole} = render(); let field = getByRole("group"); expect(field).toHaveAttribute("aria-label", "Event time"); expect(field).toHaveAttribute("id"); }); it("should support labeling with aria-labelledby", function () { let {getByRole, getAllByRole} = render(); let combobox = getByRole("group"); expect(combobox).not.toHaveAttribute("aria-label"); expect(combobox).toHaveAttribute("aria-labelledby", "foo"); let segments = getAllByRole("spinbutton"); for (let segment of segments) { expect(segment).toHaveAttribute("id"); let segmentId = segment.getAttribute("id"); expect(segment).toHaveAttribute("aria-labelledby", `${segmentId} foo`); } }); it("should support help text description", function () { let {getByRole, getAllByRole} = render(); let group = getByRole("group"); expect(group).toHaveAttribute("aria-describedby"); const descById = group.getAttribute("aria-describedby"); let description = descById && document.getElementById(descById); expect(description).toHaveTextContent("Help text"); let segments = getAllByRole("spinbutton"); expect(segments[0]).toHaveAttribute( "aria-describedby", group.getAttribute("aria-describedby"), ); for (let segment of segments.slice(1)) { expect(segment).not.toHaveAttribute("aria-describedby"); } }); it("should support error message", function () { let {getByRole, getAllByRole} = render( , ); let group = getByRole("group"); expect(group).toHaveAttribute("aria-describedby"); if (group) { let descById = group.getAttribute("aria-describedby"); let description = descById && document.getElementById(descById); expect(description).toHaveTextContent("Error message"); let segments = getAllByRole("spinbutton"); for (let segment of segments) { expect(segment).toHaveAttribute( "aria-describedby", group.getAttribute("aria-describedby"), ); } } }); }); describe("Events", function () { let onBlurSpy = jest.fn(); let onFocusChangeSpy = jest.fn(); let onFocusSpy = jest.fn(); let onKeyDownSpy = jest.fn(); let onKeyUpSpy = jest.fn(); afterEach(() => { onBlurSpy.mockClear(); onFocusChangeSpy.mockClear(); onFocusSpy.mockClear(); onKeyDownSpy.mockClear(); onKeyUpSpy.mockClear(); }); it("should focus field and switching segments via tab does not change focus", async function () { let {getAllByRole} = render( , ); let segments = getAllByRole("spinbutton"); expect(onBlurSpy).not.toHaveBeenCalled(); expect(onFocusChangeSpy).not.toHaveBeenCalled(); expect(onFocusSpy).not.toHaveBeenCalled(); await act(async () => { await user.tab(); }); expect(segments[0]).toHaveFocus(); expect(onBlurSpy).not.toHaveBeenCalled(); expect(onFocusChangeSpy).toHaveBeenCalledTimes(1); expect(onFocusSpy).toHaveBeenCalledTimes(1); await act(async () => { await user.tab(); }); expect(segments[1]).toHaveFocus(); expect(onBlurSpy).not.toHaveBeenCalled(); expect(onFocusChangeSpy).toHaveBeenCalledTimes(1); expect(onFocusSpy).toHaveBeenCalledTimes(1); }); it("should call blur when focus leaves", async function () { let {getAllByRole} = render( , ); let segments = getAllByRole("spinbutton"); expect(onBlurSpy).not.toHaveBeenCalled(); expect(onFocusChangeSpy).not.toHaveBeenCalled(); expect(onFocusSpy).not.toHaveBeenCalled(); await act(async () => { await user.tab(); }); expect(segments[0]).toHaveFocus(); await act(async () => { await user.tab(); }); expect(segments[1]).toHaveFocus(); await act(async () => { await user.tab(); }); expect(segments[2]).toHaveFocus(); expect(onBlurSpy).toHaveBeenCalledTimes(0); await act(async () => { await user.tab(); }); expect(onBlurSpy).toHaveBeenCalledTimes(1); expect(onFocusChangeSpy).toHaveBeenCalledTimes(2); expect(onFocusSpy).toHaveBeenCalledTimes(1); }); it("should trigger right arrow key event for segment navigation", async function () { let {getAllByRole} = render( , ); let segments = getAllByRole("spinbutton"); expect(onKeyDownSpy).not.toHaveBeenCalled(); expect(onKeyUpSpy).not.toHaveBeenCalled(); await act(() => { user.tab(); }); expect(segments[0]).toHaveFocus(); expect(onKeyDownSpy).not.toHaveBeenCalled(); expect(onKeyUpSpy).toHaveBeenCalledTimes(1); if (document.activeElement) { fireEvent.keyDown(document.activeElement, {key: "ArrowRight"}); fireEvent.keyUp(document.activeElement, {key: "ArrowRight"}); } expect(segments[1]).toHaveFocus(); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); expect(onKeyUpSpy).toHaveBeenCalledTimes(2); }); }); describe("Forms", () => { it("supports form values", () => { let {rerender} = render(); let input = document.querySelector("input[name=time]"); expect(input).toHaveValue("08:30:00"); rerender(); expect(input).toHaveValue("12:24:45"); rerender( , ); expect(input).toHaveValue("12:24:45"); }); it("supports form reset", async () => { function Test() { let [value, setValue] = React.useState(new Time(8, 30)); return (
); } let {getByTestId, getByRole, getAllByRole} = render(); let group = getByRole("group"); let input = document.querySelector("input[name=time]"); let segments = getAllByRole("spinbutton"); let getDescription = () => // @ts-ignore group .getAttribute("aria-describedby") .split(" ") // @ts-ignore .map((d) => document.getElementById(d).textContent) .join(" "); expect(getDescription()).toBe("Selected Time: 8:30 AM"); expect(input).toHaveValue("08:30:00"); expect(input).toHaveAttribute("name", "time"); fireEvent.keyDown(segments[0], {key: "ArrowUp"}); fireEvent.keyUp(segments[0], {key: "ArrowUp"}); expect(getDescription()).toBe("Selected Time: 9:30 AM"); expect(input).toHaveValue("09:30:00"); let button = getByTestId("reset"); triggerPress(button); expect(getDescription()).toBe("Selected Time: 8:30 AM"); expect(input).toHaveValue("08:30:00"); }); }); });