Test FlightsMapFilter Calendar min/max/disabledDates + snap effect

This commit is contained in:
2026-04-17 12:21:13 +03:00
parent 78b3e86418
commit 0c65755553
@@ -0,0 +1,172 @@
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render } from "@testing-library/react";
import { FlightsMapFilter } from "./FlightsMapFilter.js";
import type { IFlightsMapFilterState } from "../types.js";
// Capture props passed to PrimeReact Calendar.
let lastCalendarProps: Record<string, unknown> | null = null;
vi.mock("primereact/calendar", () => ({
Calendar: (props: Record<string, unknown>) => {
lastCalendarProps = props;
return <div data-testid="fm-date-input" />;
},
}));
// Stub PrimeReact AutoComplete so rendering is cheap.
vi.mock("primereact/autocomplete", () => ({
AutoComplete: (props: Record<string, unknown>) => (
<input data-testid={props["data-testid"] as string} />
),
}));
vi.mock("@/i18n/provider.js", () => ({
useTranslation: () => ({
t: (key: string) => key,
i18n: { language: "ru" },
}),
}));
vi.mock("@/shared/hooks/useCitySearch.js", () => ({
useCitySearch: () => ({ suggestions: [], search: vi.fn() }),
}));
function filter(
overrides: Partial<IFlightsMapFilterState> = {},
): IFlightsMapFilterState {
return {
connections: false,
domestic: false,
international: false,
...overrides,
};
}
function yyyymmdd(d: Date): string {
const y = d.getFullYear().toString();
const m = (d.getMonth() + 1).toString().padStart(2, "0");
const day = d.getDate().toString().padStart(2, "0");
return `${y}${m}${day}`;
}
function addDays(base: Date, n: number): Date {
const d = new Date(base);
d.setDate(d.getDate() + n);
d.setHours(0, 0, 0, 0);
return d;
}
describe("FlightsMapFilter — Calendar wiring", () => {
beforeEach(() => {
lastCalendarProps = null;
});
it("passes minDate and maxDate to Calendar", () => {
const onChange = vi.fn();
render(<FlightsMapFilter value={filter()} onChange={onChange} />);
const min = lastCalendarProps!["minDate"] as Date;
const max = lastCalendarProps!["maxDate"] as Date;
const today = new Date();
today.setHours(0, 0, 0, 0);
const expectedMin = addDays(today, -1);
const expectedMax = new Date(today);
expectedMax.setMonth(expectedMax.getMonth() + 6);
expect(min.getTime()).toBe(expectedMin.getTime());
expect(max.getTime()).toBe(expectedMax.getTime());
});
it("disables every date when availableDays is empty", () => {
const onChange = vi.fn();
render(
<FlightsMapFilter value={filter()} availableDays={[]} onChange={onChange} />,
);
const disabled = lastCalendarProps!["disabledDates"] as Date[];
expect(disabled.length).toBeGreaterThan(180);
});
it("excludes available days from disabledDates", () => {
const onChange = vi.fn();
const today = new Date();
today.setHours(0, 0, 0, 0);
const plusTwo = addDays(today, 2);
render(
<FlightsMapFilter
value={filter()}
availableDays={[yyyymmdd(plusTwo)]}
onChange={onChange}
/>,
);
const disabled = lastCalendarProps!["disabledDates"] as Date[];
const contains = disabled.some((d) => {
const x = new Date(d);
x.setHours(0, 0, 0, 0);
return x.getTime() === plusTwo.getTime();
});
expect(contains).toBe(false);
});
it("snaps a disabled value.date forward to the next available day", () => {
const onChange = vi.fn();
const today = new Date();
today.setHours(0, 0, 0, 0);
const plusOne = addDays(today, 1);
const plusTwo = addDays(today, 2);
render(
<FlightsMapFilter
value={filter({ date: yyyymmdd(plusOne) })}
availableDays={[yyyymmdd(plusTwo)]}
onChange={onChange}
/>,
);
expect(onChange).toHaveBeenCalled();
const called = onChange.mock.calls[0]![0] as IFlightsMapFilterState;
expect(called.date).toBe(yyyymmdd(plusTwo));
});
it("clears value.date when no enabled date exists in the window", () => {
const onChange = vi.fn();
const today = new Date();
today.setHours(0, 0, 0, 0);
const plusOne = addDays(today, 1);
render(
<FlightsMapFilter
value={filter({ date: yyyymmdd(plusOne) })}
availableDays={[]}
onChange={onChange}
/>,
);
expect(onChange).toHaveBeenCalled();
const called = onChange.mock.calls[0]![0] as IFlightsMapFilterState;
expect(called.date).toBeUndefined();
});
it("does not snap when the current date is already enabled", () => {
const onChange = vi.fn();
const today = new Date();
today.setHours(0, 0, 0, 0);
const plusTwo = addDays(today, 2);
render(
<FlightsMapFilter
value={filter({ date: yyyymmdd(plusTwo) })}
availableDays={[yyyymmdd(plusTwo)]}
onChange={onChange}
/>,
);
expect(onChange).not.toHaveBeenCalled();
});
});