Files
tm-landing/lib/__tests__/apiErrorHandler.test.ts
T
AmirReza Jamali 7b6cd4d965 feat(validation): Implement form validation and error handling utilities
- Add validation rules for form fields with XSS protection
- Create API error handler to parse and manage server validation errors
- Integrate validation rules into contact and inquiries forms
- Enhance error handling with user feedback for form submissions
- Introduce unit tests for validation and error handling functionalities
2026-02-08 13:35:07 +03:30

211 lines
6.4 KiB
TypeScript

import { AxiosError } from "axios";
import { handleApiError, parseApiError } from "../apiErrorHandler";
describe("API Error Handler", () => {
describe("parseApiError", () => {
it("should return default message for non-Error objects", () => {
const result = parseApiError("string error");
expect(result.generalMessage).toBe("Something went wrong. Please try again.");
expect(result.fieldErrors).toEqual({});
});
it("should handle network errors (no response)", () => {
const error = new Error("Network Error") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = undefined;
const result = parseApiError(error);
expect(result.generalMessage).toContain("Unable to connect");
});
it("should parse 400 errors with field-level errors", () => {
const error = new Error("Bad Request") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 400,
data: {
errors: {
email: "Invalid email format",
full_name: ["Name is too short", "Name contains invalid characters"],
},
},
statusText: "Bad Request",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Validation error");
expect(result.fieldErrors.email).toBe("Invalid email format");
expect(result.fieldErrors.full_name).toContain("Name is too short");
});
it("should parse 400 errors with message only", () => {
const error = new Error("Bad Request") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 400,
data: {
message: "Custom validation error message",
},
statusText: "Bad Request",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.generalMessage).toBe("Custom validation error message");
});
it("should handle 401 unauthorized errors", () => {
const error = new Error("Unauthorized") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 401,
data: {},
statusText: "Unauthorized",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Authentication required");
});
it("should handle 422 unprocessable entity errors", () => {
const error = new Error("Unprocessable Entity") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 422,
data: {
errors: {
phone: "invalid_phone",
},
},
statusText: "Unprocessable Entity",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Validation error");
expect(result.fieldErrors.phone).toContain("phone number");
});
it("should handle 429 rate limit errors", () => {
const error = new Error("Too Many Requests") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 429,
data: {},
statusText: "Too Many Requests",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Too many requests");
});
it("should handle 500 server errors", () => {
const error = new Error("Internal Server Error") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 500,
data: {},
statusText: "Internal Server Error",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Server error");
});
});
describe("handleApiError", () => {
it("should call setError for each field error", () => {
const mockSetError = jest.fn();
const mockShowToast = jest.fn();
const error = new Error("Bad Request") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 400,
data: {
errors: {
email: "Invalid email",
phone: "Invalid phone",
},
},
statusText: "Bad Request",
headers: {},
config: {} as any,
};
handleApiError(error, mockSetError, mockShowToast, ["email", "phone"]);
expect(mockSetError).toHaveBeenCalledTimes(2);
expect(mockSetError).toHaveBeenCalledWith("email", {
type: "server",
message: "Invalid email",
});
expect(mockSetError).toHaveBeenCalledWith("phone", {
type: "server",
message: "Invalid phone",
});
expect(mockShowToast).toHaveBeenCalledWith(
"Please correct the errors in the highlighted fields."
);
});
it("should only set errors for fields in formFields list", () => {
const mockSetError = jest.fn();
const mockShowToast = jest.fn();
const error = new Error("Bad Request") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 400,
data: {
errors: {
email: "Invalid email",
unknown_field: "Unknown error",
},
},
statusText: "Bad Request",
headers: {},
config: {} as any,
};
handleApiError(error, mockSetError, mockShowToast, ["email"]);
expect(mockSetError).toHaveBeenCalledTimes(1);
expect(mockSetError).toHaveBeenCalledWith("email", {
type: "server",
message: "Invalid email",
});
});
it("should show general message when no field errors", () => {
const mockSetError = jest.fn();
const mockShowToast = jest.fn();
const error = new Error("Internal Server Error") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 500,
data: {},
statusText: "Internal Server Error",
headers: {},
config: {} as any,
};
handleApiError(error, mockSetError, mockShowToast, ["email"]);
expect(mockSetError).not.toHaveBeenCalled();
expect(mockShowToast).toHaveBeenCalledWith(expect.stringContaining("Server error"));
});
});
});