fix(ui): handle alert form errors (#11623)

This commit is contained in:
Alejandro Bailo
2026-06-16 17:44:48 +02:00
committed by GitHub
parent 8bc42a5ded
commit 262dfda0aa
4 changed files with 103 additions and 5 deletions
@@ -701,6 +701,31 @@ describe("AlertFormModal", () => {
expect(errorMessage).toHaveClass("text-text-error-primary");
});
it("should clear the preview error when save shows the form error", async () => {
// Given
const user = userEvent.setup();
alertsActionMocks.seedAlertRule.mockResolvedValue({
error: "No alert-compatible filters",
});
mockRecipientsList();
renderCreateModal({ editingAlert: createEditingAlert() });
// When
await user.click(screen.getByRole("button", { name: /^test$/i }));
expect(await screen.findByText("Test result")).toBeVisible();
await user.click(screen.getByRole("button", { name: /^save$/i }));
// Then
await waitFor(() =>
expect(screen.queryByText("Test result")).not.toBeInTheDocument(),
);
expect(
screen.getAllByText(
"Apply at least one alert-compatible Findings filter.",
),
).toHaveLength(1);
});
it("should hydrate advanced edit mode filters and normalize them on save", async () => {
// Given
const user = userEvent.setup();
@@ -1,6 +1,6 @@
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { isValidElement, type ReactNode } from "react";
import { isValidElement, type ReactNode, useState } from "react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
@@ -8,6 +8,10 @@ import {
ALERT_TRIGGER_KINDS,
type AlertRule,
} from "@/app/(prowler)/alerts/_types";
import type {
AlertFormSubmitResult,
AlertFormValues,
} from "@/app/(prowler)/alerts/_types/alert-form";
import { AlertsManager } from "../alerts-manager";
@@ -96,12 +100,32 @@ vi.mock("../alert-form-modal", () => ({
open,
editingAlert,
onOpenChange,
onSubmit,
}: {
open: boolean;
editingAlert?: AlertRule | null;
onOpenChange: (open: boolean) => void;
}) =>
open ? (
onSubmit: (values: AlertFormValues) => Promise<AlertFormSubmitResult>;
}) => {
const [error, setError] = useState<string | null>(null);
const submit = async () => {
const result = await onSubmit({
name: "Updated alert",
description: "",
method: "email",
frequency: ALERT_TRIGGER_KINDS.AFTER_SCAN,
condition: {
op: ALERT_AGGREGATE_OPS.ANY,
filter: { severity: ["critical"] },
},
recipientEmails: [],
enabled: true,
});
setError(result.ok ? null : (result.error ?? null));
};
return open ? (
<div
role="dialog"
aria-label={editingAlert ? "Edit Alert" : "Create Alert"}
@@ -109,9 +133,14 @@ vi.mock("../alert-form-modal", () => ({
<button type="button" onClick={() => onOpenChange(false)}>
Close modal
</button>
<button type="button" onClick={submit}>
Submit alert
</button>
{editingAlert?.attributes.name}
{error && <p>{error}</p>}
</div>
) : null,
) : null;
},
}));
vi.mock("../alerts-empty-state", () => ({
@@ -259,6 +288,42 @@ describe("AlertsManager", () => {
});
});
it("shows a manage alerts permission message for edit 403 errors", async () => {
// Given
const user = userEvent.setup();
const alert = makeAlert(true);
actionMocks.updateAlert.mockResolvedValue({
error: "You do not have permission to perform this action.",
status: 403,
});
render(
<AlertsManager
alerts={[alert]}
loadError={null}
providers={[]}
completedScanIds={[]}
scanDetails={[]}
uniqueRegions={[]}
uniqueServices={[]}
uniqueResourceTypes={[]}
uniqueCategories={[]}
uniqueGroups={[]}
initialEditingAlert={alert}
/>,
);
// When
await user.click(screen.getByRole("button", { name: /submit alert/i }));
// Then
expect(
await screen.findByText(
"You don't have permission to manage alerts. Ask an administrator to update your role.",
),
).toBeVisible();
expect(toastMock).not.toHaveBeenCalled();
});
it("shows a success toast after disabling an alert", async () => {
// Given
const user = userEvent.setup();
@@ -451,6 +451,7 @@ const AlertFormModalContent = ({
? await seedAlertRule(pendingFilters)
: null;
if (seedResult?.error) {
setPreview(null);
setErrors({ root: ALERT_SEED_ERROR });
return;
}
@@ -49,6 +49,8 @@ interface AlertsManagerProps {
const ALERTS_FINDINGS_HREF =
"/findings?filter[muted]=false&filter[status__in]=FAIL";
const ALERTS_PERMISSION_ERROR =
"You don't have permission to manage alerts. Ask an administrator to update your role.";
export const AlertsManager = ({
alerts,
@@ -108,7 +110,12 @@ export const AlertsManager = ({
}
const payload = toAlertPayload(values);
const result = await updateAlert(editingAlert.id, payload);
if (result?.error) return { ok: false, error: result.error };
if (result?.error) {
return {
ok: false,
error: result.status === 403 ? ALERTS_PERMISSION_ERROR : result.error,
};
}
toast({
title: "Alert updated",
description: result.data.attributes.name,