// @vitest-environment jsdom
//
// The `page` scope as App binds it through `useGlobalKeymap`: the `g` chords jump to a module this
// account can reach, stay out of an overlay's way, and leave the global keys (Shift+W, h) intact.
// Its own file for the same reason as keymap.sequence.test.ts: a pending `g` is module state.
import { useEffect } from "react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { act, cleanup, render } from "@testing-library/react";
import { closeKeyboardHelp, useGlobalKeymap, useKeyboardHelpOpen } from "./keymap";
import { pushLayer, removeLayer } from "./layerStack";
import type { Page } from "./urlState";
let clock = Date.UTC(2031, 0, 1);
beforeEach(() => {
vi.useFakeTimers({ toFake: ["Date"] });
clock += 3_600_000;
vi.setSystemTime(clock);
});
afterEach(() => {
cleanup();
closeKeyboardHelp();
vi.useRealTimers();
});
const ALL: Page[] = ["home", "feed", "channels", "playlists", "plex", "files", "settings"];
function Host({
onNavigate,
pages = ALL,
onHelp = () => {},
}: {
onNavigate: (p: Page) => void;
pages?: Page[];
onHelp?: (open: boolean) => void;
}) {
useGlobalKeymap(true, onNavigate, pages);
const help = useKeyboardHelpOpen();
useEffect(() => {
onHelp(help);
}, [help, onHelp]);
return null;
}
const key = (init: KeyboardEventInit) =>
act(() => {
window.dispatchEvent(
new KeyboardEvent("keydown", { bubbles: true, cancelable: true, ...init }),
);
});
describe("the g chords", () => {
it("jump to the module named by the second key", () => {
const nav = vi.fn();
render();
key({ key: "f" });
expect(nav).not.toHaveBeenCalled();
key({ key: "g" });
key({ key: "f" });
key({ key: "g" });
key({ key: "," });
expect(nav.mock.calls).toEqual([["feed"], ["settings"]]);
});
// The retired Downloads page's chord still answers — where its content went.
it("g d, the old Downloads chord, goes to Files", () => {
const nav = vi.fn();
render();
key({ key: "g" });
key({ key: "d" });
expect(nav.mock.calls).toEqual([["files"]]);
});
// `h` alone opens the sheet; the `h` of `g h` must not ALSO do that.
it("take the h of g h away from the help sheet", () => {
const nav = vi.fn();
let help = false;
render( (help = v)} />);
key({ key: "g" });
key({ key: "h" });
expect(nav).toHaveBeenCalledWith("home");
expect(help).toBe(false);
});
it("refuse a module this account cannot reach", () => {
const nav = vi.fn();
render();
key({ key: "g" });
key({ key: "x" });
expect(nav).not.toHaveBeenCalled();
});
it("stand aside for an overlay, while Shift+W still answers there", () => {
const nav = vi.fn();
render();
const id = pushLayer();
key({ key: "g" });
key({ key: "f" });
expect(nav).not.toHaveBeenCalled();
key({ key: "w", shiftKey: true });
expect(nav).toHaveBeenCalledWith("home");
removeLayer(id);
});
});