mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-09-02 17:23:45 +00:00
99 lines
3.9 KiB
TypeScript
99 lines
3.9 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
import { render, screen, fireEvent } from "@testing-library/react";
|
|
import { ResolvedHopBlock } from "../../../src/features/packets/PathData";
|
|
import type { ResolvedHop } from "../../../src/types/api";
|
|
|
|
// mobile/touch == no hover-capable pointer; desktop == has hover. Interaction modality keys off
|
|
// (hover: hover), not viewport width.
|
|
function setMobile(mobile: boolean) {
|
|
window.matchMedia = vi.fn().mockImplementation((query: string) => ({
|
|
matches: /hover: hover/.test(query) ? !mobile : mobile,
|
|
media: query,
|
|
onchange: null,
|
|
addEventListener: vi.fn(),
|
|
removeEventListener: vi.fn(),
|
|
addListener: vi.fn(),
|
|
removeListener: vi.fn(),
|
|
dispatchEvent: vi.fn(),
|
|
})) as unknown as typeof window.matchMedia;
|
|
}
|
|
|
|
afterEach(() => vi.restoreAllMocks());
|
|
|
|
const singleHop: ResolvedHop = {
|
|
confidence: "high",
|
|
nodes: [{ id: "node-1", name: "Repeater A", publicKey: "deadbeefcafe" }],
|
|
};
|
|
|
|
describe("ResolvedHopBlock per-hop SNR", () => {
|
|
it("shows the formatted SNR in the popover when the hop carries one", () => {
|
|
setMobile(true);
|
|
render(<ResolvedHopBlock hop={{ ...singleHop, snr: 10.75 }} label="ABC1" />);
|
|
fireEvent.click(screen.getByText("ABC1"));
|
|
expect(screen.getByText(/SNR/)).toBeInTheDocument();
|
|
expect(screen.getByText("10.75")).toBeInTheDocument();
|
|
});
|
|
|
|
it("shows no SNR line when the hop has none", () => {
|
|
setMobile(true);
|
|
render(<ResolvedHopBlock hop={singleHop} label="ABC1" />);
|
|
fireEvent.click(screen.getByText("ABC1"));
|
|
expect(screen.queryByText(/SNR/)).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("ResolvedHopBlock (desktop)", () => {
|
|
it("opens the node directly when the single-match block is clicked", () => {
|
|
setMobile(false);
|
|
const onViewNode = vi.fn();
|
|
render(<ResolvedHopBlock hop={singleHop} label="ABC1" onViewNode={onViewNode} />);
|
|
fireEvent.click(screen.getByText("ABC1"));
|
|
expect(onViewNode).toHaveBeenCalledWith("node-1");
|
|
});
|
|
});
|
|
|
|
describe("ResolvedHopBlock (mobile)", () => {
|
|
it("reveals the name popover on tap instead of navigating", () => {
|
|
setMobile(true);
|
|
const onViewNode = vi.fn();
|
|
render(<ResolvedHopBlock hop={singleHop} label="ABC1" onViewNode={onViewNode} />);
|
|
|
|
fireEvent.click(screen.getByText("ABC1"));
|
|
// popover shows the resolved name, and we did NOT jump straight into the node
|
|
expect(screen.getByText("Repeater A")).toBeInTheDocument();
|
|
expect(onViewNode).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("opens the node when the name in the popover is tapped", () => {
|
|
setMobile(true);
|
|
const onViewNode = vi.fn();
|
|
render(<ResolvedHopBlock hop={singleHop} label="ABC1" onViewNode={onViewNode} />);
|
|
|
|
fireEvent.click(screen.getByText("ABC1"));
|
|
fireEvent.click(screen.getByText("Repeater A"));
|
|
expect(onViewNode).toHaveBeenCalledWith("node-1");
|
|
});
|
|
|
|
// A touch device wider than the mobile breakpoint (a tablet, or Chrome's device emulation, which
|
|
// reports no hover even at tablet widths) must still tap-to-toggle — not hover — so the popover
|
|
// doesn't dismiss before the resolved name can be tapped. Modality keys off (hover: hover), not width.
|
|
it("taps to open on a hover-less device even at desktop width", () => {
|
|
window.matchMedia = vi.fn().mockImplementation((query: string) => ({
|
|
matches: false, // no hover, and not below the mobile width
|
|
media: query,
|
|
onchange: null,
|
|
addEventListener: vi.fn(),
|
|
removeEventListener: vi.fn(),
|
|
addListener: vi.fn(),
|
|
removeListener: vi.fn(),
|
|
dispatchEvent: vi.fn(),
|
|
})) as unknown as typeof window.matchMedia;
|
|
const onViewNode = vi.fn();
|
|
render(<ResolvedHopBlock hop={singleHop} label="ABC1" onViewNode={onViewNode} />);
|
|
|
|
fireEvent.click(screen.getByText("ABC1"));
|
|
expect(screen.getByText("Repeater A")).toBeInTheDocument();
|
|
expect(onViewNode).not.toHaveBeenCalled();
|
|
});
|
|
});
|