From abaa38554fef69596e9d9283961af1411ae2c378 Mon Sep 17 00:00:00 2001 From: Lakshmi Tanmay Date: Tue, 3 Nov 2026 23:05:32 +0530 Subject: [PATCH 1/2] fix(web): keep sidebar search results when opening threads --- .../Sidebar.search.browser.test.tsx | 83 +++++++++++++++++++ apps/web/src/components/Sidebar.tsx | 1 - 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/components/Sidebar.search.browser.test.tsx diff --git a/apps/web/src/components/Sidebar.search.browser.test.tsx b/apps/web/src/components/Sidebar.search.browser.test.tsx new file mode 100644 index 000000000000..db2189de5a80 --- /dev/null +++ b/apps/web/src/components/Sidebar.search.browser.test.tsx @@ -0,0 +1,83 @@ +import { createMemoryHistory } from "@tanstack/react-router"; +import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings"; +import type * as Context from "effect/Context"; +import { expect, it, vi } from "vite-plus/test"; +import { page, userEvent } from "vite-plus/test/browser"; + +import "~/index.css"; + +import { AppRoot } from "~/AppRoot"; +import { __setClientSettingsForTests } from "~/hooks/useSettings"; +import * as primaryAuth from "~/environments/primary/auth"; +import { getRouter } from "~/router"; +import { + ENVIRONMENT_ID, + installFakeEnvironment, + threadWithHistory, + type FakeDriver, +} from "../../test/fakeEnvironment"; +import { render } from "../../test/render"; + +vi.mock("~/environments/primary/auth", { spy: true }); +vi.mock("~/components/ThreadRouteView", () => ({ ThreadRouteView: () => null })); + +vi.mock("../../../../packages/client-runtime/src/connection/driver.ts", async (importOriginal) => { + const [actual, { fakeDriver }, Layer] = await Promise.all([ + importOriginal<{ readonly ConnectionDriver: Context.Key }>(), + import("../../test/fakeEnvironment"), + import("effect/Layer"), + ]); + return { ...actual, layer: Layer.succeed(actual.ConnectionDriver, fakeDriver) }; +}); + +it("keeps sidebar search results while opening threads until explicitly cleared", async () => { + const first = threadWithHistory("search-first", 1); + const second = threadWithHistory("search-second", 1); + using _environment = installFakeEnvironment([first, second]); + __setClientSettingsForTests({ + ...DEFAULT_CLIENT_SETTINGS, + onboardingCompletedAt: "2026-01-01T00:00:00.000Z", + }); + using _auth = vi + .spyOn(primaryAuth, "resolveInitialServerAuthGateState") + .mockResolvedValue({ status: "authenticated" }); + await page.viewport(1400, 900); + const router = getRouter( + createMemoryHistory({ initialEntries: [`/${ENVIRONMENT_ID}/${second.id}`] }), + ); + await router.load(); + // AppRoot owns the application router. + await using screen = await render(, { router: false }); + const search = screen.getByRole("combobox", { name: "Search threads" }); + const results = screen.getByRole("listbox", { name: "Thread search results" }); + await vi.waitFor(() => { + expect(router.state.location.pathname).toBe(`/${ENVIRONMENT_ID}/${second.id}`); + }); + await userEvent.fill(search, "search-"); + await expect.element(results.getByRole("option")).toHaveLength(2); + + await results.getByRole("option", { name: new RegExp(first.title) }).click(); + await vi.waitFor(() => { + expect(router.state.location.pathname).toBe(`/${ENVIRONMENT_ID}/${first.id}`); + }); + await expect.element(search).toHaveValue("search-"); + await expect.element(results.getByRole("option")).toHaveLength(2); + + await search.click(); + await userEvent.keyboard("{ArrowDown}{Enter}"); + await vi.waitFor(() => { + expect(router.state.location.pathname).toBe(`/${ENVIRONMENT_ID}/${second.id}`); + }); + await expect.element(search).toHaveValue("search-"); + await expect.element(results.getByRole("option")).toHaveLength(2); + + await screen.getByRole("button", { name: "Clear thread search" }).click(); + await expect.element(search).toHaveValue(""); + await expect.element(results).not.toBeInTheDocument(); + + await userEvent.fill(search, "search-"); + await expect.element(results.getByRole("option")).toHaveLength(2); + await userEvent.keyboard("{Escape}"); + await expect.element(search).toHaveValue(""); + await expect.element(results).not.toBeInTheDocument(); +}); diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index a94b9e9497c6..c9fbb577821b 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -1515,7 +1515,6 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve threadSearchMatchKey({ environmentId: thread.environmentId, threadId: thread.id }), ); const query = threadSearch.query; - clearThreadSearch(); navigateToThread(ref); if (match) openThreadFindOnQuery(scopedThreadKey(ref), findForMessageMatch(match, query)); }; From d51a9782c48ceb88268526e70cb15c9c484a0513 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 11 Nov 2026 03:25:58 +0000 Subject: [PATCH 2/2] test(web): focus sidebar search regression on result selection AI-assisted; verified with the tests and tools listed above. --- apps/web/src/components/Sidebar.search.browser.test.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/web/src/components/Sidebar.search.browser.test.tsx b/apps/web/src/components/Sidebar.search.browser.test.tsx index db2189de5a80..ef47f104d172 100644 --- a/apps/web/src/components/Sidebar.search.browser.test.tsx +++ b/apps/web/src/components/Sidebar.search.browser.test.tsx @@ -30,7 +30,7 @@ vi.mock("../../../../packages/client-runtime/src/connection/driver.ts", async (i return { ...actual, layer: Layer.succeed(actual.ConnectionDriver, fakeDriver) }; }); -it("keeps sidebar search results while opening threads until explicitly cleared", async () => { +it("keeps sidebar search results while opening threads", async () => { const first = threadWithHistory("search-first", 1); const second = threadWithHistory("search-second", 1); using _environment = installFakeEnvironment([first, second]); @@ -74,10 +74,4 @@ it("keeps sidebar search results while opening threads until explicitly cleared" await screen.getByRole("button", { name: "Clear thread search" }).click(); await expect.element(search).toHaveValue(""); await expect.element(results).not.toBeInTheDocument(); - - await userEvent.fill(search, "search-"); - await expect.element(results.getByRole("option")).toHaveLength(2); - await userEvent.keyboard("{Escape}"); - await expect.element(search).toHaveValue(""); - await expect.element(results).not.toBeInTheDocument(); });