Files
MrAlders0n b427282b30 Rebuild My Atlas on the shared Analytics cards (2.0.5)
Atlas cards now use the Signal tab StatCard tiles, shared Card, badges,
action buttons, IATA chips and search bar. Polling moves to TanStack Query
with the app retry policy, pauses while rate-limited, and recovers a pin
whose server ID changed by its full public key. Search results show a key
prefix and explain short or non-hex input; heard-by states which packet it
counts. Sparkline component removed in favour of the stats one.
2026-10-12 13:18:24 -04:00

83 lines
4.0 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent, act } from "@testing-library/react";
import { BottomNav } from "../../src/components/BottomNav";
import i18n from "../../src/i18n";
describe("BottomNav", () => {
it("keeps Nodes primary and puts My Atlas in More, with its identifier intact in French", async () => {
const onTabChange = vi.fn();
render(<BottomNav activeTab="MyAtlas" onTabChange={onTabChange} />);
expect(screen.getAllByRole("tab").map((tab) => tab.textContent)).toEqual(["Packets", "Channels", "Map", "Nodes"]);
expect(screen.getByText("More").closest("button")!.className).toContain("text-primary");
await act(() => i18n.changeLanguage("fr"));
fireEvent.click(screen.getByText("Plus"));
fireEvent.click(screen.getByRole("menuitem", { name: "Mon Atlas" }));
expect(onTabChange).toHaveBeenCalledWith("MyAtlas");
});
it("translates an open More sheet without changing the selected tab identifier", async () => {
const onTabChange = vi.fn();
render(<BottomNav activeTab="Analytics" onTabChange={onTabChange} />);
fireEvent.click(screen.getByText("More"));
await act(() => i18n.changeLanguage("fr"));
expect(screen.getByRole("menu", { name: "Autres onglets" })).toBeInTheDocument();
expect(screen.getByText("Plus").closest("button")).toHaveAttribute("aria-expanded", "true");
fireEvent.click(screen.getByRole("menuitem", { name: "Analyses" }));
expect(onTabChange).toHaveBeenCalledWith("Analytics");
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
});
it("marks the active primary tab with aria-selected", () => {
render(<BottomNav activeTab="Map" onTabChange={() => {}} />);
expect(screen.getByRole("tab", { name: "Map" })).toHaveAttribute("aria-selected", "true");
expect(screen.getByRole("tab", { name: "Packets" })).toHaveAttribute("aria-selected", "false");
});
it("opens the More sheet and selects an overflow tab", () => {
const onTabChange = vi.fn();
render(<BottomNav activeTab="Packets" onTabChange={onTabChange} />);
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
fireEvent.click(screen.getByText("More"));
expect(screen.getByRole("menu", { name: "More tabs" })).toBeInTheDocument();
fireEvent.click(screen.getByRole("menuitem", { name: "Routes" }));
expect(onTabChange).toHaveBeenCalledWith("Routes");
// sheet closes after a pick
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
});
it("highlights More when an overflow tab is active", () => {
render(<BottomNav activeTab="Analytics" onTabChange={() => {}} />);
const more = screen.getByText("More").closest("button")!;
expect(more.className).toContain("text-primary");
});
// iOS drops the click when live content lands mid-tap, so a touch acts on touchend alone
const tapEnd = (el: Element, x = 0, y = 0) => fireEvent.touchEnd(el, { changedTouches: [{ clientX: x, clientY: y }] });
it("switches tab on touchend and cancels the trailing click", () => {
const onTabChange = vi.fn();
render(<BottomNav activeTab="Packets" onTabChange={onTabChange} />);
const notPrevented = tapEnd(screen.getByRole("tab", { name: "Channels" }));
expect(onTabChange).toHaveBeenCalledTimes(1);
expect(onTabChange).toHaveBeenCalledWith("Channels");
expect(notPrevented).toBe(false);
});
it("ignores a touch that lifts off the button", () => {
const onTabChange = vi.fn();
render(<BottomNav activeTab="Packets" onTabChange={onTabChange} />);
tapEnd(screen.getByRole("tab", { name: "Channels" }), 50, 50);
expect(onTabChange).not.toHaveBeenCalled();
});
it("opens More and picks an overflow tab by touch", () => {
const onTabChange = vi.fn();
render(<BottomNav activeTab="Packets" onTabChange={onTabChange} />);
tapEnd(screen.getByText("More").closest("button")!);
tapEnd(screen.getByRole("menuitem", { name: "Routes" }));
expect(onTabChange).toHaveBeenCalledWith("Routes");
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
});
});