refactor(inquiries): Update form validation and error handling

- Change required fields in inquiries form to enforce mandatory input
- Enhance API error handler to provide user-friendly messages without exposing raw server text
- Improve test cases for API error handling to ensure accurate message mapping and validation
- Refactor error messages for clarity and consistency across the application
This commit is contained in:
AmirReza Jamali
2026-04-13 08:37:22 +03:30
parent 1ee57a4ec1
commit 8518c2c140
4 changed files with 380 additions and 115 deletions
+78 -24
View File
@@ -35,12 +35,12 @@ describe("API Error Handler", () => {
};
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");
expect(result.generalMessage).toContain("highlighted fields");
expect(result.fieldErrors.email).toMatch(/email/i);
expect(result.fieldErrors.full_name).toMatch(/name|short|character/i);
});
it("should parse 400 errors with message only", () => {
it("should map message-only 400 responses without exposing raw server text", () => {
const error = new Error("Bad Request") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
@@ -54,7 +54,47 @@ describe("API Error Handler", () => {
};
const result = parseApiError(error);
expect(result.generalMessage).toBe("Custom validation error message");
expect(result.generalMessage).not.toContain("Custom validation");
expect(result.generalMessage.length).toBeGreaterThan(10);
});
it("should parse nested error object with field errors", () => {
const error = new Error("Bad Request") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 400,
data: {
error: {
code: "validation_failed",
errors: { work_email: "invalid_email" },
},
},
statusText: "Bad Request",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.fieldErrors.work_email).toMatch(/email/i);
});
it("should parse FastAPI-style detail arrays", () => {
const error = new Error("Unprocessable") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 422,
data: {
detail: [
{ loc: ["body", "phone_number"], msg: "value is not a valid phone" },
],
},
statusText: "Unprocessable Entity",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.fieldErrors.phone_number).toMatch(/phone/i);
});
it("should handle 401 unauthorized errors", () => {
@@ -69,7 +109,7 @@ describe("API Error Handler", () => {
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Authentication required");
expect(result.generalMessage).toMatch(/Authentication|Sign in/i);
});
it("should handle 422 unprocessable entity errors", () => {
@@ -88,7 +128,7 @@ describe("API Error Handler", () => {
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Validation error");
expect(result.generalMessage).toContain("highlighted fields");
expect(result.fieldErrors.phone).toContain("phone number");
});
@@ -104,7 +144,7 @@ describe("API Error Handler", () => {
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Too many requests");
expect(result.generalMessage).toMatch(/many|wait/i);
});
it("should handle 500 server errors", () => {
@@ -119,7 +159,23 @@ describe("API Error Handler", () => {
};
const result = parseApiError(error);
expect(result.generalMessage).toContain("Server error");
expect(result.generalMessage).toMatch(/server|try again/i);
});
it("should give a client-safe message for unlisted 4xx statuses with a body", () => {
const error = new Error("Nope") as AxiosError;
error.isAxiosError = true;
(error as AxiosError).response = {
status: 418,
data: { message: "I'm a teapot" },
statusText: "I'm a teapot",
headers: {},
config: {} as any,
};
const result = parseApiError(error);
expect(result.generalMessage).not.toContain("teapot");
expect(result.generalMessage.length).toBeGreaterThan(5);
});
});
@@ -146,17 +202,15 @@ describe("API Error Handler", () => {
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."
expect(mockSetError).toHaveBeenCalledWith(
"email",
expect.objectContaining({ type: "server", message: expect.any(String) })
);
expect(mockSetError).toHaveBeenCalledWith(
"phone",
expect.objectContaining({ type: "server", message: expect.any(String) })
);
expect(mockShowToast).toHaveBeenCalledWith(expect.stringContaining("highlighted fields"));
});
it("should only set errors for fields in formFields list", () => {
@@ -181,10 +235,10 @@ describe("API Error Handler", () => {
handleApiError(error, mockSetError, mockShowToast, ["email"]);
expect(mockSetError).toHaveBeenCalledTimes(1);
expect(mockSetError).toHaveBeenCalledWith("email", {
type: "server",
message: "Invalid email",
});
expect(mockSetError).toHaveBeenCalledWith(
"email",
expect.objectContaining({ type: "server" })
);
});
it("should show general message when no field errors", () => {
@@ -204,7 +258,7 @@ describe("API Error Handler", () => {
handleApiError(error, mockSetError, mockShowToast, ["email"]);
expect(mockSetError).not.toHaveBeenCalled();
expect(mockShowToast).toHaveBeenCalledWith(expect.stringContaining("Server error"));
expect(mockShowToast).toHaveBeenCalledWith(expect.stringMatching(/server|try again/i));
});
});
});