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

Sidebar.shortcuts.browser.test.tsx

104 lines4.6 KB

1import { createMemoryHistory } from "@tanstack/react-router";
2import { scopeThreadRef } from "@t3tools/client-runtime/environment";
3import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings";
4import type * as Context from "effect/Context";
5import { expect, it, vi } from "vite-plus/test";
6import { page, userEvent } from "vite-plus/test/browser";
7​
8import { AppRoot } from "~/AppRoot";
9// The thread route loads ChatView lazily; its first transform would eat the test's timeout.
10import "~/components/ChatView";
11import { __setClientSettingsForTests } from "~/hooks/useSettings";
12import { useRightPanelStore } from "~/rightPanelStore";
13import { getRouter } from "~/router";
14import {
15 ENVIRONMENT_ID,
16 installFakeEnvironment,
17 threadWithHistory,
18 type FakeDriver,
19} from "../../test/fakeEnvironment";
20import { act, render } from "../../test/render";
21​
22// The socket is the only fake layer; see test/appScenarios.renders.tsx.
23vi.mock("../../../../packages/client-runtime/src/connection/driver.ts", async (importOriginal) => {
24 const [actual, { fakeDriver }, Layer] = await Promise.all([
25 importOriginal<{ readonly ConnectionDriver: Context.Key<unknown, FakeDriver> }>(),
26 import("../../test/fakeEnvironment"),
27 import("effect/Layer"),
28 ]);
29 return { ...actual, layer: Layer.succeed(actual.ConnectionDriver, fakeDriver) };
30});
31​
32vi.mock(import("~/rpc/requestLatencyState"), async (original) => ({
33 ...(await original()),
34 trackRpcRequestSent: () => {},
35}));
36​
37vi.mock(import("~/state/server"), async (original) => {
38 const actual = await original();
39 const { Atom } = await import("effect/reactivity");
40 const { compileResolvedKeybindingsConfig } = await import("@t3tools/shared/keybindings");
41 const keybindings = compileResolvedKeybindingsConfig([
42 { key: "mod+shift+]", command: "thread.next", when: "!previewFocus && !previewOpen" },
43 { key: "mod+1", command: "thread.jump.1", when: "!previewFocus" },
44 ]);
45 return { ...actual, primaryServerKeybindingsAtom: Atom.make(keybindings) };
46});
47​
48it("leaves thread shortcuts and their hints to the preview when their rules exclude it", async () => {
49 const [first, second] = [threadWithHistory("thread-a", 1), threadWithHistory("thread-b", 1)];
50 using _environment = installFakeEnvironment([first, second]);
51 using _platform = vi.spyOn(navigator, "platform", "get").mockReturnValue("Linux x86_64");
52 __setClientSettingsForTests({
53 ...DEFAULT_CLIENT_SETTINGS,
54 onboardingCompletedAt: "2026-01-01T00:00:00.000Z",
55 });
56 await page.viewport(1400, 900);
57 const router = getRouter(
58 createMemoryHistory({ initialEntries: [`/${ENVIRONMENT_ID}/${first.id}`] }),
59 );
60 await router.load();
61 await using _app = await render(<AppRoot router={router} />, {
62 // AppRoot brings its own router.
63 router: false,
64 });
65 await expect.element(page.getByText(`Thread ${second.id}`).first()).toBeVisible();
66​
67 const previewChrome = document.createElement("div");
68 previewChrome.dataset.previewPanelMode = "browser";
69 previewChrome.style.cssText = "position: fixed; top: 0; left: 0; z-index: 10000";
70 previewChrome.innerHTML = '<button type="button">Reload</button>';
71 document.body.append(previewChrome);
72 using _chrome = { [Symbol.dispose]: () => previewChrome.remove() };
73​
74 const press = () => userEvent.keyboard("{Control>}{Shift>}]{/Shift}{/Control}");
75 const jumpHint = () => document.querySelector("[data-jump-hint]");
76 const whileHoldingControl = async (assertJumpHints: () => void) => {
77 // Every deadline reads as due on subscribe, so hints skip the hold delay.
78 let now = Date.parse("2026-01-01T00:00:00.000Z");
79 using _clock = vi.spyOn(Date, "now").mockImplementation(() => (now += 1000));
80 await userEvent.keyboard("{Control>}");
81 await act(() => {});
82 await vi.waitFor(assertJumpHints);
83 await userEvent.keyboard("{/Control}");
84 };
85 const firstPath = `/${ENVIRONMENT_ID}/${first.id}`;
86​
87 await userEvent.click(page.getByRole("button", { name: "Reload" }));
88 await press();
89 expect(router.history.location.pathname).toBe(firstPath);
90 await whileHoldingControl(() => expect(jumpHint()).toBeNull());
91​
92 const firstRef = scopeThreadRef(ENVIRONMENT_ID, first.id);
93 await act(() => useRightPanelStore.getState().openBrowser(firstRef, null));
94 if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
95 await press();
96 expect(router.history.location.pathname).toBe(firstPath);
97​
98 await act(() => useRightPanelStore.getState().close(firstRef));
99 await press();
100 await vi.waitFor(() =>
101 expect(router.history.location.pathname).toBe(`/${ENVIRONMENT_ID}/${second.id}`),
102 );
103 await whileHoldingControl(() => expect(jumpHint()).not.toBeNull());
104});