mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-10-18 11:10:15 +00:00
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.
83 lines
4.0 KiB
TypeScript
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();
|
|
});
|
|
});
|