fix: enhance error handling for custom headers and improve test coverage for header management
This commit is contained in:
parent
fde55efc2f
commit
90ab419d12
3 changed files with 163 additions and 41 deletions
|
|
@ -137,7 +137,11 @@ const App = () => {
|
|||
if (savedHeaders) {
|
||||
try {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ const CustomHeaders = ({
|
|||
size="sm"
|
||||
onClick={addHeader}
|
||||
className="text-xs px-2"
|
||||
data-testid="add-header-button"
|
||||
>
|
||||
<Plus className="w-3 h-3 mr-1" />
|
||||
Add
|
||||
|
|
@ -185,6 +186,7 @@ const CustomHeaders = ({
|
|||
value={header.name}
|
||||
onChange={(e) => updateHeader(index, "name", e.target.value)}
|
||||
className="font-mono text-xs"
|
||||
data-testid={`header-name-input-${index}`}
|
||||
/>
|
||||
<div className="relative">
|
||||
<Input
|
||||
|
|
@ -195,6 +197,7 @@ const CustomHeaders = ({
|
|||
}
|
||||
type={visibleValues.has(index) ? "text" : "password"}
|
||||
className="font-mono text-xs pr-8"
|
||||
data-testid={`header-value-input-${index}`}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -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 { describe, it, beforeEach, jest } from "@jest/globals";
|
||||
import Sidebar from "../Sidebar";
|
||||
|
|
@ -623,117 +629,179 @@ describe("Sidebar", () => {
|
|||
fireEvent.click(button);
|
||||
};
|
||||
|
||||
it("should update bearer token", () => {
|
||||
const setBearerToken = jest.fn();
|
||||
it("should update bearer token via custom headers", async () => {
|
||||
const setCustomHeaders = jest.fn();
|
||||
renderSidebar({
|
||||
bearerToken: "",
|
||||
setBearerToken,
|
||||
customHeaders: [],
|
||||
setCustomHeaders,
|
||||
transportType: "sse", // Set transport type to SSE
|
||||
});
|
||||
|
||||
openAuthSection();
|
||||
|
||||
const tokenInput = screen.getByTestId("bearer-token-input");
|
||||
fireEvent.change(tokenInput, { target: { value: "new_token" } });
|
||||
// Add a new header
|
||||
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", () => {
|
||||
const setHeaderName = jest.fn();
|
||||
it("should update header name via custom headers", () => {
|
||||
const setCustomHeaders = jest.fn();
|
||||
renderSidebar({
|
||||
headerName: "Authorization",
|
||||
setHeaderName,
|
||||
customHeaders: [
|
||||
{
|
||||
name: "Authorization",
|
||||
value: "Bearer token123",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
setCustomHeaders,
|
||||
transportType: "sse",
|
||||
});
|
||||
|
||||
openAuthSection();
|
||||
|
||||
const headerInput = screen.getByTestId("header-input");
|
||||
fireEvent.change(headerInput, { target: { value: "X-Custom-Auth" } });
|
||||
const headerNameInput = screen.getByTestId("header-name-input-0");
|
||||
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", () => {
|
||||
const setBearerToken = jest.fn();
|
||||
it("should clear bearer token via custom headers", () => {
|
||||
const setCustomHeaders = jest.fn();
|
||||
renderSidebar({
|
||||
bearerToken: "existing_token",
|
||||
setBearerToken,
|
||||
customHeaders: [
|
||||
{
|
||||
name: "Authorization",
|
||||
value: "Bearer existing_token",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
setCustomHeaders,
|
||||
transportType: "sse", // Set transport type to SSE
|
||||
});
|
||||
|
||||
openAuthSection();
|
||||
|
||||
const tokenInput = screen.getByTestId("bearer-token-input");
|
||||
fireEvent.change(tokenInput, { target: { value: "" } });
|
||||
const headerValueInput = screen.getByTestId("header-value-input-0");
|
||||
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({
|
||||
bearerToken: "existing_token",
|
||||
customHeaders: [
|
||||
{
|
||||
name: "Authorization",
|
||||
value: "Bearer existing_token",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
transportType: "sse", // Set transport type to SSE
|
||||
});
|
||||
|
||||
openAuthSection();
|
||||
|
||||
// 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");
|
||||
|
||||
// Update the token
|
||||
fireEvent.change(tokenInput, { target: { value: "new_token" } });
|
||||
fireEvent.change(tokenInput, { target: { value: "Bearer new_token" } });
|
||||
|
||||
// Rerender with updated token
|
||||
rerender(
|
||||
<TooltipProvider>
|
||||
<Sidebar
|
||||
{...defaultProps}
|
||||
bearerToken="new_token"
|
||||
customHeaders={[
|
||||
{
|
||||
name: "Authorization",
|
||||
value: "Bearer new_token",
|
||||
enabled: true,
|
||||
},
|
||||
]}
|
||||
transportType="sse"
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
// 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", () => {
|
||||
const { rerender } = renderSidebar({
|
||||
bearerToken: "existing_token",
|
||||
customHeaders: [
|
||||
{
|
||||
name: "Authorization",
|
||||
value: "Bearer existing_token",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
transportType: "sse", // Set transport type to SSE
|
||||
});
|
||||
|
||||
openAuthSection();
|
||||
|
||||
// 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");
|
||||
|
||||
// Update the token
|
||||
fireEvent.change(tokenInput, { target: { value: "new_token" } });
|
||||
fireEvent.change(tokenInput, { target: { value: "Bearer new_token" } });
|
||||
|
||||
// Rerender with updated token
|
||||
rerender(
|
||||
<TooltipProvider>
|
||||
<Sidebar
|
||||
{...defaultProps}
|
||||
bearerToken="new_token"
|
||||
customHeaders={[
|
||||
{
|
||||
name: "Authorization",
|
||||
value: "Bearer new_token",
|
||||
enabled: true,
|
||||
},
|
||||
]}
|
||||
transportType="sse"
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
// 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", () => {
|
||||
renderSidebar({
|
||||
headerName: "X-API-Key",
|
||||
customHeaders: [
|
||||
{
|
||||
name: "X-API-Key",
|
||||
value: "api-key-123",
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
transportType: "sse",
|
||||
});
|
||||
|
||||
|
|
@ -741,7 +809,7 @@ describe("Sidebar", () => {
|
|||
openAuthSection();
|
||||
|
||||
// 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");
|
||||
|
||||
// Close auth section
|
||||
|
|
@ -752,19 +820,66 @@ describe("Sidebar", () => {
|
|||
fireEvent.click(authButton);
|
||||
|
||||
// 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({
|
||||
headerName: undefined,
|
||||
customHeaders: [],
|
||||
transportType: "sse",
|
||||
});
|
||||
|
||||
openAuthSection();
|
||||
|
||||
const headerInput = screen.getByTestId("header-input");
|
||||
expect(headerInput).toHaveAttribute("placeholder", "Authorization");
|
||||
// Verify that the "No custom headers configured" message is shown
|
||||
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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue