diff --git a/src/features/popular-requests/components/PopularRequestsPanel.test.tsx b/src/features/popular-requests/components/PopularRequestsPanel.test.tsx
new file mode 100644
index 00000000..829663ab
--- /dev/null
+++ b/src/features/popular-requests/components/PopularRequestsPanel.test.tsx
@@ -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(
+ ,
+ );
+
+ expect(container.innerHTML).toBe("");
+ });
+
+ it("renders nothing on error (graceful degradation)", () => {
+ mockUsePopularRequests.mockReturnValue({
+ requests: [],
+ loading: false,
+ error: new Error("API failure"),
+ });
+
+ const { container } = render(
+ ,
+ );
+
+ expect(container.innerHTML).toBe("");
+ });
+
+ it("renders nothing when requests array is empty", () => {
+ mockUsePopularRequests.mockReturnValue({
+ requests: [],
+ loading: false,
+ error: null,
+ });
+
+ const { container } = render(
+ ,
+ );
+
+ expect(container.innerHTML).toBe("");
+ });
+
+ it("renders the title and up to 4 request items", () => {
+ mockUsePopularRequests.mockReturnValue({
+ requests: mockRequests,
+ loading: false,
+ error: null,
+ });
+
+ render();
+
+ 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();
+
+ 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();
+
+ // 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ const button = screen.getByRole("button");
+ button.focus();
+ await userEvent.keyboard("{Enter}");
+
+ expect(onRequestClick).toHaveBeenCalledWith(mockRequests[0]);
+ });
+});