Files
kandev/apps/web/components/task/chat/messages/inline-code.test.tsx
T

96 lines
3.0 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { act, cleanup, fireEvent, render, screen } from "@testing-library/react";
import { InlineCode } from "./inline-code";
const { copyMock } = vi.hoisted(() => ({
copyMock: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("@/hooks/use-copy-to-clipboard", () => ({
useCopyToClipboard: () => ({ copy: copyMock }),
}));
describe("InlineCode", () => {
beforeEach(() => {
copyMock.mockClear();
});
afterEach(() => {
cleanup();
});
it("renders the inline code content", () => {
render(<InlineCode>npm install</InlineCode>);
expect(screen.getByText("npm install")).toBeDefined();
});
it("portals the hover tooltip to document.body so overflow-hidden ancestors cannot clip it", () => {
// Mirrors the user-message bubble with nested overflow-hidden layers that previously clipped the tooltip.
const { container } = render(
<div className="overflow-hidden">
<div className="overflow-hidden">
<InlineCode>git status</InlineCode>
</div>
</div>,
);
fireEvent.mouseEnter(screen.getByText("git status"));
const tooltip = screen.getByRole("tooltip");
expect(tooltip.textContent).toBe("Copy to clipboard");
// Tooltip must escape the clipping wrapper: lives on document.body, not inside the container.
expect(container.contains(tooltip)).toBe(false);
expect(document.body.contains(tooltip)).toBe(true);
});
it("hides the tooltip when the pointer leaves", () => {
render(<InlineCode>ls</InlineCode>);
const code = screen.getByText("ls");
fireEvent.mouseEnter(code);
expect(screen.queryByRole("tooltip")).not.toBeNull();
fireEvent.mouseLeave(code);
expect(screen.queryByRole("tooltip")).toBeNull();
});
it("copies the content and shows a Copied! confirmation on click", async () => {
render(<InlineCode>echo hi</InlineCode>);
await act(async () => {
fireEvent.click(screen.getByText("echo hi"));
});
expect(copyMock).toHaveBeenCalledWith("echo hi");
expect(screen.getByRole("tooltip").textContent).toBe("Copied!");
});
it("wires the trigger to the tooltip via aria-describedby only while visible", () => {
render(<InlineCode>pwd</InlineCode>);
const code = screen.getByText("pwd");
expect(code.getAttribute("aria-describedby")).toBeNull();
fireEvent.mouseEnter(code);
const tooltip = screen.getByRole("tooltip");
expect(code.getAttribute("aria-describedby")).toBe(tooltip.id);
expect(tooltip.id).not.toBe("");
fireEvent.mouseLeave(code);
expect(code.getAttribute("aria-describedby")).toBeNull();
});
it("dismisses the tooltip on scroll so it cannot drift from the anchored code", () => {
render(<InlineCode>cd</InlineCode>);
fireEvent.mouseEnter(screen.getByText("cd"));
expect(screen.queryByRole("tooltip")).not.toBeNull();
act(() => {
window.dispatchEvent(new Event("scroll"));
});
expect(screen.queryByRole("tooltip")).toBeNull();
});
});