From 6c69eda606e19e9420b9a768219599c4df560477 Mon Sep 17 00:00:00 2001 From: Blockchainrafik Date: Sun, 30 Aug 2026 08:49:23 +0100 Subject: [PATCH] =?UTF-8?q?Add=20DisputeRaiseModal=20=E2=80=94=20a11y,=20e?= =?UTF-8?q?mpty=20state,=20interactive=20states,=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/dispute-raise-modal.test.tsx | 924 +++++++++++++++++++++++++ app/components/DisputeRaiseModal.tsx | 417 +++++++++++ 2 files changed, 1341 insertions(+) create mode 100644 __tests__/dispute-raise-modal.test.tsx create mode 100644 app/components/DisputeRaiseModal.tsx diff --git a/__tests__/dispute-raise-modal.test.tsx b/__tests__/dispute-raise-modal.test.tsx new file mode 100644 index 0000000..24c25bf --- /dev/null +++ b/__tests__/dispute-raise-modal.test.tsx @@ -0,0 +1,924 @@ +/** + * Unit tests for DisputeRaiseModal + * + * Covers (issue #339): + * 1. Node rendering — modal open/closed guard + * 2. Empty-state node rendering (#333) — null milestoneIndex + * 3. Empty-state layout / design-token classes (#333) + * 4. Confirmation body — valid milestoneIndex + * 5. Milestone label helper (milestoneLabel) + * 6. ARIA attribute compliance (#330) + * - role="dialog", aria-modal, aria-labelledby, aria-describedby + * - role="alert" on warning banner + * - aria-label on action buttons + * - aria-disabled mirroring HTML disabled + * - aria-live="polite" on empty state + * - Escape key closes the modal + * - Backdrop click closes the modal + * 7. Interactive states / Tailwind classes (#331) + * - hover, focus-visible, disabled class presence + * - active:scale-[0.97] on action buttons + * - disabled:opacity-40, disabled:cursor-not-allowed + * 8. Action button behaviour + * - onConfirm / onClose callbacks + * - isPending disables both buttons and shows spinner label + * - no-handler (null milestoneIndex) disables confirm + * 9. Amount display + * 10. TxStatusBanner visibility + */ + +import { fireEvent, render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import DisputeRaiseModal, { + milestoneLabel, +} from "@/app/components/DisputeRaiseModal"; + +// --------------------------------------------------------------------------- +// Mocks — TxStatusBanner and ButtonSpinner are simple presentational +// components; we stub them to keep tests focused on DisputeRaiseModal logic. +// --------------------------------------------------------------------------- + +vi.mock("@/app/components/TxStatusBanner", () => ({ + default: ({ state, successMessage }: { state: { phase: string }; successMessage?: string }) => + state.phase !== "idle" ? ( +
+ {successMessage} +
+ ) : null, +})); + +vi.mock("@/app/components/ButtonSpinner", () => ({ + default: () =>