Add Phase 5C integration tests for PopularRequestsPanel
Covers all 5 request modes, loading/error states, keyboard accessibility, and the 4-item display limit.
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { PopularRequestsPanel } from "./PopularRequestsPanel";
|
||||
import type { PopularRequest } from "../types";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mocks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Mock the usePopularRequests hook
|
||||
const mockUsePopularRequests = vi.fn<
|
||||
() => { requests: PopularRequest[]; loading: boolean; error: Error | null }
|
||||
>();
|
||||
|
||||
vi.mock("../hooks/usePopularRequests.js", () => ({
|
||||
usePopularRequests: () => mockUsePopularRequests(),
|
||||
}));
|
||||
|
||||
// Mock i18n
|
||||
vi.mock("@/i18n/provider.js", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
i18n: { language: "en" },
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock useCityName
|
||||
vi.mock("@/shared/hooks/useDictionaries.js", () => ({
|
||||
useCityName: (code: string) => code,
|
||||
}));
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Test data
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const mockRequests: PopularRequest[] = [
|
||||
{
|
||||
mode: "FlightNumber",
|
||||
carrier: "SU",
|
||||
flightNumber: "9027",
|
||||
type: "Onlineboard",
|
||||
},
|
||||
{
|
||||
mode: "FlightNumber",
|
||||
carrier: "SU",
|
||||
flightNumber: "9006",
|
||||
type: "Onlineboard",
|
||||
},
|
||||
{
|
||||
mode: "Route",
|
||||
departure: "KUF",
|
||||
arrival: "ABA",
|
||||
type: "Onlineboard",
|
||||
},
|
||||
{
|
||||
mode: "Route",
|
||||
departure: "RTW",
|
||||
arrival: "ABA",
|
||||
type: "Onlineboard",
|
||||
},
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("PopularRequestsPanel", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders nothing while loading", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [],
|
||||
loading: true,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const { container } = render(
|
||||
<PopularRequestsPanel onRequestClick={vi.fn()} />,
|
||||
);
|
||||
|
||||
expect(container.innerHTML).toBe("");
|
||||
});
|
||||
|
||||
it("renders nothing on error (graceful degradation)", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [],
|
||||
loading: false,
|
||||
error: new Error("API failure"),
|
||||
});
|
||||
|
||||
const { container } = render(
|
||||
<PopularRequestsPanel onRequestClick={vi.fn()} />,
|
||||
);
|
||||
|
||||
expect(container.innerHTML).toBe("");
|
||||
});
|
||||
|
||||
it("renders nothing when requests array is empty", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const { container } = render(
|
||||
<PopularRequestsPanel onRequestClick={vi.fn()} />,
|
||||
);
|
||||
|
||||
expect(container.innerHTML).toBe("");
|
||||
});
|
||||
|
||||
it("renders the title and up to 4 request items", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: mockRequests,
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
expect(screen.getByText("BOARD.POPULAR-CHAPTERS")).toBeInTheDocument();
|
||||
// Should render 4 items
|
||||
const items = screen.getAllByRole("button");
|
||||
expect(items).toHaveLength(4);
|
||||
});
|
||||
|
||||
it("calls onRequestClick when a flight number item is clicked", async () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [mockRequests[0]!],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const onRequestClick = vi.fn();
|
||||
render(<PopularRequestsPanel onRequestClick={onRequestClick} />);
|
||||
|
||||
const button = screen.getByRole("button");
|
||||
await userEvent.click(button);
|
||||
|
||||
expect(onRequestClick).toHaveBeenCalledWith(mockRequests[0]);
|
||||
});
|
||||
|
||||
it("renders flight number with carrier and number", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [mockRequests[0]!],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
// SU\u00a09027 (non-breaking space)
|
||||
expect(screen.getByText("SU\u00a09027")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders route request with departure and arrival", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [mockRequests[2]!],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
expect(screen.getByText("KUF - ABA")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("limits display to 4 items even with more data", () => {
|
||||
const manyRequests: PopularRequest[] = [
|
||||
...mockRequests,
|
||||
{
|
||||
mode: "Arrival",
|
||||
arrival: "SVO",
|
||||
type: "Onlineboard",
|
||||
},
|
||||
];
|
||||
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: manyRequests,
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
const items = screen.getAllByRole("button");
|
||||
expect(items).toHaveLength(4);
|
||||
});
|
||||
|
||||
it("renders arrival request mode", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [
|
||||
{
|
||||
mode: "Arrival",
|
||||
arrival: "SVO",
|
||||
type: "Onlineboard",
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
expect(screen.getByText("BOARD.ARRIVAL")).toBeInTheDocument();
|
||||
expect(screen.getByText("SVO")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders departure request mode", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [
|
||||
{
|
||||
mode: "Departure",
|
||||
departure: "DME",
|
||||
type: "Onlineboard",
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
expect(screen.getByText("BOARD.DEPARTURE")).toBeInTheDocument();
|
||||
expect(screen.getByText("DME")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders schedule route with correct label", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [
|
||||
{
|
||||
mode: "Route",
|
||||
departure: "SVO",
|
||||
arrival: "LED",
|
||||
type: "Schedule",
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
expect(screen.getByText("SCHEDULE.SCHEDULE-OUTBOUND")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders RouteWithBack with full-route label", () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [
|
||||
{
|
||||
mode: "RouteWithBack",
|
||||
departure: "SVO",
|
||||
arrival: "JFK",
|
||||
type: "Schedule",
|
||||
},
|
||||
],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<PopularRequestsPanel onRequestClick={vi.fn()} />);
|
||||
|
||||
expect(
|
||||
screen.getByText("SCHEDULE.SCHEDULE-FULL-ROUTE"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("supports keyboard activation (Enter key)", async () => {
|
||||
mockUsePopularRequests.mockReturnValue({
|
||||
requests: [mockRequests[0]!],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const onRequestClick = vi.fn();
|
||||
render(<PopularRequestsPanel onRequestClick={onRequestClick} />);
|
||||
|
||||
const button = screen.getByRole("button");
|
||||
button.focus();
|
||||
await userEvent.keyboard("{Enter}");
|
||||
|
||||
expect(onRequestClick).toHaveBeenCalledWith(mockRequests[0]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user