165 lines
5.1 KiB
TypeScript
165 lines
5.1 KiB
TypeScript
import "@testing-library/jest-dom/vitest";
|
|
import { cleanup, fireEvent, render, screen, within } from "@testing-library/react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import type { BoatProfile } from "@watermaps/shared";
|
|
import { BoatProfilePanel } from "../src/components/BoatProfilePanel";
|
|
|
|
afterEach(cleanup);
|
|
|
|
describe("BoatProfilePanel", () => {
|
|
it("shows the current profile, all routing-relevant fields and cookie information", () => {
|
|
renderPanel();
|
|
|
|
expect(screen.getByRole("heading", { name: "Aktuelles Boot" })).toBeVisible();
|
|
expect(screen.getByLabelText("Name des Bootes")).toHaveValue("Seestern");
|
|
expect(screen.getByLabelText("Kategorie")).toHaveValue("sailboat");
|
|
expect(screen.getByLabelText("Länge über alles")).toHaveValue(10.4);
|
|
expect(screen.getByLabelText("Breite")).toHaveValue(3.3);
|
|
expect(
|
|
screen.getByLabelText("Durchfahrtshöhe ab Wasserlinie")
|
|
).toHaveValue(14.2);
|
|
expect(screen.getByLabelText("Tiefgang")).toHaveValue(1.8);
|
|
expect(screen.getByLabelText("Sicherheitsreserve")).toHaveValue(0.5);
|
|
expect(screen.getByLabelText("Reisegeschwindigkeit")).toHaveValue(6);
|
|
expect(screen.getByLabelText("Bugrolle über Wasserlinie")).toHaveValue(1.1);
|
|
expect(
|
|
screen.getByRole("group", { name: "Ankern" })
|
|
).toHaveTextContent("von der Durchfahrtshöhe getrennt");
|
|
expect(screen.getByText(/in einem Cookie dieses Browsers gespeichert/)).toBeVisible();
|
|
});
|
|
|
|
it("validates, normalizes and saves the edited profile", () => {
|
|
const onSave = vi.fn(() => true);
|
|
renderPanel({ onSave, hasActiveRoute: true });
|
|
|
|
expect(screen.getByText(/Beim Speichern wird sie verworfen/)).toBeVisible();
|
|
fireEvent.change(screen.getByLabelText("Name des Bootes"), {
|
|
target: { value: " Kleine Möwe " }
|
|
});
|
|
fireEvent.change(screen.getByLabelText("Kategorie"), {
|
|
target: { value: "motor-sailer" }
|
|
});
|
|
fireEvent.change(screen.getByLabelText("Breite"), {
|
|
target: { value: "3.6" }
|
|
});
|
|
fireEvent.change(screen.getByLabelText("Bugrolle über Wasserlinie"), {
|
|
target: { value: "1.25" }
|
|
});
|
|
fireEvent.click(
|
|
screen.getByRole("button", { name: "Bootsdaten speichern" })
|
|
);
|
|
|
|
expect(onSave).toHaveBeenCalledWith({
|
|
...PROFILE,
|
|
name: "Kleine Möwe",
|
|
category: "motor-sailer",
|
|
beamM: 3.6,
|
|
bowRollerHeightM: 1.25
|
|
});
|
|
expect(screen.getByRole("status")).toHaveTextContent(
|
|
"Die Bootsdaten wurden in diesem Browser gespeichert."
|
|
);
|
|
});
|
|
|
|
it("reports invalid values without calling the persistence callback", () => {
|
|
const onSave = vi.fn(() => true);
|
|
renderPanel({ onSave });
|
|
|
|
fireEvent.change(screen.getByLabelText("Reisegeschwindigkeit"), {
|
|
target: { value: "81" }
|
|
});
|
|
fireEvent.submit(
|
|
screen.getByRole("button", { name: "Bootsdaten speichern" }).closest("form")!
|
|
);
|
|
|
|
expect(onSave).not.toHaveBeenCalled();
|
|
expect(screen.getByRole("alert")).toHaveTextContent(
|
|
"Reisegeschwindigkeit muss zwischen mehr als 0 und 80 liegen."
|
|
);
|
|
});
|
|
|
|
it("explains when the browser refuses to save the cookie", () => {
|
|
renderPanel({ onSave: vi.fn(() => false) });
|
|
|
|
fireEvent.click(
|
|
screen.getByRole("button", { name: "Bootsdaten speichern" })
|
|
);
|
|
|
|
expect(screen.getByRole("alert")).toHaveTextContent(
|
|
"nicht im Cookie gespeichert"
|
|
);
|
|
});
|
|
|
|
it("only offers deletion for stored data and requires inline confirmation", () => {
|
|
const onDelete = vi.fn(() => true);
|
|
const { rerender } = renderPanel({ stored: false, onDelete });
|
|
|
|
expect(
|
|
screen.queryByRole("button", { name: "Bootsdaten löschen" })
|
|
).not.toBeInTheDocument();
|
|
|
|
rerender(
|
|
<BoatProfilePanel
|
|
profile={PROFILE}
|
|
stored
|
|
onSave={vi.fn(() => true)}
|
|
onDelete={onDelete}
|
|
/>
|
|
);
|
|
|
|
fireEvent.click(
|
|
screen.getByRole("button", { name: "Bootsdaten löschen" })
|
|
);
|
|
const confirmation = screen.getByRole("group", {
|
|
name: "Löschen der Bootsdaten bestätigen"
|
|
});
|
|
expect(onDelete).not.toHaveBeenCalled();
|
|
|
|
fireEvent.click(
|
|
within(confirmation).getByRole("button", { name: "Abbrechen" })
|
|
);
|
|
expect(
|
|
screen.queryByRole("group", {
|
|
name: "Löschen der Bootsdaten bestätigen"
|
|
})
|
|
).not.toBeInTheDocument();
|
|
|
|
fireEvent.click(
|
|
screen.getByRole("button", { name: "Bootsdaten löschen" })
|
|
);
|
|
fireEvent.click(
|
|
screen.getByRole("button", { name: "Endgültig löschen" })
|
|
);
|
|
|
|
expect(onDelete).toHaveBeenCalledTimes(1);
|
|
expect(screen.getByRole("status")).toHaveTextContent(
|
|
"aus diesem Browser gelöscht"
|
|
);
|
|
});
|
|
});
|
|
|
|
const PROFILE: BoatProfile = {
|
|
name: "Seestern",
|
|
category: "sailboat",
|
|
lengthM: 10.4,
|
|
beamM: 3.3,
|
|
draughtM: 1.8,
|
|
airDraftM: 14.2,
|
|
safetyReserveM: 0.5,
|
|
cruiseSpeedKn: 6,
|
|
bowRollerHeightM: 1.1
|
|
};
|
|
|
|
function renderPanel(
|
|
overrides: Partial<Parameters<typeof BoatProfilePanel>[0]> = {}
|
|
) {
|
|
const props: Parameters<typeof BoatProfilePanel>[0] = {
|
|
profile: PROFILE,
|
|
stored: true,
|
|
onSave: vi.fn(() => true),
|
|
onDelete: vi.fn(() => true),
|
|
...overrides
|
|
};
|
|
return render(<BoatProfilePanel {...props} />);
|
|
}
|