Fixed tab naming to correspond to the web client. (#1780)

This commit is contained in:
Bob Dickinson 2026-07-25 17:11:52 -07:00 • committed by GitHub
commit d009fcca09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 134 additions and 51 deletions

View file

@ -686,7 +686,7 @@ describe("App (status, layout, modals)", () => {
h.ctrl.fetchRequests = [{ ...errorRequest, responseStatus: 401 }];
const r = await mount(oneHttp());
await expectFrame(r, "error");
await expectFrame(r, "HTTP Requests (1)");
await expectFrame(r, "Network (1)");
});
it("updates dimensions when the terminal resizes", async () => {
@ -752,7 +752,7 @@ describe("App (status, layout, modals)", () => {
h.ctrl.status = "connected";
h.ctrl.prompts = [promptWithArgs];
const r = await mount(oneStdio());
await press(r, ["p", TAB, ENTER]);
await press(r, ["m", TAB, ENTER]);
await expectFrame(r, "MOCK_FORM");
await press(r, [ESC]);
expect(r.lastFrame() ?? "").not.toContain("MOCK_FORM");
@ -762,7 +762,7 @@ describe("App (status, layout, modals)", () => {
h.ctrl.status = "connected";
h.ctrl.prompts = [promptWithArgs];
const r = await mount(oneStdio());
await press(r, ["p", TAB, TAB, "+"]);
await press(r, ["m", TAB, TAB, "+"]);
const f = r.lastFrame() ?? "";
expect(f).toContain("Arguments:");
expect(f).toContain("Full JSON:");
@ -772,7 +772,7 @@ describe("App (status, layout, modals)", () => {
h.ctrl.status = "connected";
h.ctrl.prompts = [promptNoName];
const r = await mount(oneStdio());
await press(r, ["p", TAB, TAB, "+"]);
await press(r, ["m", TAB, TAB, "+"]);
const f = r.lastFrame() ?? "";
expect(f).toContain("Prompt: Unknown");
expect(f).not.toContain("Arguments:");
@ -781,7 +781,7 @@ describe("App (status, layout, modals)", () => {
it("opens message details for a request message (with response)", async () => {
h.ctrl.messages = [reqMessage];
const r = await mount(oneStdio());
await press(r, ["m", TAB, TAB, "+"]);
await press(r, ["p", TAB, TAB, "+"]);
const f = r.lastFrame() ?? "";
expect(f).toContain("Direction: request");
expect(f).toContain("Response:");
@ -790,14 +790,14 @@ describe("App (status, layout, modals)", () => {
it("opens message details for a notification message", async () => {
h.ctrl.messages = [notifMessage];
const r = await mount(oneStdio());
await press(r, ["m", TAB, TAB, "+"]);
await press(r, ["p", TAB, TAB, "+"]);
await expectFrame(r, "Notification:");
});
it("opens message details for a response message", async () => {
h.ctrl.messages = [respMessage];
const r = await mount(oneStdio());
await press(r, ["m", TAB, TAB, "+"]);
await press(r, ["p", TAB, TAB, "+"]);
await expectFrame(r, "Response:");
});
@ -805,7 +805,7 @@ describe("App (status, layout, modals)", () => {
h.ctrl.status = "connected";
h.ctrl.fetchRequests = [bareRequest];
const r = await mount(oneHttp());
await press(r, ["h", TAB, TAB, "+"]);
await press(r, ["n", TAB, TAB, "+"]);
await expectFrame(r, "Request Headers:");
});
@ -825,11 +825,11 @@ describe("App (status, layout, modals)", () => {
expect(h.disconnect).toHaveBeenCalled();
});
it("renders the HTTP requests tab and opens full request details", async () => {
it("renders the Network tab and opens full request details", async () => {
h.ctrl.status = "connected";
h.ctrl.fetchRequests = [fullRequest];
const r = await mount(oneHttp());
await press(r, ["h", TAB, TAB, "+"]);
await press(r, ["n", TAB, TAB, "+"]);
const f = r.lastFrame() ?? "";
expect(f).toContain("Request Headers:");
expect(f).toContain("Status: 200");
@ -839,17 +839,17 @@ describe("App (status, layout, modals)", () => {
h.ctrl.status = "connected";
h.ctrl.fetchRequests = [errorRequest];
const r = await mount(oneHttp());
await press(r, ["h", TAB, TAB, "+"]);
await press(r, ["n", TAB, TAB, "+"]);
await expectFrame(r, "Error: boom");
});
it("renders the Logging tab for a stdio server", async () => {
it("renders the Console tab for a stdio server", async () => {
h.ctrl.status = "connected";
h.ctrl.stderrLogs = [stderrLog];
const r = await mount(oneStdio());
await press(r, ["l"]);
await press(r, ["o"]);
const f = r.lastFrame() ?? "";
expect(f).toContain("Logging (1)");
expect(f).toContain("Console (1)");
expect(f).toContain("log line");
});
});
@ -913,20 +913,20 @@ describe("App (input handling, focus, effects)", () => {
it("cycles focus order through the messages tab panes", async () => {
h.ctrl.messages = [reqMessage];
const r = await mount(oneStdio());
await press(r, ["m"]);
await press(r, ["p"]);
await press(r, [TAB, TAB, TAB, TAB]); // forward through messages focusOrder
await press(r, [STAB, STAB]); // reverse
await expectFrame(r, "Messages");
await expectFrame(r, "Protocol");
});
it("cycles focus order through the requests tab panes", async () => {
h.ctrl.status = "connected";
h.ctrl.fetchRequests = [fullRequest];
const r = await mount(oneHttp());
await press(r, ["h"]);
await press(r, ["n"]);
await press(r, [TAB, TAB, TAB, TAB]);
await press(r, [STAB, STAB]);
await expectFrame(r, "Requests");
await expectFrame(r, "Network");
});
it("switches away from the Auth tab when selecting a non-OAuth server", async () => {
@ -937,11 +937,11 @@ describe("App (input handling, focus, effects)", () => {
expect(r.lastFrame() ?? "").not.toContain("No OAuth information yet");
});
it("switches away from the Logging tab when selecting a non-stdio server", async () => {
it("switches away from the Console tab when selecting a non-stdio server", async () => {
const r = await mount(stdioThenHttp());
await press(r, ["l"]); // Logging tab (stdio)
await press(r, ["o"]); // Console tab (stdio)
await press(r, [STAB]); // tabs -> serverList
await press(r, [DOWN]); // select the http server -> effect leaves Logging
await press(r, [DOWN]); // select the http server -> effect leaves Console
await expectFrame(r, "Server Configuration");
});

View file

@ -99,7 +99,7 @@ describe("HistoryTab", () => {
/>,
);
const frame = lastFrame() ?? "";
expect(frame).toContain("Messages (0)");
expect(frame).toContain("Protocol (0)");
expect(frame).toContain("No messages");
expect(frame).toContain("Select a message to view details");
expect(onCountChange).toHaveBeenCalledWith(0);
@ -115,7 +115,7 @@ describe("HistoryTab", () => {
/>,
);
const frame = lastFrame() ?? "";
expect(frame).toContain("Messages (7)");
expect(frame).toContain("Protocol (7)");
// request with response → "✓"; pending request → "..."
expect(frame).toContain("→ tools/list ✓");
expect(frame).toContain("→ tools/call ...");
@ -226,7 +226,7 @@ describe("HistoryTab", () => {
// up to move back toward the top
stdin.write(UP);
await tick();
expect(lastFrame() ?? "").toContain("Messages (7)");
expect(lastFrame() ?? "").toContain("Protocol (7)");
});
it("handles details-pane scrolling, footer, and zoom shortcut", async () => {

View file

@ -38,7 +38,7 @@ describe("NotificationsTab", () => {
<NotificationsTab stderrLogs={[]} width={80} height={20} />,
);
const frame = lastFrame() ?? "";
expect(frame).toContain("Logging (0)");
expect(frame).toContain("Console (0)");
expect(frame).toContain("No stderr output yet");
});
@ -48,7 +48,7 @@ describe("NotificationsTab", () => {
<NotificationsTab stderrLogs={logs} width={80} height={20} />,
);
const frame = lastFrame() ?? "";
expect(frame).toContain("Logging (2)");
expect(frame).toContain("Console (2)");
expect(frame).toContain("first error");
expect(frame).toContain("second error");
expect(frame).not.toContain("No stderr output yet");
@ -134,7 +134,7 @@ describe("NotificationsTab", () => {
const { lastFrame, stdin } = render(
<NotificationsTab stderrLogs={logs} width={80} height={20} focused />,
);
expect(lastFrame() ?? "").toContain("Logging (2)");
expect(lastFrame() ?? "").toContain("Console (2)");
// Drive every useInput branch
stdin.write(UP);

View file

@ -97,7 +97,7 @@ describe("RequestsTab", () => {
/>,
);
const frame = lastFrame() ?? "";
expect(frame).toContain("Requests (0)");
expect(frame).toContain("Network (0)");
expect(frame).toContain("No requests");
expect(frame).toContain("Select a request to view details");
expect(onCountChange).toHaveBeenCalledWith(0);
@ -107,7 +107,7 @@ describe("RequestsTab", () => {
const { lastFrame } = render(
<RequestsTab serverName="srv" requests={[]} width={120} height={30} />,
);
expect(lastFrame() ?? "").toContain("Requests (0)");
expect(lastFrame() ?? "").toContain("Network (0)");
});
it("renders the list with status colors, labels and durations", () => {
@ -125,7 +125,7 @@ describe("RequestsTab", () => {
/>,
);
const frame = lastFrame() ?? "";
expect(frame).toContain("Requests (4)");
expect(frame).toContain("Network (4)");
// category labels
expect(frame).toContain("AUTH");
expect(frame).toContain("MCP");
@ -272,7 +272,7 @@ describe("RequestsTab", () => {
// up moves back toward the top
stdin.write(UP);
await tick();
expect(lastFrame() ?? "").toContain("Requests (8)");
expect(lastFrame() ?? "").toContain("Network (8)");
});
it("clamps at the bottom when paging past the end", async () => {

View file

@ -13,9 +13,10 @@ describe("Tabs", () => {
const frame = lastFrame() ?? "";
expect(frame).toContain("Info");
expect(frame).toContain("Auth");
expect(frame).toContain("Logging");
// requests defaults to hidden
expect(frame).not.toContain("HTTP Requests");
expect(frame).toContain("Console");
expect(frame).toContain("Protocol");
// Network defaults to hidden
expect(frame).not.toContain("Network");
});
it("hides the auth tab when showAuth is false", () => {
@ -34,7 +35,7 @@ describe("Tabs", () => {
showLogging={false}
/>,
);
expect(lastFrame() ?? "").not.toContain("Logging");
expect(lastFrame() ?? "").not.toContain("Console");
});
it("shows the requests tab when showRequests is true", () => {
@ -46,7 +47,7 @@ describe("Tabs", () => {
showRequests={true}
/>,
);
expect(lastFrame() ?? "").toContain("HTTP Requests");
expect(lastFrame() ?? "").toContain("Network");
});
it("marks the active tab with the ▶ marker", () => {

View file

@ -1,5 +1,6 @@
import { describe, it, expect } from "vitest";
import { tabs } from "../src/components/tabsConfig.js";
import { splitLabelAtAccelerator } from "../src/components/Tabs.js";
describe("TUI", () => {
it("exports tabs with expected shape", () => {
@ -18,4 +19,52 @@ describe("TUI", () => {
expect(info?.label).toBe("Info");
expect(info?.accelerator).toBe("i");
});
it("uses web-aligned monitor tab labels with unique in-label accelerators", () => {
expect(tabs.find((t) => t.id === "messages")).toMatchObject({
label: "Protocol",
accelerator: "p",
});
expect(tabs.find((t) => t.id === "requests")).toMatchObject({
label: "Network",
accelerator: "n",
});
expect(tabs.find((t) => t.id === "logging")).toMatchObject({
label: "Console",
accelerator: "o",
});
expect(tabs.find((t) => t.id === "prompts")).toMatchObject({
label: "Prompts",
accelerator: "m",
});
const accelerators = tabs.map((t) => t.accelerator);
expect(new Set(accelerators).size).toBe(accelerators.length);
for (const tab of tabs) {
expect(tab.label.toLowerCase()).toContain(tab.accelerator.toLowerCase());
}
});
it("splits labels so the accelerator letter can be underlined mid-word", () => {
expect(splitLabelAtAccelerator("Prompts", "m")).toEqual({
before: "Pro",
accel: "m",
after: "pts",
});
expect(splitLabelAtAccelerator("Console", "o")).toEqual({
before: "C",
accel: "o",
after: "nsole",
});
expect(splitLabelAtAccelerator("Protocol", "p")).toEqual({
before: "",
accel: "P",
after: "rotocol",
});
expect(splitLabelAtAccelerator("Info", "z")).toEqual({
before: "",
accel: "I",
after: "nfo",
});
});
});