Files
portifolio-evandrodevbr/app/components/story/NotebookBrowser.test.tsx
T

165 lines
5.4 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { portfolio } from "@/lib/portfolio-data";
import NotebookBrowser, { type NotebookBrowserProps } from "./NotebookBrowser";
import { selectNotebookProjects } from "./notebookProjects";
const projects = selectNotebookProjects(portfolio.projects);
const frame: NotebookBrowserProps["frame"] = {
cursorVisible: true,
cursorX: 0.4,
cursorY: 0.6,
cursorClicking: false,
};
function renderBrowser(overrides: Partial<NotebookBrowserProps> = {}) {
return render(
<NotebookBrowser
projects={projects}
activeIndex={0}
frame={frame}
onSelectTab={jest.fn()}
{...overrides}
/>,
);
}
describe("NotebookBrowser", () => {
it("renders the active project as an accessible tabpanel", () => {
renderBrowser({ activeIndex: 1 });
expect(
screen.getByRole("tablist", { name: /projetos em produção/i }),
).toBeInTheDocument();
expect(screen.getAllByRole("tab")[1]).toHaveAttribute(
"aria-selected",
"true",
);
expect(screen.getByRole("tabpanel")).toHaveAccessibleName(
/SC Plus Aquicultura/i,
);
expect(screen.getByRole("status")).toHaveTextContent(
"Projeto ativo: SC Plus Aquicultura",
);
});
it("preloads the next screenshot and keeps a short outgoing crossfade", () => {
const view = renderBrowser();
expect(
view.container.querySelector(".notebook-screenshot-preload"),
).toBeInTheDocument();
view.rerender(
<NotebookBrowser
projects={projects}
activeIndex={1}
frame={frame}
onSelectTab={jest.fn()}
/>,
);
expect(
view.container.querySelector(".notebook-screenshot-outgoing"),
).toBeInTheDocument();
});
it("moves between tabs with arrow keys and Home/End", async () => {
const user = userEvent.setup();
const onSelectTab = jest.fn();
renderBrowser({ onSelectTab });
const tabs = screen.getAllByRole("tab");
await user.click(tabs[1]);
await user.keyboard("{ArrowRight}");
await user.keyboard("{Home}");
await user.keyboard("{End}");
expect(onSelectTab).toHaveBeenNthCalledWith(1, 1);
expect(onSelectTab).toHaveBeenNthCalledWith(2, 2);
expect(onSelectTab).toHaveBeenNthCalledWith(3, 0);
expect(onSelectTab).toHaveBeenNthCalledWith(4, projects.length - 1);
});
it("moves DOM focus with the active tab", async () => {
const user = userEvent.setup();
const tabs =
renderBrowser().container.querySelectorAll<HTMLButtonElement>(
'[role="tab"]',
);
tabs[0].focus();
await user.keyboard("{ArrowRight}");
expect(document.activeElement).toBe(tabs[1]);
});
it("removes the closed browser from the accessibility tree", () => {
renderBrowser({ interactive: false });
const browser = screen.getByTestId("notebook-browser");
expect(browser).toHaveAttribute("aria-hidden", "true");
expect(browser).toHaveAttribute("inert");
});
it("bounds arrow navigation at the first and last tab", async () => {
const user = userEvent.setup();
const onSelectTab = jest.fn();
renderBrowser({ onSelectTab });
const tabs = screen.getAllByRole("tab");
await user.click(tabs[0]);
await user.keyboard("{ArrowLeft}");
await user.click(tabs.at(-1) as HTMLElement);
await user.keyboard("{ArrowRight}");
expect(onSelectTab).toHaveBeenNthCalledWith(2, 0);
expect(onSelectTab).toHaveBeenNthCalledWith(4, projects.length - 1);
});
it("exposes a real deployment link with safe new-tab attributes", () => {
renderBrowser();
const link = screen.getByRole("link", { name: /abrir projeto/i });
expect(link).toHaveAttribute("href", projects[0].demoUrl);
expect(link).toHaveAttribute("target", "_blank");
expect(link).toHaveAttribute("rel", "noopener noreferrer");
});
it("keeps the animated cursor decorative and never embeds an iframe", () => {
const { container } = renderBrowser({
frame: { ...frame, cursorClicking: true },
});
const cursor = container.querySelector(".notebook-cursor");
expect(cursor).toHaveAttribute("aria-hidden", "true");
expect(cursor).toHaveStyle({ left: "40%", top: "60%" });
expect(cursor?.closest(".notebook-browser")).toBe(
container.querySelector(".notebook-browser"),
);
expect(container.querySelector("iframe")).not.toBeInTheDocument();
});
it("marks only the selected tab as pressed by the choreographed cursor", () => {
const { container } = renderBrowser({
activeIndex: 1,
frame: { ...frame, cursorClicking: true },
});
const tabs = container.querySelectorAll(".notebook-tab");
expect(tabs[1]).toHaveAttribute("data-cursor-clicking", "true");
expect(tabs[0]).toHaveAttribute("data-cursor-clicking", "false");
expect(tabs[2]).toHaveAttribute("data-cursor-clicking", "false");
expect(tabs[3]).toHaveAttribute("data-cursor-clicking", "false");
});
it("uses the selected tab to label the panel and screenshot", () => {
renderBrowser({ activeIndex: 2 });
const tab = screen.getAllByRole("tab")[2];
const panel = screen.getByRole("tabpanel");
const image = screen.getByRole("img");
expect(tab).toHaveAttribute("aria-controls", panel.id);
expect(panel).toHaveAttribute("aria-labelledby", tab.id);
expect(image).toHaveAccessibleName(/SEV Moda Íntima/i);
expect(image).toHaveAttribute("src", "/assets/prints/print-sev.png");
});
});