shoc-frontend-new/src/test/app/(protected)/locations/state-select.test.tsx
Alexandre Brandizzi a75b9c8be6 feat(sites): Add Site modal and site detail panel with in-place edit
- Sites page: Add Site modal with Site Code, Client, address, State/Area, optional Site Phone and Email, points of contact
- Duplicate Site Code shows inline under the field and keeps the modal open
- Row opens a non-modal detail panel; Street Address links to Google Maps; key-less map preview
- Edit switches the panel in place with Site Code read-only; Save needs a valid change, Cancel makes no call
- Site Phone is sent independently of the contacts; zip and title are carried through edits
- Retire the separate new/edit location pages
2026-09-25 11:43:30 -03:00

136 lines
4.6 KiB
TypeScript

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { fireEvent, screen, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { StateSelect } from "@/app/(protected)/locations/_components/state-select";
import {
locationDefaultValues,
locationSchema,
type LocationFormValues,
} from "@/domain/locations/schemas/location-schema";
import { renderWithProviders } from "@/test/test-utils";
function StateSelectHarness({
onSubmit = vi.fn(),
}: {
onSubmit?: (values: LocationFormValues) => void;
}) {
const {
control,
handleSubmit,
formState: { errors },
} = useForm<LocationFormValues>({
resolver: zodResolver(locationSchema),
defaultValues: {
...locationDefaultValues,
name: "Dallas DC",
address: "3811 Distribution Dr",
city: "Dallas",
zipCode: "70068",
contacts: [{ name: "Jane", phone: "(421) 433-0032" }],
contactEmail: "jane@example.com",
accountId: "7",
},
});
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<StateSelect control={control} error={errors.state} />
<button type="submit">Save</button>
</form>
);
}
function openOptions() {
const input = screen.getByRole("combobox");
input.focus();
fireEvent.mouseDown(input);
return input;
}
describe("StateSelect", () => {
it("starts empty with no implicit default state", () => {
renderWithProviders(<StateSelectHarness />, { withAuth: false });
expect(screen.getByRole("combobox")).toHaveValue("");
expect(screen.queryByText("Area: ")).not.toBeInTheDocument();
});
it("shows searchable Name (CODE) options and no free-text entries", async () => {
renderWithProviders(<StateSelectHarness />, { withAuth: false });
const input = openOptions();
const options = await screen.findAllByRole("option");
expect(options).toHaveLength(50);
expect(screen.getByRole("option", { name: "Texas (TX)" })).toBeInTheDocument();
expect(screen.getByRole("option", { name: "California (CA)" })).toBeInTheDocument();
fireEvent.change(input, { target: { value: "mass" } });
await screen.findAllByRole("option");
expect(screen.getByRole("option", { name: "Massachusetts (MA)" })).toBeInTheDocument();
expect(screen.getAllByRole("option")).toHaveLength(1);
fireEvent.change(input, { target: { value: "Republic of Texas" } });
expect(screen.queryByRole("option")).not.toBeInTheDocument();
});
it("selects by search and shows the Area helper immediately", async () => {
const onSubmit = vi.fn();
renderWithProviders(<StateSelectHarness onSubmit={onSubmit} />, { withAuth: false });
const input = openOptions();
fireEvent.change(input, { target: { value: "texas" } });
fireEvent.click(await screen.findByRole("option", { name: "Texas (TX)" }));
expect(input).toHaveValue("Texas (TX)");
expect(screen.getByText("Area: West")).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "Save" }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({ state: "TX" }),
expect.anything(),
);
});
});
it("is keyboard accessible (arrow + enter selection)", async () => {
const onSubmit = vi.fn();
renderWithProviders(<StateSelectHarness onSubmit={onSubmit} />, { withAuth: false });
const input = screen.getByRole("combobox");
input.focus();
fireEvent.change(input, { target: { value: "ohio" } });
fireEvent.keyDown(input, { key: "ArrowDown" });
fireEvent.keyDown(input, { key: "Enter" });
expect(input).toHaveValue("Ohio (OH)");
expect(screen.getByText("Area: Central")).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "Save" }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({ state: "OH" }),
expect.anything(),
);
});
});
it("requires a state and rejects free text on submit", async () => {
const onSubmit = vi.fn();
renderWithProviders(<StateSelectHarness onSubmit={onSubmit} />, { withAuth: false });
fireEvent.click(screen.getByRole("button", { name: "Save" }));
expect(await screen.findByText("State is required")).toBeInTheDocument();
expect(onSubmit).not.toHaveBeenCalled();
const input = screen.getByRole("combobox");
input.focus();
fireEvent.change(input, { target: { value: "NotAState" } });
fireEvent.keyDown(input, { key: "Enter" });
fireEvent.click(screen.getByRole("button", { name: "Save" }));
await screen.findByText("State is required");
expect(onSubmit).not.toHaveBeenCalled();
});
});