Skip to content
Source
Sign inSign up
Open History to see the last commit

CaptureShortcutConfig.browser.test.tsx

122 lines4.6 KB

1import {
2 DEFAULT_CLIENT_SETTINGS,
3 type DesktopCaptureConfigPreview,
4 type DesktopSnapShotState,
5} from "@t3tools/contracts";
6import { userEvent } from "vite-plus/test/browser";
7import { expect, it, vi } from "vite-plus/test";
8​
9import { act, render } from "../../../test/render";
10​
11const bridge = vi.hoisted(() => ({
12 previewSnapShotConfig: vi.fn(),
13 applySnapShotConfig: vi.fn(),
14 setSnapShotShortcutSuppressed: vi.fn(),
15}));
16const toast = vi.hoisted(() => vi.fn());
17vi.mock(import("../../lib/desktopSnapShot"), async (original) => ({
18 ...(await original()),
19 getDesktopSnapShotBridge: () => bridge as never,
20}));
21vi.mock(import("../ui/toast"), async (original) => {
22 const actual = await original();
23 return { ...actual, toastManager: { ...actual.toastManager, add: toast } };
24});
25​
26import { CaptureShortcutConfig } from "./CaptureShortcutConfig";
27​
28const preview: DesktopCaptureConfigPreview = {
29 id: "approved-snapshot",
30 path: "/config/niri/config.kdl",
31 resolvedPath: "/config/niri/config.kdl",
32 before: "binds {\n}\n",
33 after: 'binds {\n Ctrl+Shift+2 { spawn "gdbus"; }\n}\n',
34 shortcut: "Ctrl+Shift+2",
35 operation: "install",
36};
37const complete = vi.fn<() => Promise<void>>();
38​
39function setup() {
40 const platform = vi.spyOn(navigator, "platform", "get").mockReturnValue("Linux");
41 bridge.setSnapShotShortcutSuppressed.mockReset().mockResolvedValue(undefined);
42 bridge.previewSnapShotConfig.mockReset().mockResolvedValue(preview);
43 bridge.applySnapShotConfig
44 .mockReset()
45 .mockResolvedValue({ backupPath: "/config/backup", warning: null });
46 complete.mockReset().mockResolvedValue(undefined);
47 toast.mockClear();
48 return { [Symbol.dispose]: () => platform.mockRestore() };
50​
51async function renderConfig(desktop: "niri" | "hyprland" = "niri") {
52 const state: DesktopSnapShotState = {
53 mode: "portal",
54 linuxBackend: desktop,
55 shortcut: DEFAULT_CLIENT_SETTINGS.snapShotShortcut,
56 shortcutRegistered: false,
57 shortcutActionRegistered: true,
58 shortcutMessage: null,
59 message: null,
60 shortcutConfigPath: preview.path,
61 shortcutBinding: "manual binding",
62 };
63 const screen = await render(<CaptureShortcutConfig state={state} onComplete={complete} />);
64 const { container } = screen;
65 const button = (label: string) =>
66 [...container.querySelectorAll("button")].find((element) => element.textContent === label);
67 const recorder = () => container.querySelector<HTMLButtonElement>("[data-keybinding-capture]")!;
68 return Object.assign(screen, {
69 button,
70 recorder,
71 diff: () => container.querySelector('[aria-label="Shortcut changes"]'),
72 alert: () => container.querySelector('[role="alert"]'),
73 advanced: () => container.querySelector("details")!,
74 async click(label: string) {
75 const target = button(label);
76 expect(target, `button ${label}`).toBeDefined();
77 await act(async () => target!.click());
78 },
79 async recordKeys(keys: string) {
80 await userEvent.click(recorder());
81 await userEvent.keyboard(keys);
82 },
83 });
85​
86it.each(["niri", "hyprland"] as const)(
87 "shows the fallback before review and requires separate read and write approval on %s",
88 async (desktop) => {
89 using _env = setup();
90 await using screen = await renderConfig(desktop);
91 expect(screen.recorder().getAttribute("aria-label")).toBe(
92 "Record snapshot shortcut, currently Ctrl+Shift+2",
93 );
94 expect(screen.advanced().textContent).toContain("manual binding");
95 expect(bridge.previewSnapShotConfig).not.toHaveBeenCalled();
96 await screen.click("Review changes");
97 expect(screen.recorder().getAttribute("aria-label")).toBe(
98 "Record snapshot shortcut, currently Ctrl+Shift+2",
99 );
100 expect(bridge.applySnapShotConfig).not.toHaveBeenCalled();
101 expect(screen.diff()).not.toBeNull();
102 await screen.click("Save shortcut");
103 expect(bridge.applySnapShotConfig).toHaveBeenCalledExactlyOnceWith(preview.id);
104 expect(complete).toHaveBeenCalledOnce();
105 expect(toast).toHaveBeenCalledWith(expect.objectContaining({ type: "success" }));
106 },
107);
108​
109it("withdraws a stale diff and requires another read instead of retrying its write", async () => {
110 using _env = setup();
111 await using screen = await renderConfig();
112 await screen.click("Review changes");
113 bridge.applySnapShotConfig.mockRejectedValue(
114 new Error("Your config changed since this preview."),
115 );
116 await screen.click("Save shortcut");
117 expect(screen.alert()?.textContent).toContain("Review the changes and try again");
118 expect(screen.advanced().textContent).toContain("Your config changed since this preview.");
119 expect(screen.button("Review changes")).toBeDefined();
120 expect(screen.diff()).toBeNull();
121 expect(complete).not.toHaveBeenCalled();
122});