import { act, render, renderHook, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { type PropsWithChildren, useContext, useRef } from "react";
import { TabControlContext, TabsProvider } from "../context/TabControl";
import { Tab } from "../Tab.js";
import { useActiveTab, useRegisterTabControl, useTabControls, useTabDispatch } from "./tabControl";
interface Config {
selected: number;
}
const createWrapper = ({ selected }: Config = { selected: 3 }) => {
return ({ children }: PropsWithChildren) => {
return {children};
};
};
describe("tab control hooks", () => {
beforeAll(() => {
vi.spyOn(console, "warn").mockImplementation(() => {});
});
afterAll(() => {
vi.mocked(console.warn).mockReset();
});
describe("useActiveTab()", () => {
it("returns the current tab value", () => {
const { result } = renderHook(useActiveTab, { wrapper: createWrapper() });
expect(result.current).toBe(3);
});
});
describe("useRegisterTabControl()", () => {
it("returns the dispatch function", () => {
const { result } = renderHook(() => useRegisterTabControl({ value: 0, ref: useRef(null) }), {
wrapper: createWrapper()
});
expect(result.current).toEqual(expect.any(Function));
});
it("register and unregister", () => {
const { result, unmount } = renderHook(() => {
const div = document.createElement("div");
return useRegisterTabControl({ value: 0, ref: useRef(div) });
});
unmount();
expect(result.current).toEqual(expect.any(Function));
});
describe("without provider", () => {
it("log a warning", async () => {
const { result, unmount } = renderHook(() => {
const div = document.createElement("div");
return useRegisterTabControl({ value: 0, ref: useRef(div) });
});
await act(() => result.current({ type: "update", payload: 4 }));
unmount();
expect(console.warn).toHaveBeenCalledWith("Tab Controller Context dispatch used outside of Provider");
expect(console.warn).toHaveBeenCalledWith("Tab Controller Context register used outside of Provider");
expect(console.warn).toHaveBeenCalledWith("Tab Controller Context unregister used outside of Provider");
});
});
});
describe("useTabDispatch()", () => {
it("returns the dispatch function", () => {
const { result } = renderHook(useTabDispatch, {
wrapper: createWrapper()
});
expect(result.current).toEqual(expect.any(Function));
});
describe("without provider", () => {
it("log a warning", async () => {
const { result } = renderHook(useTabDispatch);
await act(() => result.current({ type: "update", payload: 4 }));
expect(console.warn).toHaveBeenCalledWith("Tab Controller Context dispatch used outside of Provider");
});
});
describe("dispatch update", () => {
it("update the state", async () => {
const Test = () => {
const dispatch = useTabDispatch();
const tab = useActiveTab();
const update = () => {
dispatch({ type: "update", payload: 4 });
};
return (
<>
>
);
};
render(, { wrapper: createWrapper() });
const button = screen.getByRole("button");
const output = screen.getByRole("status");
expect(output).toHaveValue("3");
await userEvent.click(button);
expect(output).toHaveValue("4");
});
});
describe("dispatch end", () => {
it("update the state", async () => {
const Test = () => {
const dispatch = useTabDispatch();
const tab = useActiveTab();
const end = () => {
dispatch({ type: "end" });
};
return (
<>
Label 0
Label 3
Label 5
>
);
};
render(, { wrapper: createWrapper() });
const button = screen.getByRole("button");
const output = screen.getByRole("status");
expect(output).toHaveValue("3");
await userEvent.click(button);
expect(output).toHaveValue("5");
});
});
describe("dispatch start", () => {
it("update the state", async () => {
const Test = () => {
const dispatch = useTabDispatch();
const tab = useActiveTab();
const start = () => {
dispatch({ type: "start" });
};
return (
<>
Label 0
Label 3
Label 5
>
);
};
render(, { wrapper: createWrapper() });
const button = screen.getByRole("button");
const output = screen.getByRole("status");
expect(output).toHaveValue("3");
await userEvent.click(button);
expect(output).toHaveValue("0");
});
});
describe("dispatch next", () => {
it("update the state", async () => {
const Test = () => {
const dispatch = useTabDispatch();
const tab = useActiveTab();
const next = () => {
dispatch({ type: "next" });
};
return (
<>
Label 0
Label 3
Label 4
Label 5
>
);
};
render(, { wrapper: createWrapper() });
const button = screen.getByRole("button");
const output = screen.getByRole("status");
expect(output).toHaveValue("3");
await userEvent.click(button);
expect(output).toHaveValue("4");
});
describe("when current tab is last one", () => {
it("loop to the first tab", async () => {
const Test = () => {
const dispatch = useTabDispatch();
const tab = useActiveTab();
const next = () => {
dispatch({ type: "next" });
};
return (
<>
Label 0
Label 3
Label 4
Label 5
>
);
};
render(, { wrapper: createWrapper({ selected: 5 }) });
const button = screen.getByRole("button");
const output = screen.getByRole("status");
expect(output).toHaveValue("5");
await userEvent.click(button);
expect(output).toHaveValue("0");
});
});
});
describe("dispatch previous", () => {
it("update the state", async () => {
const Test = () => {
const dispatch = useTabDispatch();
const tab = useActiveTab();
const previous = () => {
dispatch({ type: "previous" });
};
return (
<>
Label 0
Label 2
Label 3
Label 4
Label 5
>
);
};
render(, { wrapper: createWrapper() });
const button = screen.getByRole("button");
const output = screen.getByRole("status");
expect(output).toHaveValue("3");
await userEvent.click(button);
expect(output).toHaveValue("2");
});
describe("when current tab is first one", () => {
it("loop to the last tab", async () => {
const Test = () => {
const dispatch = useTabDispatch();
const tab = useActiveTab();
const previous = () => {
dispatch({ type: "previous" });
};
return (
<>
Label 0
Label 2
Label 3
Label 4
Label 5
>
);
};
render(, { wrapper: createWrapper({ selected: 0 }) });
const button = screen.getByRole("button");
const output = screen.getByRole("status");
expect(output).toHaveValue("0");
await userEvent.click(button);
expect(output).toHaveValue("5");
});
});
});
});
describe("useTabControl()", () => {
it("retuns the elements registered map", () => {
const Test = () => {
const div = document.createElement("div");
const ref = useRef(div);
useRegisterTabControl({ value: 0, ref });
const controls = useTabControls();
return ;
};
render(, { wrapper: createWrapper() });
const output = screen.getByRole("status");
expect(output).toHaveValue("1");
});
it("allow unregister", async () => {
const Test = () => {
const div = document.createElement("div");
const ref = useRef(div);
const { register, unregister } = useContext(TabControlContext);
const controls = useTabControls();
return (
<>
>
);
};
render(, { wrapper: createWrapper() });
const register = screen.getByRole("button", { name: "REGISTER" });
const unregister = screen.getByRole("button", { name: "UNREGISTER" });
await userEvent.click(register);
expect(screen.getByRole("status")).toHaveValue("1");
await userEvent.click(unregister);
expect(screen.getByRole("status")).toHaveValue("0");
});
});
});