Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/components/analytics/AnalyticsOverview.test.tsx
T

110 lines
3.9 KiB
TypeScript

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(
<Router>
<AnalyticsOverview overview={overview} withUsers={false} />
</Router>
);
}
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)");
});
});