adding fixes as buttons were not visible for streamable-http transport type, as per PR review comment
This commit is contained in:
@@ -119,13 +119,22 @@ const Sidebar = ({
|
|||||||
args: args.trim() ? args.split(/\s+/) : [],
|
args: args.trim() ? args.split(/\s+/) : [],
|
||||||
env: { ...env },
|
env: { ...env },
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
|
if (transportType === "sse") {
|
||||||
return {
|
return {
|
||||||
type: "sse",
|
type: "sse",
|
||||||
url: sseUrl,
|
url: sseUrl,
|
||||||
note: "For SSE connections, add this URL directly in Client",
|
note: "For SSE connections, add this URL directly in Client",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (transportType === "streamable-http") {
|
||||||
|
return {
|
||||||
|
type: "streamable-http",
|
||||||
|
url: sseUrl,
|
||||||
|
note: "For Streamable HTTP connections, add this URL directly in Client",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}, [transportType, command, args, env, sseUrl]);
|
}, [transportType, command, args, env, sseUrl]);
|
||||||
|
|
||||||
// Memoized config entry generator
|
// Memoized config entry generator
|
||||||
@@ -266,44 +275,6 @@ const Sidebar = ({
|
|||||||
className="font-mono"
|
className="font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-2 mt-2">
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleCopyServerEntry}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{copiedServerEntry ? (
|
|
||||||
<CheckCheck className="h-4 w-4 mr-2" />
|
|
||||||
) : (
|
|
||||||
<Copy className="h-4 w-4 mr-2" />
|
|
||||||
)}
|
|
||||||
Server Entry
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Copy Server Entry</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleCopyServerFile}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{copiedServerFile ? (
|
|
||||||
<CheckCheck className="h-4 w-4 mr-2" />
|
|
||||||
) : (
|
|
||||||
<Copy className="h-4 w-4 mr-2" />
|
|
||||||
)}
|
|
||||||
Servers File
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Copy Servers File</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -319,22 +290,6 @@ const Sidebar = ({
|
|||||||
className="font-mono"
|
className="font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full mt-2">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleCopyServerFile}
|
|
||||||
className="w-full"
|
|
||||||
title="Copy SSE URL Configuration"
|
|
||||||
>
|
|
||||||
{copiedServerFile ? (
|
|
||||||
<CheckCheck className="h-4 w-4 mr-2" />
|
|
||||||
) : (
|
|
||||||
<Copy className="h-4 w-4 mr-2" />
|
|
||||||
)}
|
|
||||||
Copy Servers File
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -382,6 +337,7 @@ const Sidebar = ({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{transportType === "stdio" && (
|
{transportType === "stdio" && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Button
|
<Button
|
||||||
@@ -507,6 +463,46 @@ const Sidebar = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Always show both copy buttons for all transport types */}
|
||||||
|
<div className="grid grid-cols-2 gap-2 mt-2">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleCopyServerEntry}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{copiedServerEntry ? (
|
||||||
|
<CheckCheck className="h-4 w-4 mr-2" />
|
||||||
|
) : (
|
||||||
|
<Copy className="h-4 w-4 mr-2" />
|
||||||
|
)}
|
||||||
|
Server Entry
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Copy Server Entry</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleCopyServerFile}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{copiedServerFile ? (
|
||||||
|
<CheckCheck className="h-4 w-4 mr-2" />
|
||||||
|
) : (
|
||||||
|
<Copy className="h-4 w-4 mr-2" />
|
||||||
|
)}
|
||||||
|
Servers File
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Copy Servers File</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Configuration */}
|
{/* Configuration */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -649,6 +649,31 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
jest.clearAllTimers();
|
jest.clearAllTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getCopyButtons = () => {
|
||||||
|
return {
|
||||||
|
serverEntry: screen.getByRole("button", { name: /server entry/i }),
|
||||||
|
serversFile: screen.getByRole("button", { name: /servers file/i }),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should render both copy buttons for all transport types", () => {
|
||||||
|
["stdio", "sse", "streamable-http"].forEach((transportType) => {
|
||||||
|
renderSidebar({ transportType });
|
||||||
|
// There should be exactly one Server Entry and one Servers File button per render
|
||||||
|
const serverEntryButtons = screen.getAllByRole("button", {
|
||||||
|
name: /server entry/i,
|
||||||
|
});
|
||||||
|
const serversFileButtons = screen.getAllByRole("button", {
|
||||||
|
name: /servers file/i,
|
||||||
|
});
|
||||||
|
expect(serverEntryButtons).toHaveLength(1);
|
||||||
|
expect(serversFileButtons).toHaveLength(1);
|
||||||
|
// Clean up DOM for next iteration
|
||||||
|
// (Testing Library's render does not auto-unmount in a loop)
|
||||||
|
document.body.innerHTML = "";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should copy server entry configuration to clipboard for STDIO transport", async () => {
|
it("should copy server entry configuration to clipboard for STDIO transport", async () => {
|
||||||
const command = "node";
|
const command = "node";
|
||||||
const args = "--inspect server.js";
|
const args = "--inspect server.js";
|
||||||
@@ -661,20 +686,13 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
env,
|
env,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use act to properly wrap the clipboard operations
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
const copyServerEntryButton = screen.getByRole("button", {
|
const { serverEntry } = getCopyButtons();
|
||||||
name: /server entry/i,
|
fireEvent.click(serverEntry);
|
||||||
});
|
|
||||||
fireEvent.click(copyServerEntryButton);
|
|
||||||
|
|
||||||
// Fast-forward timers to handle the setTimeout
|
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check clipboard API was called with the correct configuration
|
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const expectedConfig = JSON.stringify(
|
const expectedConfig = JSON.stringify(
|
||||||
{
|
{
|
||||||
command,
|
command,
|
||||||
@@ -684,7 +702,6 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -701,18 +718,12 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
const copyServersFileButton = screen.getByRole("button", {
|
const { serversFile } = getCopyButtons();
|
||||||
name: /servers file/i,
|
fireEvent.click(serversFile);
|
||||||
});
|
|
||||||
fireEvent.click(copyServersFileButton);
|
|
||||||
|
|
||||||
// Fast-forward timers to handle the setTimeout
|
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check clipboard API was called with the correct configuration
|
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const expectedConfig = JSON.stringify(
|
const expectedConfig = JSON.stringify(
|
||||||
{
|
{
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
@@ -726,31 +737,43 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
|
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should copy server entry configuration to clipboard for SSE transport", async () => {
|
||||||
|
const sseUrl = "http://localhost:3000/events";
|
||||||
|
renderSidebar({ transportType: "sse", sseUrl });
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const { serverEntry } = getCopyButtons();
|
||||||
|
fireEvent.click(serverEntry);
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
||||||
|
const expectedConfig = JSON.stringify(
|
||||||
|
{
|
||||||
|
type: "sse",
|
||||||
|
url: sseUrl,
|
||||||
|
note: "For SSE connections, add this URL directly in Client",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
);
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should copy servers file configuration to clipboard for SSE transport", async () => {
|
it("should copy servers file configuration to clipboard for SSE transport", async () => {
|
||||||
const sseUrl = "http://localhost:3000/events";
|
const sseUrl = "http://localhost:3000/events";
|
||||||
|
renderSidebar({ transportType: "sse", sseUrl });
|
||||||
renderSidebar({
|
|
||||||
transportType: "sse",
|
|
||||||
sseUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
const copyServersFileButton = screen.getByRole("button", {
|
const { serversFile } = getCopyButtons();
|
||||||
name: /servers file/i,
|
fireEvent.click(serversFile);
|
||||||
});
|
|
||||||
fireEvent.click(copyServersFileButton);
|
|
||||||
|
|
||||||
// Fast-forward timers to handle the setTimeout
|
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check clipboard API was called with the correct configuration
|
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const expectedConfig = JSON.stringify(
|
const expectedConfig = JSON.stringify(
|
||||||
{
|
{
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
@@ -764,7 +787,56 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
|
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should copy server entry configuration to clipboard for streamable-http transport", async () => {
|
||||||
|
const sseUrl = "http://localhost:3001/sse";
|
||||||
|
renderSidebar({ transportType: "streamable-http", sseUrl });
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const { serverEntry } = getCopyButtons();
|
||||||
|
fireEvent.click(serverEntry);
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
||||||
|
const expectedConfig = JSON.stringify(
|
||||||
|
{
|
||||||
|
type: "streamable-http",
|
||||||
|
url: sseUrl,
|
||||||
|
note: "For Streamable HTTP connections, add this URL directly in Client",
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should copy servers file configuration to clipboard for streamable-http transport", async () => {
|
||||||
|
const sseUrl = "http://localhost:3001/sse";
|
||||||
|
renderSidebar({ transportType: "streamable-http", sseUrl });
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const { serversFile } = getCopyButtons();
|
||||||
|
fireEvent.click(serversFile);
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
||||||
|
const expectedConfig = JSON.stringify(
|
||||||
|
{
|
||||||
|
mcpServers: {
|
||||||
|
"default-server": {
|
||||||
|
type: "streamable-http",
|
||||||
|
url: sseUrl,
|
||||||
|
note: "For Streamable HTTP connections, add this URL directly in Client",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
);
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -779,18 +851,12 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
const copyServerEntryButton = screen.getByRole("button", {
|
const { serverEntry } = getCopyButtons();
|
||||||
name: /server entry/i,
|
fireEvent.click(serverEntry);
|
||||||
});
|
|
||||||
fireEvent.click(copyServerEntryButton);
|
|
||||||
|
|
||||||
// Fast-forward timers to handle the setTimeout
|
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check clipboard API was called with empty args array
|
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
expect(mockClipboardWrite).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const expectedConfig = JSON.stringify(
|
const expectedConfig = JSON.stringify(
|
||||||
{
|
{
|
||||||
command,
|
command,
|
||||||
@@ -800,7 +866,6 @@ describe("Sidebar Environment Variables", () => {
|
|||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
expect(mockClipboardWrite).toHaveBeenCalledWith(expectedConfig);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user