Polish WebUI streaming and provider settings
This commit is contained in:
@@ -198,8 +198,52 @@ describe("App layout", () => {
|
||||
name: "openrouter",
|
||||
label: "OpenRouter",
|
||||
configured: false,
|
||||
api_key_required: true,
|
||||
default_api_base: "https://openrouter.ai/api/v1",
|
||||
},
|
||||
{
|
||||
name: "azure_openai",
|
||||
label: "Azure OpenAI",
|
||||
configured: false,
|
||||
api_key_required: true,
|
||||
},
|
||||
{
|
||||
name: "huggingface",
|
||||
label: "Hugging Face",
|
||||
configured: false,
|
||||
api_key_required: true,
|
||||
},
|
||||
{
|
||||
name: "siliconflow",
|
||||
label: "SiliconFlow",
|
||||
configured: false,
|
||||
api_key_required: true,
|
||||
},
|
||||
{
|
||||
name: "volcengine",
|
||||
label: "VolcEngine",
|
||||
configured: false,
|
||||
api_key_required: true,
|
||||
},
|
||||
{
|
||||
name: "byteplus",
|
||||
label: "BytePlus",
|
||||
configured: false,
|
||||
api_key_required: true,
|
||||
},
|
||||
{
|
||||
name: "qianfan",
|
||||
label: "Qianfan",
|
||||
configured: false,
|
||||
api_key_required: true,
|
||||
},
|
||||
{
|
||||
name: "atomic_chat",
|
||||
label: "Atomic Chat",
|
||||
configured: false,
|
||||
api_key_required: false,
|
||||
default_api_base: "http://localhost:1337/v1",
|
||||
},
|
||||
],
|
||||
web_search: {
|
||||
provider: "brave",
|
||||
@@ -254,6 +298,9 @@ describe("App layout", () => {
|
||||
fireEvent.click(screen.getByText("OpenAI"));
|
||||
expect(screen.getByText("open••••-key")).toBeInTheDocument();
|
||||
expect(screen.queryByDisplayValue("unsaved-openai-key")).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByText("Atomic Chat"));
|
||||
expect(screen.getByDisplayValue("http://localhost:1337/v1")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Save" })).toBeEnabled();
|
||||
|
||||
fireEvent.click(screen.getByRole("tab", { name: "Web Search" }));
|
||||
expect(screen.getByText("Search provider")).toBeInTheDocument();
|
||||
|
||||
@@ -35,6 +35,18 @@ vi.mock("react-syntax-highlighter/dist/esm/styles/prism/one-light", () => ({
|
||||
}));
|
||||
|
||||
describe("CodeBlock", () => {
|
||||
it("renders plain code without mounting the highlighter when highlighting is disabled", () => {
|
||||
render(
|
||||
<ThemeProvider theme="dark">
|
||||
<CodeBlock language="ts" code="const value = 1;" highlight={false} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("highlighted-code")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("const value = 1;")).toBeInTheDocument();
|
||||
expect(screen.getByText("ts")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("reads theme from context without creating per-block observers", async () => {
|
||||
const originalMutationObserver = globalThis.MutationObserver;
|
||||
const observer = vi.fn();
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { MarkdownText } from "@/components/MarkdownText";
|
||||
|
||||
const rendererSpy = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("@/components/MarkdownTextRenderer", () => ({
|
||||
default: ({
|
||||
children,
|
||||
highlightCode,
|
||||
}: {
|
||||
children: string;
|
||||
highlightCode?: boolean;
|
||||
}) => {
|
||||
rendererSpy({ children, highlightCode });
|
||||
return (
|
||||
<div
|
||||
data-testid="markdown-renderer"
|
||||
data-highlight-code={String(highlightCode)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
describe("MarkdownText", () => {
|
||||
it("throttles streaming markdown commits and flushes before final highlighting", async () => {
|
||||
rendererSpy.mockClear();
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const { rerender } = render(
|
||||
<MarkdownText streaming>hello</MarkdownText>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveTextContent("hello");
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveAttribute(
|
||||
"data-highlight-code",
|
||||
"false",
|
||||
);
|
||||
expect(rendererSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender(<MarkdownText streaming>hello world</MarkdownText>);
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveTextContent("hello");
|
||||
expect(rendererSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(79);
|
||||
});
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveTextContent("hello");
|
||||
expect(rendererSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(1);
|
||||
});
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveTextContent("hello world");
|
||||
expect(rendererSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
rerender(<MarkdownText streaming>hello world!!!</MarkdownText>);
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveTextContent("hello world");
|
||||
|
||||
rerender(<MarkdownText>hello world!!!</MarkdownText>);
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveTextContent("hello world!!!");
|
||||
expect(screen.getByTestId("markdown-renderer")).toHaveAttribute(
|
||||
"data-highlight-code",
|
||||
"true",
|
||||
);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user