fix(app): preserve file search results while loading (#43836)
This commit is contained in:
@@ -60,6 +60,39 @@ test("keeps the file-browser sidebar mounted when switching file tabs", async ({
|
|||||||
await expect.poll(() => viewport.evaluate((element) => element.scrollTop)).toBe(scrolled)
|
await expect.poll(() => viewport.evaluate((element) => element.scrollTop)).toBe(scrolled)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("keeps previous file search results visible while the next search loads", async ({ page }) => {
|
||||||
|
const searchPending = Promise.withResolvers<void>()
|
||||||
|
await setup(page, async ({ query }) => {
|
||||||
|
if (query === "file-0") return ["file-00.ts"]
|
||||||
|
if (query === "file-7") {
|
||||||
|
await searchPending.promise
|
||||||
|
return ["file-79.ts"]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.goto(`/server/${base64Encode(server)}/session/${sessionID}`)
|
||||||
|
await expectSessionTitle(page, title)
|
||||||
|
|
||||||
|
const panel = page.locator("#review-panel")
|
||||||
|
await panel.getByRole("button", { name: "Open file" }).click()
|
||||||
|
const filter = panel.getByRole("combobox", { name: "Filter files" })
|
||||||
|
await filter.fill("file-0")
|
||||||
|
await expect(panel.getByRole("option", { name: "file-00.ts" })).toBeVisible()
|
||||||
|
|
||||||
|
const nextSearch = page.waitForRequest((request) => {
|
||||||
|
const url = new URL(request.url())
|
||||||
|
return url.pathname === "/find/file" && url.searchParams.get("query") === "file-7"
|
||||||
|
})
|
||||||
|
await filter.fill("file-7")
|
||||||
|
await nextSearch
|
||||||
|
await expect(panel.getByRole("option", { name: "file-00.ts" })).toBeVisible()
|
||||||
|
|
||||||
|
searchPending.resolve()
|
||||||
|
await expect(panel.getByRole("option", { name: "file-79.ts" })).toBeVisible()
|
||||||
|
await expect(panel.getByRole("option", { name: "file-00.ts" })).toBeHidden()
|
||||||
|
})
|
||||||
|
|
||||||
type Probed = HTMLElement & { __e2eProbe?: string }
|
type Probed = HTMLElement & { __e2eProbe?: string }
|
||||||
|
|
||||||
async function writeProbe(page: Page) {
|
async function writeProbe(page: Page) {
|
||||||
@@ -74,7 +107,10 @@ async function readProbe(page: Page) {
|
|||||||
.evaluate((el) => (el as Probed).__e2eProbe)
|
.evaluate((el) => (el as Probed).__e2eProbe)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setup(page: Page) {
|
async function setup(
|
||||||
|
page: Page,
|
||||||
|
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown | Promise<unknown>,
|
||||||
|
) {
|
||||||
await mockOpenCodeServer(page, {
|
await mockOpenCodeServer(page, {
|
||||||
directory,
|
directory,
|
||||||
project: {
|
project: {
|
||||||
@@ -119,6 +155,7 @@ async function setup(page: Page) {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
fileContent: (path) => ({ type: "text", content: `contents:${path}` }),
|
fileContent: (path) => ({ type: "text", content: `contents:${path}` }),
|
||||||
|
findFiles,
|
||||||
pageMessages: () => ({ items: [] }),
|
pageMessages: () => ({ items: [] }),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export interface MockServerConfig {
|
|||||||
questions?: unknown[] | (() => unknown[])
|
questions?: unknown[] | (() => unknown[])
|
||||||
fileList?: (path: string) => unknown | Promise<unknown>
|
fileList?: (path: string) => unknown | Promise<unknown>
|
||||||
fileContent?: (path: string) => unknown | Promise<unknown>
|
fileContent?: (path: string) => unknown | Promise<unknown>
|
||||||
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown
|
findFiles?: (input: { query: string; dirs?: string; limit?: number }) => unknown | Promise<unknown>
|
||||||
sessionStatus?: Record<string, unknown> | (() => Record<string, unknown>)
|
sessionStatus?: Record<string, unknown> | (() => Record<string, unknown>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createMemo, createSignal, createUniqueId, Show } from "solid-js"
|
import { createMemo, createSignal, createUniqueId, Show } from "solid-js"
|
||||||
import { createQuery } from "@tanstack/solid-query"
|
import { createQuery, keepPreviousData } from "@tanstack/solid-query"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
import { SessionFilePanelV2, SessionFilePanelV2Empty } from "@opencode-ai/session-ui/v2/session-file-panel-v2"
|
import { SessionFilePanelV2, SessionFilePanelV2Empty } from "@opencode-ai/session-ui/v2/session-file-panel-v2"
|
||||||
import { SessionReviewV2Sidebar } from "@opencode-ai/session-ui/v2/session-review-v2"
|
import { SessionReviewV2Sidebar } from "@opencode-ai/session-ui/v2/session-review-v2"
|
||||||
@@ -50,6 +50,7 @@ export function SessionFileBrowserTab(props: {
|
|||||||
queryKey: ["session-open-file", workspaceKey(), value] as const,
|
queryKey: ["session-open-file", workspaceKey(), value] as const,
|
||||||
enabled: value.length > 0,
|
enabled: value.length > 0,
|
||||||
queryFn: ({ signal }) => file.searchFiles(value, { limit: 200, signal }),
|
queryFn: ({ signal }) => file.searchFiles(value, { limit: 200, signal }),
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const files = createMemo(() => {
|
const files = createMemo(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user