import { act } from "react"; import { afterEach, describe, it, expect } from "vitest"; import { render, screen } from "@testing-library/react"; import { DiveFormActions } from "./dive-form-actions"; import { pinToBottom, reveal, scrollPast } from "@/test/intersection"; // Whether the row is pinned is the observer's answer, so `pinToBottom` stands in // for the form running on below the screen and `reveal` for the row sitting in // its place at the form's end. function renderActions(error: string | null = null) { render( , ); return screen .getByRole("button", { name: "Log dive" }) .closest("[data-dive-form-actions]")!; } describe("DiveFormActions", () => { it("marks itself stuck only while pinned to the foot of the screen", () => { const row = renderActions(); expect(row).not.toHaveAttribute("data-stuck"); act(() => pinToBottom()); expect(row).toHaveAttribute("data-stuck"); act(() => reveal()); expect(row).not.toHaveAttribute("data-stuck"); act(() => pinToBottom()); act(() => scrollPast()); expect(row).not.toHaveAttribute("data-stuck"); }); it("carries a refused submit above its buttons, pinned or not", () => { const row = renderActions("Your browser would not submit this form."); const alert = screen.getByRole("alert"); expect(alert).toHaveTextContent("Your browser would not submit this form."); expect(row).toContainElement(alert); expect( alert.compareDocumentPosition( screen.getByRole("button", { name: "Log dive" }), ) & Node.DOCUMENT_POSITION_FOLLOWING, ).toBeTruthy(); }); describe("on a short viewport", () => { const original = window.visualViewport; afterEach(() => { Object.defineProperty(window, "visualViewport", { value: original, configurable: true, }); }); it("marks the root for as long as it is on the page", () => { // The mark is what lets go of the pin in `globals.css`, and the form has // no dialog open to set it. Object.defineProperty(window, "visualViewport", { value: Object.assign(new EventTarget(), { height: 320, offsetTop: 0 }), configurable: true, }); const { unmount } = render( , ); const root = document.documentElement; expect(root).toHaveAttribute("data-visual-viewport-short"); unmount(); expect(root).not.toHaveAttribute("data-visual-viewport-short"); }); }); // Logging while the save is in flight would race it: on a fresh form the draft // the save makes would be left behind the dive. it("holds Log dive while a draft save is in flight", () => { const actions = (status: "idle" | "saving" | "saved") => ( {}, status }} /> ); const { rerender } = render(actions("saving")); expect(screen.getByRole("button", { name: "Log dive" })).toBeDisabled(); expect( screen.getByRole("button", { name: "Save as draft" }), ).toBeDisabled(); rerender(actions("saved")); expect(screen.getByRole("button", { name: "Log dive" })).toBeEnabled(); expect(screen.getByRole("button", { name: "Saved" })).toBeEnabled(); }); });