fix: enhance error handling for custom headers and improve test coverage for header management

This commit is contained in:
jeetul.samaiya 2025-09-10 21:01:24 +05:30
commit 90ab419d12
3 changed files with 163 additions and 41 deletions

View file

@ -137,7 +137,11 @@ const App = () => {
if (savedHeaders) { if (savedHeaders) {
try { try {
return JSON.parse(savedHeaders); return JSON.parse(savedHeaders);
} catch { } catch (error) {
console.warn(
`Failed to parse custom headers: "${savedHeaders}", will try legacy migration`,
error,
);
// Fall back to migration if JSON parsing fails // Fall back to migration if JSON parsing fails
} }
} }

View file

@ -153,6 +153,7 @@ const CustomHeaders = ({
size="sm" size="sm"
onClick={addHeader} onClick={addHeader}
className="text-xs px-2" className="text-xs px-2"
data-testid="add-header-button"
> >
<Plus className="w-3 h-3 mr-1" /> <Plus className="w-3 h-3 mr-1" />
Add Add
@ -185,6 +186,7 @@ const CustomHeaders = ({
value={header.name} value={header.name}
onChange={(e) => updateHeader(index, "name", e.target.value)} onChange={(e) => updateHeader(index, "name", e.target.value)}
className="font-mono text-xs" className="font-mono text-xs"
data-testid={`header-name-input-${index}`}
/> />
<div className="relative"> <div className="relative">
<Input <Input
@ -195,6 +197,7 @@ const CustomHeaders = ({
} }
type={visibleValues.has(index) ? "text" : "password"} type={visibleValues.has(index) ? "text" : "password"}
className="font-mono text-xs pr-8" className="font-mono text-xs pr-8"
data-testid={`header-value-input-${index}`}
/> />
<Button <Button
type="button" type="button"

View file

@ -1,4 +1,10 @@
import { render, screen, fireEvent, act } from "@testing-library/react"; import {
render,
screen,
fireEvent,
act,
waitFor,
} from "@testing-library/react";
import "@testing-library/jest-dom"; import "@testing-library/jest-dom";
import { describe, it, beforeEach, jest } from "@jest/globals"; import { describe, it, beforeEach, jest } from "@jest/globals";
import Sidebar from "../Sidebar"; import Sidebar from "../Sidebar";
@ -623,117 +629,179 @@ describe("Sidebar", () => {
fireEvent.click(button); fireEvent.click(button);
}; };
it("should update bearer token", () => { it("should update bearer token via custom headers", async () => {
const setBearerToken = jest.fn(); const setCustomHeaders = jest.fn();
renderSidebar({ renderSidebar({
bearerToken: "", customHeaders: [],
setBearerToken, setCustomHeaders,
transportType: "sse", // Set transport type to SSE transportType: "sse", // Set transport type to SSE
}); });
openAuthSection(); openAuthSection();
const tokenInput = screen.getByTestId("bearer-token-input"); // Add a new header
fireEvent.change(tokenInput, { target: { value: "new_token" } }); const addButton = screen.getByTestId("add-header-button");
fireEvent.click(addButton);
expect(setBearerToken).toHaveBeenCalledWith("new_token"); // Verify that setCustomHeaders was called to add an empty header
expect(setCustomHeaders).toHaveBeenCalledWith([
{
name: "",
value: "",
enabled: true,
},
]);
}); });
it("should update header name", () => { it("should update header name via custom headers", () => {
const setHeaderName = jest.fn(); const setCustomHeaders = jest.fn();
renderSidebar({ renderSidebar({
headerName: "Authorization", customHeaders: [
setHeaderName, {
name: "Authorization",
value: "Bearer token123",
enabled: true,
},
],
setCustomHeaders,
transportType: "sse", transportType: "sse",
}); });
openAuthSection(); openAuthSection();
const headerInput = screen.getByTestId("header-input"); const headerNameInput = screen.getByTestId("header-name-input-0");
fireEvent.change(headerInput, { target: { value: "X-Custom-Auth" } }); fireEvent.change(headerNameInput, { target: { value: "X-Custom-Auth" } });
expect(setHeaderName).toHaveBeenCalledWith("X-Custom-Auth"); expect(setCustomHeaders).toHaveBeenCalledWith([
{
name: "X-Custom-Auth",
value: "Bearer token123",
enabled: true,
},
]);
}); });
it("should clear bearer token", () => { it("should clear bearer token via custom headers", () => {
const setBearerToken = jest.fn(); const setCustomHeaders = jest.fn();
renderSidebar({ renderSidebar({
bearerToken: "existing_token", customHeaders: [
setBearerToken, {
name: "Authorization",
value: "Bearer existing_token",
enabled: true,
},
],
setCustomHeaders,
transportType: "sse", // Set transport type to SSE transportType: "sse", // Set transport type to SSE
}); });
openAuthSection(); openAuthSection();
const tokenInput = screen.getByTestId("bearer-token-input"); const headerValueInput = screen.getByTestId("header-value-input-0");
fireEvent.change(tokenInput, { target: { value: "" } }); fireEvent.change(headerValueInput, { target: { value: "" } });
expect(setBearerToken).toHaveBeenCalledWith(""); expect(setCustomHeaders).toHaveBeenCalledWith([
{
name: "Authorization",
value: "",
enabled: true,
},
]);
}); });
it("should properly render bearer token input", () => { it("should properly render header value input as password field", () => {
const { rerender } = renderSidebar({ const { rerender } = renderSidebar({
bearerToken: "existing_token", customHeaders: [
{
name: "Authorization",
value: "Bearer existing_token",
enabled: true,
},
],
transportType: "sse", // Set transport type to SSE transportType: "sse", // Set transport type to SSE
}); });
openAuthSection(); openAuthSection();
// Token input should be a password field // Token input should be a password field
const tokenInput = screen.getByTestId("bearer-token-input"); const tokenInput = screen.getByTestId("header-value-input-0");
expect(tokenInput).toHaveProperty("type", "password"); expect(tokenInput).toHaveProperty("type", "password");
// Update the token // Update the token
fireEvent.change(tokenInput, { target: { value: "new_token" } }); fireEvent.change(tokenInput, { target: { value: "Bearer new_token" } });
// Rerender with updated token // Rerender with updated token
rerender( rerender(
<TooltipProvider> <TooltipProvider>
<Sidebar <Sidebar
{...defaultProps} {...defaultProps}
bearerToken="new_token" customHeaders={[
{
name: "Authorization",
value: "Bearer new_token",
enabled: true,
},
]}
transportType="sse" transportType="sse"
/> />
</TooltipProvider>, </TooltipProvider>,
); );
// Token input should still exist after update // Token input should still exist after update
expect(screen.getByTestId("bearer-token-input")).toBeInTheDocument(); expect(screen.getByTestId("header-value-input-0")).toBeInTheDocument();
}); });
it("should maintain token visibility state after update", () => { it("should maintain token visibility state after update", () => {
const { rerender } = renderSidebar({ const { rerender } = renderSidebar({
bearerToken: "existing_token", customHeaders: [
{
name: "Authorization",
value: "Bearer existing_token",
enabled: true,
},
],
transportType: "sse", // Set transport type to SSE transportType: "sse", // Set transport type to SSE
}); });
openAuthSection(); openAuthSection();
// Token input should be a password field // Token input should be a password field
const tokenInput = screen.getByTestId("bearer-token-input"); const tokenInput = screen.getByTestId("header-value-input-0");
expect(tokenInput).toHaveProperty("type", "password"); expect(tokenInput).toHaveProperty("type", "password");
// Update the token // Update the token
fireEvent.change(tokenInput, { target: { value: "new_token" } }); fireEvent.change(tokenInput, { target: { value: "Bearer new_token" } });
// Rerender with updated token // Rerender with updated token
rerender( rerender(
<TooltipProvider> <TooltipProvider>
<Sidebar <Sidebar
{...defaultProps} {...defaultProps}
bearerToken="new_token" customHeaders={[
{
name: "Authorization",
value: "Bearer new_token",
enabled: true,
},
]}
transportType="sse" transportType="sse"
/> />
</TooltipProvider>, </TooltipProvider>,
); );
// Token input should still exist after update // Token input should still exist after update
expect(screen.getByTestId("bearer-token-input")).toBeInTheDocument(); expect(screen.getByTestId("header-value-input-0")).toBeInTheDocument();
}); });
it("should maintain header name when toggling auth section", () => { it("should maintain header name when toggling auth section", () => {
renderSidebar({ renderSidebar({
headerName: "X-API-Key", customHeaders: [
{
name: "X-API-Key",
value: "api-key-123",
enabled: true,
},
],
transportType: "sse", transportType: "sse",
}); });
@ -741,7 +809,7 @@ describe("Sidebar", () => {
openAuthSection(); openAuthSection();
// Verify header name is displayed // Verify header name is displayed
const headerInput = screen.getByTestId("header-input"); const headerInput = screen.getByTestId("header-name-input-0");
expect(headerInput).toHaveValue("X-API-Key"); expect(headerInput).toHaveValue("X-API-Key");
// Close auth section // Close auth section
@ -752,19 +820,66 @@ describe("Sidebar", () => {
fireEvent.click(authButton); fireEvent.click(authButton);
// Verify header name is still preserved // Verify header name is still preserved
expect(screen.getByTestId("header-input")).toHaveValue("X-API-Key"); expect(screen.getByTestId("header-name-input-0")).toHaveValue(
"X-API-Key",
);
}); });
it("should display default header name when not specified", () => { it("should display placeholder for header name when no headers exist", () => {
renderSidebar({ renderSidebar({
headerName: undefined, customHeaders: [],
transportType: "sse", transportType: "sse",
}); });
openAuthSection(); openAuthSection();
const headerInput = screen.getByTestId("header-input"); // Verify that the "No custom headers configured" message is shown
expect(headerInput).toHaveAttribute("placeholder", "Authorization"); expect(
screen.getByText("No custom headers configured"),
).toBeInTheDocument();
expect(
screen.getByText('Click "Add" to get started'),
).toBeInTheDocument();
// Verify the Add button is present
const addButton = screen.getByTestId("add-header-button");
expect(addButton).toBeInTheDocument();
});
it("should allow editing existing headers", () => {
const setCustomHeaders = jest.fn();
renderSidebar({
customHeaders: [
{
name: "Authorization",
value: "Bearer token123",
enabled: true,
},
],
setCustomHeaders,
transportType: "sse",
});
openAuthSection();
// Verify header inputs are rendered
const headerNameInput = screen.getByTestId("header-name-input-0");
const headerValueInput = screen.getByTestId("header-value-input-0");
expect(headerNameInput).toHaveValue("Authorization");
expect(headerValueInput).toHaveValue("Bearer token123");
expect(headerNameInput).toHaveAttribute("placeholder", "Header Name");
expect(headerValueInput).toHaveAttribute("placeholder", "Header Value");
// Update header name
fireEvent.change(headerNameInput, { target: { value: "X-API-Key" } });
expect(setCustomHeaders).toHaveBeenCalledWith([
{
name: "X-API-Key",
value: "Bearer token123",
enabled: true,
},
]);
}); });
}); });