From d0c50e81c58abb10158fe1d995a682f1de2a0341 Mon Sep 17 00:00:00 2001 From: gnezim Date: Fri, 17 Apr 2026 01:28:13 +0300 Subject: [PATCH] Add RegistrationButton component --- .../RegistrationButton.test.tsx | 52 +++++++++++++++++++ .../BoardDetailsHeader/RegistrationButton.tsx | 32 ++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/features/online-board/components/BoardDetailsHeader/RegistrationButton.test.tsx create mode 100644 src/features/online-board/components/BoardDetailsHeader/RegistrationButton.tsx diff --git a/src/features/online-board/components/BoardDetailsHeader/RegistrationButton.test.tsx b/src/features/online-board/components/BoardDetailsHeader/RegistrationButton.test.tsx new file mode 100644 index 00000000..050f0a33 --- /dev/null +++ b/src/features/online-board/components/BoardDetailsHeader/RegistrationButton.test.tsx @@ -0,0 +1,52 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { RegistrationButton } from "./RegistrationButton.js"; +import type { ISimpleFlight } from "../../types.js"; + +vi.mock("@/i18n/provider.js", () => ({ + useTranslation: () => ({ t: (k: string) => k }), +})); + +function makeFlight(carrier = "SU"): ISimpleFlight { + return { + id: "X", + routeType: "Direct", + flyingTime: "1h", + status: "Scheduled", + flightId: { carrier, flightNumber: "0022", suffix: "", date: "20260417" }, + operatingBy: { carrier, flightNumber: "0022" }, + leg: {} as never, + } as ISimpleFlight; +} + +describe("RegistrationButton", () => { + beforeEach(() => { + Object.defineProperty(window, "open", { value: vi.fn(), writable: true }); + }); + + it("renders label SHARED.ONLINE-REGISTRATION", () => { + render(); + expect(screen.getByText("SHARED.ONLINE-REGISTRATION")).toBeTruthy(); + }); + + it("has data-testid registration-button", () => { + render(); + expect(screen.getByTestId("registration-button")).toBeTruthy(); + }); + + it("click opens aeroflot.ru registration URL for SU", () => { + render(); + fireEvent.click(screen.getByTestId("registration-button")); + const openMock = window.open as ReturnType; + expect(openMock).toHaveBeenCalledTimes(1); + expect(openMock.mock.calls[0]![0]).toContain("aeroflot.ru"); + }); + + it("click does not open anything for AF (no registrationUrl)", () => { + render(); + fireEvent.click(screen.getByTestId("registration-button")); + const openMock = window.open as ReturnType; + expect(openMock).not.toHaveBeenCalled(); + }); +}); diff --git a/src/features/online-board/components/BoardDetailsHeader/RegistrationButton.tsx b/src/features/online-board/components/BoardDetailsHeader/RegistrationButton.tsx new file mode 100644 index 00000000..5fcdb031 --- /dev/null +++ b/src/features/online-board/components/BoardDetailsHeader/RegistrationButton.tsx @@ -0,0 +1,32 @@ +import type { FC } from "react"; +import { useTranslation } from "@/i18n/provider.js"; +import type { ISimpleFlight } from "../../types.js"; +import { AIRLINES } from "./airlines.js"; +import "./actions.scss"; + +export interface RegistrationButtonProps { + flight: ISimpleFlight; +} + +export const RegistrationButton: FC = ({ flight }) => { + const { t } = useTranslation(); + + const handleClick = () => { + const carrier = flight.operatingBy.carrier; + if (!carrier) return; + const config = AIRLINES[carrier]; + if (!config?.registrationUrl) return; + window.open(config.registrationUrl, "_blank", "noopener,noreferrer"); + }; + + return ( + + ); +};