fix(webui): validate inferred file paths before preview (#4935)

This commit is contained in:
chengyongru
2026-07-15 10:45:40 +08:00
committed by GitHub
parent aa70aa48f9
commit 5ed28a6744
10 changed files with 461 additions and 51 deletions
+53
View File
@@ -231,6 +231,59 @@ describe("ThreadShell", () => {
);
});
it("keeps inferred file paths non-interactive when the availability probe fails", async () => {
const client = makeClient();
let resolveProbe!: (value: Response) => void;
const probe = new Promise<Response>((resolve) => {
resolveProbe = resolve;
});
const fetchMock = vi.fn((input: RequestInfo | URL) => {
const url = String(input);
if (url.includes("websocket%3Apreview-error/webui-thread")) {
return Promise.resolve(httpJson(transcriptFromSimpleMessages([
{ role: "assistant", content: "Unreadable file: `prompts/dream.md`" },
])));
}
if (url.includes("websocket%3Apreview-error/file-preview?")) return probe;
return Promise.resolve({
ok: false,
status: 404,
json: async () => ({}),
});
});
vi.stubGlobal("fetch", fetchMock);
render(wrap(
client,
<ThreadShell
session={session("preview-error")}
title="Preview error"
onToggleSidebar={() => {}}
/>,
));
const reference = await screen.findByTestId("inline-file-path");
await waitFor(() => expect(fetchMock).toHaveBeenCalledWith(
expect.stringContaining("file-preview?path=prompts%2Fdream.md&probe=1"),
expect.anything(),
));
await act(async () => {
resolveProbe({
ok: false,
status: 500,
text: async () => "failed to read file",
json: async () => ({}),
} as Response);
await probe;
await Promise.resolve();
});
expect(reference).not.toHaveAttribute("role");
expect(reference).not.toHaveAttribute("tabindex");
fireEvent.click(reference);
expect(screen.queryByText("failed to read file")).not.toBeInTheDocument();
});
it("does not navigate away when clicking the chat title", async () => {
const client = makeClient();
const onGoHome = vi.fn();