Files
flights_web/src/features/flights-map/api.test.ts
T
gnezim aa61049229 Add flights-map types, API functions, hooks, and feature flag
Phase 4A: Define IFlightRoute, IMapMarker, IMapPolyline types; implement
searchDestinations and getFlightsMapCalendar API functions with 11 tests;
add useFlightsMapSearch, useFlightsMapCalendar hooks; add FEATURE_FLIGHTS_MAP
env var for feature flag gating.
2026-04-15 09:40:19 +03:00

218 lines
6.1 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import { ApiClient } from "@/shared/api/client";
import { searchDestinations, getFlightsMapCalendar } from "./api";
import type {
FlightsMapSearchParams,
FlightsMapCalendarParams,
IDestinationsResponse,
IFlightsMapDaysResponse,
} from "./types";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function createMockClient(responseBody: unknown, status = 200) {
const mockFetch = vi.fn<typeof fetch>().mockResolvedValue(
new Response(JSON.stringify(responseBody), {
status,
headers: { "Content-Type": "application/json" },
}),
);
const client = new ApiClient({
baseUrl: "https://api.test.com",
locale: "ru",
fetchImpl: mockFetch,
retry: { maxRetries: 0 },
});
return { client, mockFetch };
}
function extractUrl(mockFetch: ReturnType<typeof vi.fn>): URL {
const call = mockFetch.mock.calls[0] as [string, ...unknown[]];
return new URL(call[0]);
}
const DESTINATIONS_RESPONSE: IDestinationsResponse = {
data: {
routes: [
{ route: ["SVO", "LED"], isDirect: true },
{ route: ["SVO", "DME", "LED"], isDirect: false },
],
},
};
const DAYS_RESPONSE: IFlightsMapDaysResponse = {
days: "01101",
};
// ---------------------------------------------------------------------------
// searchDestinations
// ---------------------------------------------------------------------------
describe("searchDestinations", () => {
it("sends GET to destinations/1 with query params", async () => {
const { client, mockFetch } = createMockClient(DESTINATIONS_RESPONSE);
const params: FlightsMapSearchParams = {
departure: "SVO",
arrival: "LED",
dateFrom: "20250601",
dateTo: "20251201",
};
await searchDestinations(client, params);
const url = extractUrl(mockFetch);
expect(url.pathname).toBe("/destinations/1");
expect(url.searchParams.get("departure")).toBe("SVO");
expect(url.searchParams.get("arrival")).toBe("LED");
expect(url.searchParams.get("dateFrom")).toBe("20250601");
expect(url.searchParams.get("dateTo")).toBe("20251201");
expect(url.searchParams.get("connections")).toBe("0");
});
it("omits arrival param when not provided", async () => {
const { client, mockFetch } = createMockClient(DESTINATIONS_RESPONSE);
await searchDestinations(client, {
departure: "SVO",
dateFrom: "20250601",
dateTo: "20251201",
});
const url = extractUrl(mockFetch);
expect(url.searchParams.has("arrival")).toBe(false);
});
it("sends connections param when specified", async () => {
const { client, mockFetch } = createMockClient(DESTINATIONS_RESPONSE);
await searchDestinations(client, {
departure: "SVO",
arrival: "LED",
dateFrom: "20250601",
dateTo: "20251201",
connections: 1,
});
const url = extractUrl(mockFetch);
expect(url.searchParams.get("connections")).toBe("1");
});
it("returns the deserialized response", async () => {
const { client } = createMockClient(DESTINATIONS_RESPONSE);
const result = await searchDestinations(client, {
departure: "SVO",
arrival: "LED",
dateFrom: "20250601",
dateTo: "20251201",
});
expect(result).toEqual(DESTINATIONS_RESPONSE);
});
it("throws on server error", async () => {
const { client } = createMockClient({ error: "internal" }, 500);
await expect(
searchDestinations(client, {
departure: "SVO",
dateFrom: "20250601",
dateTo: "20251201",
}),
).rejects.toThrow("HTTP 500");
});
});
// ---------------------------------------------------------------------------
// getFlightsMapCalendar
// ---------------------------------------------------------------------------
describe("getFlightsMapCalendar", () => {
it("builds correct path for direct route", async () => {
const { client, mockFetch } = createMockClient(DAYS_RESPONSE);
await getFlightsMapCalendar(client, {
date: "20250601",
departure: "SVO",
arrival: "LED",
connections: false,
});
const url = extractUrl(mockFetch);
expect(url.pathname).toBe("/days/20250601/200/route/SVO-LED/flights-map/v1");
});
it("builds correct path for connecting route", async () => {
const { client, mockFetch } = createMockClient(DAYS_RESPONSE);
await getFlightsMapCalendar(client, {
date: "20250601",
departure: "SVO",
arrival: "LED",
connections: true,
});
const url = extractUrl(mockFetch);
expect(url.pathname).toBe(
"/days/20250601/200/connections/SVO-LED-1/flights-map/v1",
);
});
it("builds correct path for departure-only (spider mode)", async () => {
const { client, mockFetch } = createMockClient(DAYS_RESPONSE);
await getFlightsMapCalendar(client, {
date: "20250601",
departure: "SVO",
connections: false,
});
const url = extractUrl(mockFetch);
expect(url.pathname).toBe("/days/20250601/200/departure/SVO/flights-map/v1");
});
it("parses binary days string into available date strings", async () => {
const { client } = createMockClient({ days: "10110" });
const result = await getFlightsMapCalendar(client, {
date: "20250601",
departure: "SVO",
arrival: "LED",
connections: false,
});
expect(result).toEqual(["20250601", "20250603", "20250604"]);
});
it("returns empty array for empty days string", async () => {
const { client } = createMockClient({ days: "" });
const result = await getFlightsMapCalendar(client, {
date: "20250601",
departure: "SVO",
arrival: "LED",
connections: false,
});
expect(result).toEqual([]);
});
it("throws on server error", async () => {
const { client } = createMockClient({ error: "internal" }, 500);
await expect(
getFlightsMapCalendar(client, {
date: "20250601",
departure: "SVO",
arrival: "LED",
connections: false,
}),
).rejects.toThrow("HTTP 500");
});
});