refactor(webui): improve visual consistency (#5249)
This commit is contained in:
@@ -509,6 +509,9 @@ describe("App layout", () => {
|
||||
expect(screen.getByText("cron")).toBeInTheDocument();
|
||||
expect(screen.getByText("github")).toBeInTheDocument();
|
||||
expect(screen.getByText("Needs setup")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Review the instruction skills this agent can load during a conversation."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("navigation", { name: "Sidebar navigation" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("navigation", { name: "Settings sections" })).not.toBeInTheDocument();
|
||||
expect(within(sidebar).getByRole("button", { name: "Skills" })).toHaveAttribute(
|
||||
@@ -534,6 +537,11 @@ describe("App layout", () => {
|
||||
"true",
|
||||
);
|
||||
expect(screen.getByText("Setup required")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
"Allow the agent to load this skill when its requirements are ready.",
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByText("brew install gh")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Unavailable reason")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Missing CLI")).not.toBeInTheDocument();
|
||||
@@ -737,6 +745,9 @@ describe("App layout", () => {
|
||||
expect(
|
||||
await screen.findByRole("heading", { name: "Trending by marketplace" }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Each marketplace keeps its own ranking and install metrics."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByText("find-skills")).toBeInTheDocument();
|
||||
expect(screen.getByText("ima-skills")).toBeInTheDocument();
|
||||
expect(screen.getAllByText("SkillHub")).toHaveLength(2);
|
||||
@@ -1994,6 +2005,11 @@ describe("App layout", () => {
|
||||
fireEvent.click(await screen.findByRole("menuitem", { name: "Appearance" }));
|
||||
expect(screen.getByText("Brand logos")).toBeInTheDocument();
|
||||
expect(screen.getByRole("switch", { name: "Brand logos" })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Switch between light and dark appearance."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Choose the language used by the WebUI.")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Stored only in this browser.")).not.toBeInTheDocument();
|
||||
expect(within(settingsNav).getByRole("button", { name: "Settings: Appearance" })).toBeInTheDocument();
|
||||
fireEvent.pointerDown(within(settingsNav).getByRole("button", { name: "Settings: Appearance" }));
|
||||
fireEvent.click(await screen.findByRole("menuitem", { name: "Models" }));
|
||||
@@ -2079,6 +2095,14 @@ describe("App layout", () => {
|
||||
expect(screen.getByRole("button", { name: "openai/gpt-5.4-image-2" })).toBeInTheDocument();
|
||||
expect(screen.getByText("Save directory")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
"Expose generate_image in chats when a configured image provider is available.",
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Choose a model supported by the selected image provider."),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(within(settingsNav).getByRole("button", { name: "Web" }));
|
||||
expect(screen.getByText("Search provider")).toBeInTheDocument();
|
||||
@@ -2086,6 +2110,12 @@ describe("App layout", () => {
|
||||
expect(screen.getByRole("button", { name: /Brave Search/ })).toBeInTheDocument();
|
||||
expect(screen.getByTestId("provider-picker-logo-brave")).toBeInTheDocument();
|
||||
expect(screen.getByText("BSAo••••ew20")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Choose the backend used by the web search tool."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Results returned by each web_search call."),
|
||||
).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Edit" }));
|
||||
fireEvent.change(screen.getByPlaceholderText("Leave blank to keep the current key"), {
|
||||
target: { value: "unsaved-brave-key" },
|
||||
@@ -2098,8 +2128,14 @@ describe("App layout", () => {
|
||||
expect(screen.queryByDisplayValue("unsaved-brave-key")).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(within(settingsNav).getByRole("button", { name: "System" }));
|
||||
expect(screen.getByText("Regional")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Regional")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("Timezone")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Used for schedules and time-aware replies."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Restart nanobot to apply runtime changes."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Bot name")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Bot icon")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Tool hint length")).not.toBeInTheDocument();
|
||||
@@ -2107,16 +2143,15 @@ describe("App layout", () => {
|
||||
expect(screen.queryByText("Dream")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Unified session")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("Default workspace")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();
|
||||
fireEvent.click(screen.getByRole("button", { name: "UTC" }));
|
||||
const timezoneSearch = await screen.findByPlaceholderText("Search timezone");
|
||||
expect(timezoneSearch).toBeInTheDocument();
|
||||
fireEvent.change(timezoneSearch, {
|
||||
target: { value: "Shanghai" },
|
||||
});
|
||||
await user.click(screen.getByRole("option", { name: /Asia\/Shanghai/ }));
|
||||
expect(screen.getByRole("button", { name: "Asia/Shanghai" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Save" })).toBeEnabled();
|
||||
expect(screen.getByText("UTC")).toBeInTheDocument();
|
||||
expect(screen.queryByPlaceholderText("Search timezone")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("listbox", { name: "Select timezone" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "UTC" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Save" })).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Connect SDKs and agents through a local /v1 endpoint."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("The API uses this local port.")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("restores the settings section from the URL hash after a page reload", async () => {
|
||||
@@ -2130,6 +2165,41 @@ describe("App layout", () => {
|
||||
expect(window.location.hash).toBe("#/settings?section=voice");
|
||||
});
|
||||
|
||||
it("keeps the backend timezone without writing settings on mount", async () => {
|
||||
const initialSettings = baseSettingsPayload();
|
||||
mockFetchRoutes({
|
||||
"/api/settings": initialSettings,
|
||||
});
|
||||
const fetchMock = vi.mocked(fetch);
|
||||
window.history.replaceState(null, "", "/#/settings?section=runtime");
|
||||
|
||||
render(<App />);
|
||||
|
||||
expect(await screen.findByText("UTC")).toBeInTheDocument();
|
||||
expect(
|
||||
fetchMock.mock.calls.filter(([input]) =>
|
||||
String(input).startsWith("/api/settings/update?timezone="),
|
||||
),
|
||||
).toHaveLength(0);
|
||||
expect(screen.queryByRole("heading", { name: "Regional" })).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Used for schedules and time-aware replies."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Save" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByPlaceholderText("Search timezone")).not.toBeInTheDocument();
|
||||
const systemSection = screen.getByRole("heading", { name: "System" }).closest("section");
|
||||
expect(systemSection).not.toBeNull();
|
||||
const system = within(systemSection as HTMLElement);
|
||||
const timezoneLabel = system.getByText("Timezone");
|
||||
const restartButton = system.getByRole("button", { name: "Restart nanobot" });
|
||||
expect(
|
||||
timezoneLabel.compareDocumentPosition(restartButton) & Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
system.queryByText("Restart nanobot to apply runtime changes."),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("falls back to Overview for the retired Files settings URL", async () => {
|
||||
mockFetchRoutes({ "/api/settings": baseSettingsPayload() });
|
||||
window.history.replaceState(null, "", "/#/settings?section=files");
|
||||
@@ -2211,6 +2281,7 @@ describe("App layout", () => {
|
||||
fireEvent.click(appsButton);
|
||||
|
||||
expect(await screen.findByRole("heading", { name: "Apps" })).toBeInTheDocument();
|
||||
expect(screen.queryByText("Add tools to nanobot, then @ them in chat.")).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("navigation", { name: "Sidebar navigation" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("navigation", { name: "Settings sections" })).not.toBeInTheDocument();
|
||||
expect(within(sidebar).getByRole("button", { name: "Apps" })).toHaveAttribute(
|
||||
|
||||
@@ -113,6 +113,38 @@ describe("MessageBubble", () => {
|
||||
expect(screen.queryByRole("button", { name: "Fork" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not replay an entrance animation when persisted messages mount", () => {
|
||||
const messages: UIMessage[] = [
|
||||
{
|
||||
id: "u-persisted",
|
||||
role: "user",
|
||||
content: "Earlier question",
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: "a-persisted",
|
||||
role: "assistant",
|
||||
content: "Earlier answer",
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: "t-persisted",
|
||||
role: "tool",
|
||||
kind: "trace",
|
||||
content: "Earlier tool call",
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
];
|
||||
|
||||
for (const message of messages) {
|
||||
const { container, unmount } = render(<MessageBubble message={message} />);
|
||||
for (const className of ["animate-in", "fade-in-0", "slide-in-from-bottom-1"]) {
|
||||
expect(container.firstElementChild).not.toHaveClass(className);
|
||||
}
|
||||
unmount();
|
||||
}
|
||||
});
|
||||
|
||||
it("renders failed delivery details on focus without persistent accepted chrome", async () => {
|
||||
const message: UIMessage = {
|
||||
id: "u-delivery",
|
||||
|
||||
@@ -610,14 +610,16 @@ describe("SettingsView Apps catalog", () => {
|
||||
|
||||
renderSettingsView({ initialSection: "apps" });
|
||||
|
||||
expect(await screen.findByText("Add tools to nanobot, then @ them in chat.")).toBeInTheDocument();
|
||||
expect(await screen.findByText("AnyGen")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Add tools to nanobot, then @ them in chat."),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Ready" })).toHaveAttribute("aria-pressed", "false");
|
||||
expect(screen.getByRole("button", { name: "Apps" })).toHaveAttribute("aria-pressed", "true");
|
||||
expect(screen.getByRole("button", { name: "Integrations" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Plugins" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("Api")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("AnyGen")).toBeInTheDocument();
|
||||
expect(screen.getByText("0 ready")).toBeInTheDocument();
|
||||
expect(screen.queryByText("0 ready")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows nanobot optional features and enables one", async () => {
|
||||
@@ -787,7 +789,7 @@ describe("SettingsView Apps catalog", () => {
|
||||
renderSettingsView({ initialSection: "channels" });
|
||||
|
||||
expect(await screen.findByRole("button", { name: "View Matrix settings" })).toBeInTheDocument();
|
||||
expect(screen.getByText("0 running · 1 channels")).toBeInTheDocument();
|
||||
expect(screen.queryByText("0 running · 1 channels")).not.toBeInTheDocument();
|
||||
expect(screen.getAllByText("Failed").length).toBeGreaterThan(0);
|
||||
expect(screen.queryByText("Enabled, support needs install")).not.toBeInTheDocument();
|
||||
|
||||
@@ -841,7 +843,7 @@ describe("SettingsView Apps catalog", () => {
|
||||
renderSettingsView({ initialSection: "channels" });
|
||||
|
||||
expect(await screen.findByRole("button", { name: "View Matrix settings" })).toBeInTheDocument();
|
||||
expect(screen.getByText("0 running · 1 channels")).toBeInTheDocument();
|
||||
expect(screen.queryByText("0 running · 1 channels")).not.toBeInTheDocument();
|
||||
expect(screen.getAllByText("Failed").length).toBeGreaterThan(0);
|
||||
expect(screen.getByText(runtimeError)).toBeInTheDocument();
|
||||
expect(screen.getByRole("switch", { name: "Matrix channel" })).toHaveAttribute(
|
||||
@@ -1353,7 +1355,7 @@ describe("SettingsView Apps catalog", () => {
|
||||
renderSettingsView({ initialSection: "channels" });
|
||||
|
||||
await screen.findByText("No assistant connected");
|
||||
expect(screen.getByText("0 running · 1 channels")).toBeInTheDocument();
|
||||
expect(screen.queryByText("0 running · 1 channels")).not.toBeInTheDocument();
|
||||
expect(screen.getAllByText("Failed").length).toBeGreaterThan(0);
|
||||
expect(screen.getByText(runtimeError)).toBeInTheDocument();
|
||||
expect(screen.getByRole("switch", { name: "test assistant" })).toHaveAttribute(
|
||||
|
||||
Reference in New Issue
Block a user