src/components/dives/dive-detail-main.render.test.tsx
Rama main · réplica de lectura del repositorio original.
import { describe, it, expect, vi } from "vitest";
import { render, screen, within } from "@testing-library/react";
import { DiveDetailMain } from "./dive-detail-main";
import type { Dive, Sighting } from "@/lib/api/dives";
import { dive } from "@/test/dive-fixtures";
// These renders read the diver's units, so they need an auth context.
vi.mock("@/contexts/AuthContext", () => ({
useAuth: () => ({ user: { uuid: "user-1", units: "metric" } }),
}));
// A sighting as the detail read carries one: the species' summary, and nothing
// counted or written unless a test says so.
function sighting(
overrides: Partial<NonNullable<Dive["sightings"]>[number]> = {},
): NonNullable<Dive["sightings"]>[number] {
return {
uuid: "species-1",
scientific_name: "Amphiprion ocellaris",
common_name: "Ocellaris clownfish",
rank: "Species",
count: null,
abundance: null,
notes: "",
...overrides,
};
}
// What the dive page decides about its species; how one species reads as a
// card is `species-card.render.test.tsx`'s.
describe("DiveDetailMain species card", () => {
const CLOWNFISH = sighting();
const MORAY = sighting({
uuid: "species-2",
scientific_name: "Gymnothorax javanicus",
common_name: "Giant moray",
});
it("draws a card for each species spotted, in spotting order", () => {
render(<DiveDetailMain dive={dive({ sightings: [MORAY, CLOWNFISH] })} />);
expect(screen.getByText("Species Spotted")).toBeInTheDocument();
expect(
screen
.getAllByRole("link")
.filter((link) => link.getAttribute("href")?.startsWith("/species/"))
.map((link) => link.textContent),
).toEqual(["Giant moray", "Ocellaris clownfish"]);
});
it("renders no card at all for a dive with nothing spotted", () => {
// Absent and empty alike: the API embeds sightings on the detail response
// only, so a list row has no key.
render(<DiveDetailMain dive={dive({ sightings: [] })} />);
expect(screen.queryByText("Species Spotted")).not.toBeInTheDocument();
render(<DiveDetailMain dive={dive()} />);
expect(screen.queryByText("Species Spotted")).not.toBeInTheDocument();
});
});
describe("DiveDetailMain species card counts and notes", () => {
const MANTA = sighting({
uuid: "species-manta",
scientific_name: "Mobula birostris",
common_name: "Giant manta ray",
count: 3,
notes: "Cleaning station\nat 18 m",
});
const TURTLE = sighting({
uuid: "species-turtle",
scientific_name: "Chelonia mydas",
common_name: "Green sea turtle",
});
const cardOf = (name: string) =>
within(screen.getByRole("link", { name }).closest("li")!);
it("shows how many were counted, and the note with its line breaks", () => {
render(<DiveDetailMain dive={dive({ sightings: [MANTA, TURTLE] })} />);
const manta = cardOf("Giant manta ray");
expect(
manta.getByText("Count", { selector: "dt" }).nextElementSibling,
).toHaveTextContent("3");
expect(
manta.getByText((_, element) => element?.textContent === MANTA.notes, {
selector: "span",
}),
).toHaveClass("whitespace-pre-wrap");
});
it("gives an uncounted sighting no count, never 1", () => {
render(<DiveDetailMain dive={dive({ sightings: [MANTA, TURTLE] })} />);
expect(
cardOf("Green sea turtle").queryByText("Count", { selector: "dt" }),
).not.toBeInTheDocument();
expect(cardOf("Green sea turtle").queryByRole("term")).toBeNull();
});
it("shows a band with its range, as the one figure", () => {
const banded = { ...TURTLE, abundance: "many" as const };
render(<DiveDetailMain dive={dive({ sightings: [banded] })} />);
const turtle = cardOf("Green sea turtle");
expect(turtle.getAllByRole("term").map((term) => term.textContent)).toEqual(
["Abundance"],
);
expect(
turtle.getByText("Abundance", { selector: "dt" }).nextElementSibling,
).toHaveTextContent("Many 11–100");
});
it("shows a band this build has no label for de-snaked, with no range", () => {
const unknown = {
...TURTLE,
abundance: "teeming" as unknown as Sighting["abundance"],
};
render(<DiveDetailMain dive={dive({ sightings: [unknown] })} />);
expect(
cardOf("Green sea turtle").getByText("Abundance", { selector: "dt" })
.nextElementSibling,
).toHaveTextContent(/^Teeming$/);
});
});