diff --git a/apps/web/src/components/__tests__/dialog.test.tsx b/apps/web/src/components/__tests__/dialog.test.tsx new file mode 100644 index 0000000..9c483f4 --- /dev/null +++ b/apps/web/src/components/__tests__/dialog.test.tsx @@ -0,0 +1,71 @@ +// @vitest-environment jsdom +import { cleanup, render, screen } from "@testing-library/react"; +import { userEvent } from "@testing-library/user-event"; +import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; +import { Dialog, DialogHeader } from "../ui/dialog.js"; + +beforeAll(() => { + HTMLDialogElement.prototype.showModal = + HTMLDialogElement.prototype.showModal || + function showModal(this: HTMLDialogElement) { + this.setAttribute("open", ""); + }; + HTMLDialogElement.prototype.close = + HTMLDialogElement.prototype.close || + function close(this: HTMLDialogElement) { + this.removeAttribute("open"); + }; +}); + +afterEach(cleanup); + +describe("Dialog", () => { + it("opens when open=true", () => { + render( + {}}> + Content + , + ); + expect(screen.getByText("Content")).toBeDefined(); + }); + + it("closes when open changes from true to false", () => { + const { rerender } = render( + {}}> + Content + , + ); + const dialog = document.querySelector("dialog"); + expect(dialog?.hasAttribute("open")).toBe(true); + + rerender( + {}}> + Content + , + ); + expect(dialog?.hasAttribute("open")).toBe(false); + }); + + it("calls onClose on cancel event", () => { + const onClose = vi.fn(); + render( + + Content + , + ); + const dialog = document.querySelector("dialog"); + dialog?.dispatchEvent(new Event("cancel")); + expect(onClose).toHaveBeenCalledOnce(); + }); +}); + +describe("DialogHeader", () => { + it("renders title and close button", async () => { + const onClose = vi.fn(); + render(); + + expect(screen.getByText("Test Title")).toBeDefined(); + await userEvent.click(screen.getByRole("button")); + expect(onClose).toHaveBeenCalledOnce(); + }); +}); diff --git a/apps/web/src/components/__tests__/tooltip.test.tsx b/apps/web/src/components/__tests__/tooltip.test.tsx new file mode 100644 index 0000000..f160286 --- /dev/null +++ b/apps/web/src/components/__tests__/tooltip.test.tsx @@ -0,0 +1,42 @@ +// @vitest-environment jsdom +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, it } from "vitest"; +import { Tooltip } from "../ui/tooltip.js"; + +afterEach(cleanup); + +describe("Tooltip", () => { + it("renders children", () => { + render( + + + , + ); + expect(screen.getByText("Hover me")).toBeDefined(); + }); + + it("does not show tooltip initially", () => { + render( + + Target + , + ); + expect(screen.queryByRole("tooltip")).toBeNull(); + }); + + it("shows tooltip on pointer enter and hides on pointer leave", () => { + render( + + Target + , + ); + + const wrapper = screen.getByText("Target").closest("span"); + fireEvent.pointerEnter(wrapper as HTMLElement); + expect(screen.getByRole("tooltip")).toBeDefined(); + expect(screen.getByText("Hint text")).toBeDefined(); + + fireEvent.pointerLeave(wrapper as HTMLElement); + expect(screen.queryByRole("tooltip")).toBeNull(); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 0c38f55..ed448de 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -34,8 +34,8 @@ export default defineConfig({ branches: 55, }, "apps/web/src/components/ui": { - lines: 86, - branches: 83, + lines: 93, + branches: 90, }, }, },