import { describe, it, expect, vi } from "vitest"; import { render, screen, fireEvent } from "@testing-library/react"; import { ObservationCard } from "../../../src/features/packets/ObservationCard"; import type { Observation } from "../../../src/types/api"; const obs = (over: Partial = {}): Observation => ({ id: 1, observerId: "o1", observerName: "Observer 1", iata: "YVR", heardAt: 1700000000, pathLength: { raw: "41", hashSize: 1, hopCount: 1 }, sourceBroker: "b1", resolvedPath: [], ...over, }); const tokens = (el: Element) => (el.getAttribute("class") ?? "").split(/\s+/); describe("ObservationCard", () => { // jsdom can't measure layout, so these assert the class tokens that encode the behavior. it("lets a long observer name truncate instead of overflowing the card", () => { render(); const name = screen.getByText("a-very-long-observer-station-name"); expect(tokens(name)).toEqual(expect.arrayContaining(["flex-1", "min-w-0", "truncate"])); }); it("lays the four stats out as equal grid columns that can shrink", () => { render(); const stats = screen.getByText("SNR").parentElement!.parentElement!; expect(tokens(stats)).toEqual(expect.arrayContaining(["grid", "grid-cols-4"])); for (const label of ["SNR", "RSSI", "Prop", "Hops"]) { expect(tokens(screen.getByText(label).parentElement!)).toContain("min-w-0"); } }); // The left edge mirrors the SNR coloring the desktop table applies to its SNR column. it.each([ [12, "border-l-green"], [7, "border-l-warn"], [2, "border-l-danger"], ])("colors the left edge by signal level (snr %s → %s)", (snr, cls) => { const { container } = render(); expect(tokens(container.firstElementChild!)).toContain(cls); }); it("keeps a neutral edge when SNR is unknown", () => { const { container } = render(); expect(tokens(container.firstElementChild!)).toContain("border-l-primary"); }); it("lets selection override the signal edge", () => { const { container } = render(); const cls = tokens(container.firstElementChild!); expect(cls).toContain("border-l-secondary"); expect(cls).not.toContain("border-l-green"); }); // Guards the analyzer-drawer usage: content and click-through must survive the layout change. it("still renders IATA, timestamp, path row and fires onClick", () => { const onClick = vi.fn(); render( , ); expect(screen.getByText("YVR")).toBeInTheDocument(); expect(screen.getByText("8.50")).toBeInTheDocument(); expect(screen.getByText("-108")).toBeInTheDocument(); expect(screen.getByText("41")).toBeInTheDocument(); fireEvent.click(screen.getByText("Observer 1")); expect(onClick).toHaveBeenCalledTimes(1); }); });