feat(webui): polish model preset interaction
This commit is contained in:
@@ -571,6 +571,53 @@ describe("ThreadComposer", () => {
|
||||
expect(screen.queryByText(/Enter to send/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows model details in the shared tooltip without a native title", async () => {
|
||||
render(
|
||||
<ThreadComposer
|
||||
onSend={vi.fn()}
|
||||
modelLabel="gpt-4o"
|
||||
modelDetail="gpt-4o"
|
||||
modelProvider="openai"
|
||||
modelProviderLabel="OpenAI"
|
||||
placeholder="Type your message..."
|
||||
/>,
|
||||
);
|
||||
|
||||
const badge = screen.getByLabelText("gpt-4o");
|
||||
expect(badge).not.toHaveAttribute("title");
|
||||
fireEvent.focus(badge);
|
||||
|
||||
expect(await screen.findByRole("tooltip")).toHaveTextContent("gpt-4o · OpenAI");
|
||||
});
|
||||
|
||||
it("smoothly cycles to the next preset on click", () => {
|
||||
vi.useFakeTimers();
|
||||
let runFrame: FrameRequestCallback | null = null;
|
||||
vi.spyOn(window, "requestAnimationFrame").mockImplementation((callback) => {
|
||||
runFrame = callback;
|
||||
return 1;
|
||||
});
|
||||
vi.spyOn(window, "cancelAnimationFrame").mockImplementation(() => undefined);
|
||||
const { badge, onPresetChange } = renderPresetComposer();
|
||||
|
||||
fireEvent.click(badge);
|
||||
|
||||
expect(badge).toHaveAttribute("data-switching", "true");
|
||||
const track = screen.getByTestId("composer-model-pill-track");
|
||||
expect(track).not.toHaveAttribute("data-settling");
|
||||
expect(track).toHaveStyle({ transform: "translate3d(0, -40px, 0)" });
|
||||
|
||||
act(() => runFrame?.(16));
|
||||
|
||||
expect(onPresetChange).toHaveBeenCalledWith("dflash");
|
||||
expect(badge).toHaveAttribute("data-settling", "true");
|
||||
expect(track).toHaveAttribute("data-settling", "true");
|
||||
expect(track).toHaveStyle({ transform: "translate3d(0, -80px, 0)" });
|
||||
|
||||
act(() => vi.advanceTimersByTime(260));
|
||||
expect(badge).not.toHaveAttribute("data-switching");
|
||||
});
|
||||
|
||||
it("scrolls complete preset pills after a left-button long press and wraps", () => {
|
||||
vi.useFakeTimers();
|
||||
const { badge, onPresetChange } = renderPresetComposer();
|
||||
@@ -582,7 +629,6 @@ describe("ThreadComposer", () => {
|
||||
});
|
||||
badge.dispatchEvent(idleTouchMove);
|
||||
expect(idleTouchMove.defaultPrevented).toBe(false);
|
||||
fireEvent.click(badge);
|
||||
pointerDown(badge);
|
||||
fireEvent.pointerMove(badge, { clientY: 80, pointerId: 7, pointerType: "mouse" });
|
||||
act(() => vi.advanceTimersByTime(500));
|
||||
@@ -639,6 +685,8 @@ describe("ThreadComposer", () => {
|
||||
});
|
||||
|
||||
expect(onPresetChange).toHaveBeenCalledWith("dspro");
|
||||
fireEvent.click(badge);
|
||||
expect(onPresetChange).toHaveBeenCalledTimes(1);
|
||||
expect(badge).toHaveAttribute("data-settling", "true");
|
||||
expect(track).toHaveAttribute("data-settling", "true");
|
||||
act(() => {
|
||||
|
||||
@@ -609,8 +609,12 @@ describe("ThreadShell", () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(await screen.findByTitle("fast · gpt-5.5 · OpenAI Codex")).toBeInTheDocument();
|
||||
expect(screen.queryByTitle("Default · deepseek-v4-pro · DeepSeek")).not.toBeInTheDocument();
|
||||
const badge = await screen.findByLabelText("fast");
|
||||
expect(badge).not.toHaveAttribute("title");
|
||||
fireEvent.focus(badge);
|
||||
expect(await screen.findByRole("tooltip")).toHaveTextContent(
|
||||
"fast · gpt-5.5 · OpenAI Codex",
|
||||
);
|
||||
});
|
||||
|
||||
it("switches through every named preset while preserving call-order priority", async () => {
|
||||
@@ -692,7 +696,11 @@ describe("ThreadShell", () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(await screen.findByTitle("fast · gpt-4 · Company Proxy")).toBeInTheDocument();
|
||||
const badge = await screen.findByLabelText("fast");
|
||||
fireEvent.focus(badge);
|
||||
expect(await screen.findByRole("tooltip")).toHaveTextContent(
|
||||
"fast · gpt-4 · Company Proxy",
|
||||
);
|
||||
expect(screen.queryByRole("button", { name: "Model not configured" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -740,11 +748,11 @@ describe("ThreadShell", () => {
|
||||
expect(screen.getByText("Default")).toBeInTheDocument();
|
||||
expect(screen.queryByText("deepseek-chat")).not.toBeInTheDocument();
|
||||
expect(badge).toHaveAttribute("data-fallback", "true");
|
||||
expect(badge).toHaveAttribute(
|
||||
"title",
|
||||
"deepseek/deepseek-chat",
|
||||
);
|
||||
expect(badge).not.toHaveAttribute("title");
|
||||
expect(logo).not.toHaveAttribute("data-fallback");
|
||||
const trigger = screen.getByLabelText("Default");
|
||||
fireEvent.focus(trigger);
|
||||
expect(await screen.findByRole("tooltip")).toHaveTextContent("deepseek/deepseek-chat");
|
||||
|
||||
act(() => {
|
||||
client._emitChat("fallback-model", {
|
||||
@@ -758,9 +766,9 @@ describe("ThreadShell", () => {
|
||||
screen.getByTestId("composer-model-logo-openai_codex").parentElement,
|
||||
).not.toHaveAttribute("data-fallback");
|
||||
});
|
||||
expect(
|
||||
screen.getByTestId("composer-model-logo-openai_codex").parentElement,
|
||||
).toHaveAttribute("title", "Default · gpt-5.5 · OpenAI Codex");
|
||||
expect(screen.getByRole("tooltip")).toHaveTextContent(
|
||||
"Default · gpt-5.5 · OpenAI Codex",
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId("composer-model-logo-openai_codex").parentElement,
|
||||
).toBe(badge);
|
||||
|
||||
Reference in New Issue
Block a user