import React from "react"; import "@testing-library/jest-dom/vitest"; import { fireEvent, render, screen, within } from "@testing-library/react"; import { BrowserRouter as Router } from "react-router"; import fs from "node:fs"; import path from "node:path"; import { describe, expect, test, vi } from "vitest"; import AnalyticsOverview from "./AnalyticsOverview"; vi.mock("react-i18next", () => ({ useTranslation: () => ({ t: (key: string) => key, i18n: { language: "de" } }), })); vi.mock("i18next", () => ({ t: (key: string) => key, })); vi.mock("~/utils/data/useRootData", () => ({ useRootData: () => ({ authenticated: false }), })); const overview = { uniqueVisitors: 1234, pageViews: 34, events: 5, liveVisitors: 1, top: { httpReferrers: [{ name: "Direct", count: 1234 }], urls: [ { name: null, count: 10 }, { name: "/pricing", count: 9 }, { name: "/docs", count: 4 }, ], routes: [{ name: "/portals/:portal", count: 3 }], sources: [{ name: "newsletter", count: 6 }], via: [{ name: "partner", count: 4 }], medium: [{ name: "email", count: 6 }], campaign: [{ name: "spring", count: 2 }], user: [], }, } as any; function renderOverview() { return render( ); } describe("AnalyticsOverview", () => { test("renders top-item tabs with accessible state and mobile-friendly sizing", () => { renderOverview(); const pagesTab = screen.getByRole("tab", { name: "analytics.pages" }); const routesTab = screen.getByRole("tab", { name: "analytics.routes" }); const tablist = pagesTab.closest('[role="tablist"]'); expect(tablist).toHaveClass("min-w-0", "overflow-x-auto"); expect(pagesTab).toHaveClass("min-h-10", "shrink-0", "whitespace-nowrap"); expect(pagesTab).toHaveAttribute("aria-selected", "true"); expect(routesTab).toHaveAttribute("aria-selected", "false"); const panel = screen.getByRole("tabpanel", { name: "analytics.pages" }); expect(panel).toHaveAttribute("id", pagesTab.getAttribute("aria-controls")); expect(within(panel).getByText("shared.notSet")).toBeInTheDocument(); expect(within(panel).getByText("/pricing")).toBeInTheDocument(); fireEvent.click(routesTab); expect(routesTab).toHaveAttribute("aria-selected", "true"); expect(screen.getByRole("tabpanel", { name: "analytics.routes" })).toHaveTextContent("/portals/:portal"); }); test("supports keyboard tab selection", () => { renderOverview(); const pagesTab = screen.getByRole("tab", { name: "analytics.pages" }); fireEvent.keyDown(pagesTab, { key: "ArrowRight" }); expect(screen.getByRole("tab", { name: "analytics.routes" })).toHaveAttribute("aria-selected", "true"); }); test("formats overview numbers with the active locale", () => { renderOverview(); expect(screen.getAllByText("1.234").length).toBeGreaterThanOrEqual(2); }); test("keeps analytics overview free of legacy number formatters", () => { const sourceRoot = path.resolve(process.cwd(), "apps/web/app/components/analytics"); const overviewSource = fs.readFileSync(path.join(sourceRoot, "AnalyticsOverview.tsx"), "utf8"); const chartSource = fs.readFileSync(path.join(sourceRoot, "AnalyticsOverviewChart.tsx"), "utf8"); expect(`${overviewSource}\n${chartSource}`).not.toContain("NumberUtils"); }); test("precomputes top-item max counts once per item set", () => { const source = fs.readFileSync(path.resolve(process.cwd(), "apps/web/app/components/analytics/AnalyticsOverview.tsx"), "utf8"); expect(source).toContain("useMemo"); expect(source).toContain("const maxCount = useMemo"); expect(source).toContain('fallbackName: t("shared.notSet")'); expect(source).not.toContain('fallbackName: "?"'); expect(source).not.toContain('fallbackName: "none"'); expect(source).not.toContain("const counts = items.map"); expect(source).not.toContain("Math.max(...counts)"); }); });