fix(ui): revoke OCR preview URL on unmount

This commit is contained in:
qdivan 2026-07-24 22:33:42 +08:00
parent a0b64aa988
commit 02390f5a4f
3 changed files with 67 additions and 5 deletions

View file

@ -441,6 +441,49 @@ describe("ChatUI", () => {
expect(customProxyInput).toHaveValue(testProxyUrl);
});
it("revokes the active OCR preview URL on unmount", async () => {
const createObjectURL = vi.fn(() => "blob:http://localhost/ocr-preview");
const revokeObjectURL = vi.fn();
URL.createObjectURL = createObjectURL;
URL.revokeObjectURL = revokeObjectURL;
const { getByText, unmount } = render(
<ChatUI
accessToken="1234567890"
token="1234567890"
userRole="user"
userID="1234567890"
disabledPersonalKeyCreation={false}
/>,
);
await waitFor(() => {
expect(getByText("Test Key")).toBeInTheDocument();
});
await selectComboboxOption("Select an endpoint", "/v1/ocr");
await waitFor(() => {
expect(screen.getByText("Click or drag a document or image to upload")).toBeInTheDocument();
});
const uploadInput = screen.getByLabelText(/Click or drag a document or image to upload/);
const imageFile = new File(["ocr"], "ocr.png", { type: "image/png" });
await act(async () => {
fireEvent.change(uploadInput, { target: { files: [imageFile] } });
});
await waitFor(() => {
expect(createObjectURL).toHaveBeenCalledWith(imageFile);
expect(screen.getByAltText("Upload preview")).toBeInTheDocument();
});
unmount();
expect(revokeObjectURL).toHaveBeenCalledWith("blob:http://localhost/ocr-preview");
});
it("should enable search functionality for MCP server selector", async () => {
const user = userEvent.setup();
render(

View file

@ -275,6 +275,24 @@ const ChatUI: React.FC<ChatUIProps> = ({
const codeInterpreter = useCodeInterpreter();
const chatEndRef = useRef<HTMLDivElement>(null);
const ocrFilePreviewUrlRef = useRef<string | null>(null);
const revokeOcrFilePreviewUrl = () => {
if (ocrFilePreviewUrlRef.current) {
URL.revokeObjectURL(ocrFilePreviewUrlRef.current);
}
ocrFilePreviewUrlRef.current = null;
setOcrFilePreviewUrl(null);
};
useEffect(() => {
return () => {
if (ocrFilePreviewUrlRef.current) {
URL.revokeObjectURL(ocrFilePreviewUrlRef.current);
ocrFilePreviewUrlRef.current = null;
}
};
}, []);
// Fetch MCP servers and toolsets
const loadMCPServers = async () => {
@ -704,8 +722,11 @@ const ChatUI: React.FC<ChatUIProps> = ({
};
const handleOcrFileUpload = (file: File): false => {
revokeOcrFilePreviewUrl();
setUploadedOcrFile(file);
setOcrFilePreviewUrl(file.type.startsWith("image/") ? URL.createObjectURL(file) : null);
const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : null;
ocrFilePreviewUrlRef.current = previewUrl;
setOcrFilePreviewUrl(previewUrl);
return false;
};
@ -718,11 +739,8 @@ const ChatUI: React.FC<ChatUIProps> = ({
};
const handleRemoveOcrFile = () => {
if (ocrFilePreviewUrl) {
URL.revokeObjectURL(ocrFilePreviewUrl);
}
setUploadedOcrFile(null);
setOcrFilePreviewUrl(null);
revokeOcrFilePreviewUrl();
};
const handleSendMessage = async () => {

View file

@ -35,6 +35,7 @@ describe("EndpointSelector", () => {
await user.click(combobox);
const input = await screen.findByRole("combobox");
await user.clear(input);
await user.type(input, "ocr");
expect(await screen.findByText("/v1/ocr")).toBeInTheDocument();