From 3cc5a8a50b398734671a22a21ebebbdcd2dc72b8 Mon Sep 17 00:00:00 2001 From: Lucano Vera Date: Fri, 19 Dec 2025 03:59:09 -0300 Subject: [PATCH 1/5] add request table actions test --- .../RequestTableActions.test.tsx | 454 ++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx diff --git a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx new file mode 100644 index 0000000000..d3652e40d4 --- /dev/null +++ b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx @@ -0,0 +1,454 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; + +import { PrivacyRequestResponse, PrivacyRequestStatus } from "~/types/api"; + +import { RequestTableActions } from "./RequestTableActions"; + +// Mock the mutations hook +const mockHandleApproveRequest = jest.fn(); +const mockHandleDenyRequest = jest.fn(); +const mockHandleDeleteRequest = jest.fn(); +const mockHandleFinalizeRequest = jest.fn(); +const mockIsLoading = jest.fn(() => false); + +jest.mock("./hooks/useMutations", () => ({ + useMutations: () => ({ + handleApproveRequest: mockHandleApproveRequest, + handleDenyRequest: mockHandleDenyRequest, + handleDeleteRequest: mockHandleDeleteRequest, + handleFinalizeRequest: mockHandleFinalizeRequest, + isLoading: mockIsLoading(), + }), +})); + +// Mock the config settings query +const mockUseGetConfigurationSettingsQuery = jest.fn(); +jest.mock("~/features/config-settings/config-settings.slice", () => ({ + useGetConfigurationSettingsQuery: () => + mockUseGetConfigurationSettingsQuery(), +})); + +// Mock the messaging provider query +const mockUseGetActiveMessagingProviderQuery = jest.fn(); +jest.mock("~/features/messaging/messaging.slice", () => ({ + useGetActiveMessagingProviderQuery: () => + mockUseGetActiveMessagingProviderQuery(), +})); + +// Mock Restrict component to always render children +jest.mock("~/features/common/Restrict", () => ({ + __esModule: true, + default: ({ children }: { children: React.ReactNode }) => children, +})); + +// Simple mock implementations for modals +const mockUseDisclosure = jest.fn(); + +jest.mock("fidesui", () => ({ + AntButton: ({ + children, + onClick, + loading, + disabled, + icon, + ...props + }: any) => ( + + ), + HStack: ({ children, ...props }: any) =>
{children}
, + Icons: { + Checkmark: () => , + Close: () => , + Stamp: () => 🔖, + TrashCan: ({ size }: any) => 🗑, + }, + Portal: ({ children }: any) =>
{children}
, + Text: ({ children }: any) => {children}, + useDisclosure: () => mockUseDisclosure(), +})); + +// Mock modal components but make them testable +jest.mock("./ApprovePrivacyRequestModal", () => ({ + __esModule: true, + default: ({ isOpen, onApproveRequest, onClose }: any) => + isOpen ? ( +
+ +
+ ) : null, +})); + +jest.mock("./DenyPrivacyRequestModal", () => ({ + __esModule: true, + default: ({ isOpen, onDenyRequest, onClose }: any) => + isOpen ? ( +
+ +
+ ) : null, +})); + +jest.mock("~/features/common/modals/ConfirmationModal", () => ({ + __esModule: true, + default: ({ isOpen, onConfirm, onClose }: any) => + isOpen ? ( +
+ +
+ ) : null, +})); + +// Helper to check button visibility +const expectButtonVisibility = ( + button: HTMLElement | null, + shouldBeVisible: boolean, +) => { + if (shouldBeVisible) { + expect(button).toBeInTheDocument(); + } else { + expect(button).not.toBeInTheDocument(); + } +}; + +describe("RequestTableActions", () => { + const baseRequest: PrivacyRequestResponse = { + id: "pri_123", + created_at: "2024-01-15T10:00:00Z", + status: PrivacyRequestStatus.PENDING, + } as PrivacyRequestResponse; + + const mockDisclosure = { + isOpen: false, + onOpen: jest.fn(), + onClose: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockDisclosure.isOpen = false; + mockUseDisclosure.mockReturnValue(mockDisclosure); + mockUseGetConfigurationSettingsQuery.mockReturnValue({ + data: { + notifications: { + send_request_completion_notification: true, + }, + }, + }); + mockUseGetActiveMessagingProviderQuery.mockReturnValue({ + data: { + service_type: "mailgun", + }, + }); + }); + + /** + * Action visibility matrix for all privacy request statuses. + * This table drives the parameterized tests below. + */ + const actionVisibilityByStatus = [ + { + status: PrivacyRequestStatus.PENDING, + approve: true, + deny: true, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.DUPLICATE, + approve: true, + deny: true, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.IDENTITY_UNVERIFIED, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.REQUIRES_INPUT, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.APPROVED, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.DENIED, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.IN_PROCESSING, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.COMPLETE, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.PAUSED, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.ERROR, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.CANCELED, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.AWAITING_EMAIL_SEND, + approve: false, + deny: false, + finalize: false, + delete: true, + }, + { + status: PrivacyRequestStatus.REQUIRES_MANUAL_FINALIZATION, + approve: false, + deny: false, + finalize: true, + delete: true, + }, + ]; + + describe.each(actionVisibilityByStatus)( + "Action visibility for $status", + ({ status, approve, deny, finalize, delete: deleteBtn }) => { + it("should show/hide all action buttons correctly", () => { + const request = { ...baseRequest, status }; + render(); + + const buttons = { + approve: screen.queryByTestId("privacy-request-approve-btn"), + deny: screen.queryByTestId("privacy-request-deny-btn"), + finalize: screen.queryByTestId("privacy-request-finalize-btn"), + delete: screen.queryByTestId("privacy-request-delete-btn"), + }; + + expectButtonVisibility(buttons.approve, approve); + expectButtonVisibility(buttons.deny, deny); + expectButtonVisibility(buttons.finalize, finalize); + expectButtonVisibility(buttons.delete, deleteBtn); + }); + }, + ); + + describe("Button interactions", () => { + it("should open approve modal when approve button is clicked", async () => { + const user = userEvent.setup(); + render(); + + const approveBtn = screen.getByTestId("privacy-request-approve-btn"); + await user.click(approveBtn); + + expect(mockDisclosure.onOpen).toHaveBeenCalledTimes(1); + }); + + it("should open deny modal when deny button is clicked", async () => { + const user = userEvent.setup(); + render(); + + const denyBtn = screen.getByTestId("privacy-request-deny-btn"); + await user.click(denyBtn); + + // Second disclosure for deny + expect(mockUseDisclosure).toHaveBeenCalled(); + }); + + it("should open delete modal when delete button is clicked", async () => { + const user = userEvent.setup(); + render(); + + const deleteBtn = screen.getByTestId("privacy-request-delete-btn"); + await user.click(deleteBtn); + + expect(mockUseDisclosure).toHaveBeenCalled(); + }); + + it("should open finalize modal when finalize button is clicked", async () => { + const user = userEvent.setup(); + const request = { + ...baseRequest, + status: PrivacyRequestStatus.REQUIRES_MANUAL_FINALIZATION, + }; + render(); + + const finalizeBtn = screen.getByTestId("privacy-request-finalize-btn"); + await user.click(finalizeBtn); + + expect(mockUseDisclosure).toHaveBeenCalled(); + }); + }); + + describe("Loading states", () => { + beforeEach(() => { + mockIsLoading.mockReturnValue(true); + }); + + it("should disable buttons when loading", () => { + render(); + + const approveBtn = screen.getByTestId("privacy-request-approve-btn"); + const denyBtn = screen.getByTestId("privacy-request-deny-btn"); + const deleteBtn = screen.getByTestId("privacy-request-delete-btn"); + + expect(approveBtn).toBeDisabled(); + expect(denyBtn).toBeDisabled(); + expect(deleteBtn).toBeDisabled(); + }); + + it("should show loading state on buttons", () => { + render(); + + const approveBtn = screen.getByTestId("privacy-request-approve-btn"); + expect(approveBtn).toHaveAttribute("aria-busy", "true"); + }); + }); + + describe("Modal interactions", () => { + it("should call handleApproveRequest when approve modal is confirmed", async () => { + const user = userEvent.setup(); + mockDisclosure.isOpen = true; + + render(); + + const confirmBtn = screen.getByTestId("approve-modal-confirm"); + await user.click(confirmBtn); + + expect(mockHandleApproveRequest).toHaveBeenCalledTimes(1); + }); + + it("should call handleDenyRequest when deny modal is confirmed", async () => { + const user = userEvent.setup(); + mockDisclosure.isOpen = true; + + render(); + + const confirmBtn = screen.getByTestId("deny-modal-confirm"); + await user.click(confirmBtn); + + expect(mockHandleDenyRequest).toHaveBeenCalledWith("Test reason"); + }); + + it("should call handleDeleteRequest when delete modal is confirmed", async () => { + const user = userEvent.setup(); + mockDisclosure.isOpen = true; + + render(); + + const confirmBtn = screen.getByTestId("confirmation-modal-confirm"); + await user.click(confirmBtn); + + expect(mockHandleDeleteRequest).toHaveBeenCalledTimes(1); + }); + + it("should call handleFinalizeRequest when finalize modal is confirmed", async () => { + const user = userEvent.setup(); + mockDisclosure.isOpen = true; + const request = { + ...baseRequest, + status: PrivacyRequestStatus.REQUIRES_MANUAL_FINALIZATION, + }; + + render(); + + const confirmBtn = screen.getByTestId("confirmation-modal-confirm"); + await user.click(confirmBtn); + + expect(mockHandleFinalizeRequest).toHaveBeenCalledTimes(1); + }); + }); + + describe("Keyboard navigation", () => { + it("should allow keyboard interaction with action buttons", async () => { + const user = userEvent.setup(); + render(); + + const approveBtn = screen.getByTestId("privacy-request-approve-btn"); + approveBtn.focus(); + expect(approveBtn).toHaveFocus(); + + await user.keyboard("{Enter}"); + expect(mockDisclosure.onOpen).toHaveBeenCalled(); + }); + + it("should allow tab navigation between buttons", async () => { + const user = userEvent.setup(); + render(); + + const approveBtn = screen.getByTestId("privacy-request-approve-btn"); + const denyBtn = screen.getByTestId("privacy-request-deny-btn"); + + approveBtn.focus(); + expect(approveBtn).toHaveFocus(); + + await user.tab(); + expect(denyBtn).toHaveFocus(); + }); + }); +}); From 5715df85da2eb06c446188fa4f014732abb56680 Mon Sep 17 00:00:00 2001 From: Lucano Vera Date: Fri, 19 Dec 2025 04:02:40 -0300 Subject: [PATCH 2/5] improve tests --- .../RequestTableActions.test.tsx | 119 ++++++++++++------ 1 file changed, 84 insertions(+), 35 deletions(-) diff --git a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx index d3652e40d4..9ccbd56339 100644 --- a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx +++ b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx @@ -42,9 +42,6 @@ jest.mock("~/features/common/Restrict", () => ({ default: ({ children }: { children: React.ReactNode }) => children, })); -// Simple mock implementations for modals -const mockUseDisclosure = jest.fn(); - jest.mock("fidesui", () => ({ AntButton: ({ children, @@ -74,7 +71,11 @@ jest.mock("fidesui", () => ({ }, Portal: ({ children }: any) =>
{children}
, Text: ({ children }: any) => {children}, - useDisclosure: () => mockUseDisclosure(), + useDisclosure: jest.fn(() => ({ + isOpen: false, + onOpen: jest.fn(), + onClose: jest.fn(), + })), })); // Mock modal components but make them testable @@ -154,16 +155,8 @@ describe("RequestTableActions", () => { status: PrivacyRequestStatus.PENDING, } as PrivacyRequestResponse; - const mockDisclosure = { - isOpen: false, - onOpen: jest.fn(), - onClose: jest.fn(), - }; - beforeEach(() => { jest.clearAllMocks(); - mockDisclosure.isOpen = false; - mockUseDisclosure.mockReturnValue(mockDisclosure); mockUseGetConfigurationSettingsQuery.mockReturnValue({ data: { notifications: { @@ -306,7 +299,8 @@ describe("RequestTableActions", () => { const approveBtn = screen.getByTestId("privacy-request-approve-btn"); await user.click(approveBtn); - expect(mockDisclosure.onOpen).toHaveBeenCalledTimes(1); + const modal = screen.getByTestId("approve-modal"); + expect(modal).toBeInTheDocument(); }); it("should open deny modal when deny button is clicked", async () => { @@ -316,8 +310,8 @@ describe("RequestTableActions", () => { const denyBtn = screen.getByTestId("privacy-request-deny-btn"); await user.click(denyBtn); - // Second disclosure for deny - expect(mockUseDisclosure).toHaveBeenCalled(); + const modal = screen.getByTestId("deny-modal"); + expect(modal).toBeInTheDocument(); }); it("should open delete modal when delete button is clicked", async () => { @@ -327,7 +321,8 @@ describe("RequestTableActions", () => { const deleteBtn = screen.getByTestId("privacy-request-delete-btn"); await user.click(deleteBtn); - expect(mockUseDisclosure).toHaveBeenCalled(); + const modal = screen.getByTestId("confirmation-modal"); + expect(modal).toBeInTheDocument(); }); it("should open finalize modal when finalize button is clicked", async () => { @@ -341,7 +336,8 @@ describe("RequestTableActions", () => { const finalizeBtn = screen.getByTestId("privacy-request-finalize-btn"); await user.click(finalizeBtn); - expect(mockUseDisclosure).toHaveBeenCalled(); + const modal = screen.getByTestId("confirmation-modal"); + expect(modal).toBeInTheDocument(); }); }); @@ -370,81 +366,134 @@ describe("RequestTableActions", () => { }); }); - describe("Modal interactions", () => { - it("should call handleApproveRequest when approve modal is confirmed", async () => { + describe("Complete user flows", () => { + it("should complete full approval flow from button click to request approval", async () => { const user = userEvent.setup(); - mockDisclosure.isOpen = true; - render(); + // Click approve button + const approveBtn = screen.getByTestId("privacy-request-approve-btn"); + await user.click(approveBtn); + + // Verify modal opens + const modal = screen.getByTestId("approve-modal"); + expect(modal).toBeInTheDocument(); + + // Confirm approval in modal const confirmBtn = screen.getByTestId("approve-modal-confirm"); await user.click(confirmBtn); + // Verify the mutation was called expect(mockHandleApproveRequest).toHaveBeenCalledTimes(1); + + // Verify modal closes + expect(screen.queryByTestId("approve-modal")).not.toBeInTheDocument(); }); - it("should call handleDenyRequest when deny modal is confirmed", async () => { + it("should complete full deny flow from button click to request denial", async () => { const user = userEvent.setup(); - mockDisclosure.isOpen = true; - render(); + // Click deny button + const denyBtn = screen.getByTestId("privacy-request-deny-btn"); + await user.click(denyBtn); + + // Verify modal opens + const modal = screen.getByTestId("deny-modal"); + expect(modal).toBeInTheDocument(); + + // Confirm denial in modal const confirmBtn = screen.getByTestId("deny-modal-confirm"); await user.click(confirmBtn); + // Verify the mutation was called with reason expect(mockHandleDenyRequest).toHaveBeenCalledWith("Test reason"); + + // Verify modal closes + expect(screen.queryByTestId("deny-modal")).not.toBeInTheDocument(); }); - it("should call handleDeleteRequest when delete modal is confirmed", async () => { + it("should complete full delete flow from button click to request deletion", async () => { const user = userEvent.setup(); - mockDisclosure.isOpen = true; - render(); + // Click delete button + const deleteBtn = screen.getByTestId("privacy-request-delete-btn"); + await user.click(deleteBtn); + + // Verify modal opens + const modal = screen.getByTestId("confirmation-modal"); + expect(modal).toBeInTheDocument(); + + // Confirm deletion in modal const confirmBtn = screen.getByTestId("confirmation-modal-confirm"); await user.click(confirmBtn); + // Verify the mutation was called expect(mockHandleDeleteRequest).toHaveBeenCalledTimes(1); + + // Verify modal closes + expect( + screen.queryByTestId("confirmation-modal"), + ).not.toBeInTheDocument(); }); - it("should call handleFinalizeRequest when finalize modal is confirmed", async () => { + it("should complete full finalize flow from button click to request finalization", async () => { const user = userEvent.setup(); - mockDisclosure.isOpen = true; const request = { ...baseRequest, status: PrivacyRequestStatus.REQUIRES_MANUAL_FINALIZATION, }; - render(); + // Click finalize button + const finalizeBtn = screen.getByTestId("privacy-request-finalize-btn"); + await user.click(finalizeBtn); + + // Verify modal opens + const modal = screen.getByTestId("confirmation-modal"); + expect(modal).toBeInTheDocument(); + + // Confirm finalization in modal const confirmBtn = screen.getByTestId("confirmation-modal-confirm"); await user.click(confirmBtn); + // Verify the mutation was called expect(mockHandleFinalizeRequest).toHaveBeenCalledTimes(1); + + // Verify modal closes + expect( + screen.queryByTestId("confirmation-modal"), + ).not.toBeInTheDocument(); }); }); describe("Keyboard navigation", () => { - it("should allow keyboard interaction with action buttons", async () => { + it("should open modal via keyboard interaction with approve button", async () => { const user = userEvent.setup(); render(); const approveBtn = screen.getByTestId("privacy-request-approve-btn"); - approveBtn.focus(); + + // Navigate to button and activate with Enter + await user.tab(); expect(approveBtn).toHaveFocus(); await user.keyboard("{Enter}"); - expect(mockDisclosure.onOpen).toHaveBeenCalled(); + + // Verify modal opens + const modal = screen.getByTestId("approve-modal"); + expect(modal).toBeInTheDocument(); }); - it("should allow tab navigation between buttons", async () => { + it("should allow tab navigation between action buttons", async () => { const user = userEvent.setup(); render(); const approveBtn = screen.getByTestId("privacy-request-approve-btn"); const denyBtn = screen.getByTestId("privacy-request-deny-btn"); - approveBtn.focus(); + await user.tab(); expect(approveBtn).toHaveFocus(); await user.tab(); From 705967f17dc7ce4443ef98a3e1b9e103486520f4 Mon Sep 17 00:00:00 2001 From: Lucano Vera Date: Fri, 19 Dec 2025 04:10:02 -0300 Subject: [PATCH 3/5] improve test --- .../RequestTableActions.test.tsx | 92 +++++++++++-------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx index 9ccbd56339..ce862a3a1b 100644 --- a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx +++ b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx @@ -1,5 +1,6 @@ import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import React from "react"; import { PrivacyRequestResponse, PrivacyRequestStatus } from "~/types/api"; @@ -10,7 +11,7 @@ const mockHandleApproveRequest = jest.fn(); const mockHandleDenyRequest = jest.fn(); const mockHandleDeleteRequest = jest.fn(); const mockHandleFinalizeRequest = jest.fn(); -const mockIsLoading = jest.fn(() => false); +let mockIsLoading = false; jest.mock("./hooks/useMutations", () => ({ useMutations: () => ({ @@ -18,7 +19,9 @@ jest.mock("./hooks/useMutations", () => ({ handleDenyRequest: mockHandleDenyRequest, handleDeleteRequest: mockHandleDeleteRequest, handleFinalizeRequest: mockHandleFinalizeRequest, - isLoading: mockIsLoading(), + get isLoading() { + return mockIsLoading; + }, }), })); @@ -42,41 +45,53 @@ jest.mock("~/features/common/Restrict", () => ({ default: ({ children }: { children: React.ReactNode }) => children, })); -jest.mock("fidesui", () => ({ - AntButton: ({ - children, - onClick, - loading, - disabled, - icon, - ...props - }: any) => ( - - ), - HStack: ({ children, ...props }: any) =>
{children}
, - Icons: { - Checkmark: () => , - Close: () => , - Stamp: () => 🔖, - TrashCan: ({ size }: any) => 🗑, - }, - Portal: ({ children }: any) =>
{children}
, - Text: ({ children }: any) => {children}, - useDisclosure: jest.fn(() => ({ - isOpen: false, - onOpen: jest.fn(), - onClose: jest.fn(), - })), -})); +// Mock fidesui components for testing +jest.mock("fidesui", () => { + // We need to import React inside the factory + const react = require("react"); + + return { + AntButton: ({ + children, + onClick, + loading, + disabled, + icon, + ...props + }: any) => ( + + ), + HStack: ({ children, ...props }: any) =>
{children}
, + Icons: { + Checkmark: () => , + Close: () => , + Stamp: () => 🔖, + TrashCan: ({ size }: any) => 🗑, + }, + Portal: ({ children }: any) =>
{children}
, + Text: ({ children }: any) => {children}, + useDisclosure: () => { + const [isOpen, setIsOpen] = react.useState(false); + + return { + isOpen, + onOpen: () => setIsOpen(true), + onClose: () => setIsOpen(false), + }; + }, + }; +}); + +// Note: fidesui is mocked globally in jest.setup.ts // Mock modal components but make them testable jest.mock("./ApprovePrivacyRequestModal", () => ({ @@ -157,6 +172,7 @@ describe("RequestTableActions", () => { beforeEach(() => { jest.clearAllMocks(); + mockIsLoading = false; // Reset loading state before each test mockUseGetConfigurationSettingsQuery.mockReturnValue({ data: { notifications: { @@ -343,7 +359,7 @@ describe("RequestTableActions", () => { describe("Loading states", () => { beforeEach(() => { - mockIsLoading.mockReturnValue(true); + mockIsLoading = true; }); it("should disable buttons when loading", () => { From 41580696e3a9d058f4ce461093a276a9edb4ed7e Mon Sep 17 00:00:00 2001 From: Lucano Vera Date: Fri, 19 Dec 2025 04:13:02 -0300 Subject: [PATCH 4/5] remove unnecessary comments --- .../RequestTableActions.test.tsx | 88 +------------------ 1 file changed, 1 insertion(+), 87 deletions(-) diff --git a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx index ce862a3a1b..e4fead16f3 100644 --- a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx +++ b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx @@ -6,7 +6,6 @@ import { PrivacyRequestResponse, PrivacyRequestStatus } from "~/types/api"; import { RequestTableActions } from "./RequestTableActions"; -// Mock the mutations hook const mockHandleApproveRequest = jest.fn(); const mockHandleDenyRequest = jest.fn(); const mockHandleDeleteRequest = jest.fn(); @@ -25,29 +24,24 @@ jest.mock("./hooks/useMutations", () => ({ }), })); -// Mock the config settings query const mockUseGetConfigurationSettingsQuery = jest.fn(); jest.mock("~/features/config-settings/config-settings.slice", () => ({ useGetConfigurationSettingsQuery: () => mockUseGetConfigurationSettingsQuery(), })); -// Mock the messaging provider query const mockUseGetActiveMessagingProviderQuery = jest.fn(); jest.mock("~/features/messaging/messaging.slice", () => ({ useGetActiveMessagingProviderQuery: () => mockUseGetActiveMessagingProviderQuery(), })); -// Mock Restrict component to always render children jest.mock("~/features/common/Restrict", () => ({ __esModule: true, default: ({ children }: { children: React.ReactNode }) => children, })); -// Mock fidesui components for testing jest.mock("fidesui", () => { - // We need to import React inside the factory const react = require("react"); return { @@ -91,9 +85,6 @@ jest.mock("fidesui", () => { }; }); -// Note: fidesui is mocked globally in jest.setup.ts - -// Mock modal components but make them testable jest.mock("./ApprovePrivacyRequestModal", () => ({ __esModule: true, default: ({ isOpen, onApproveRequest, onClose }: any) => @@ -151,7 +142,6 @@ jest.mock("~/features/common/modals/ConfirmationModal", () => ({ ) : null, })); -// Helper to check button visibility const expectButtonVisibility = ( button: HTMLElement | null, shouldBeVisible: boolean, @@ -172,7 +162,7 @@ describe("RequestTableActions", () => { beforeEach(() => { jest.clearAllMocks(); - mockIsLoading = false; // Reset loading state before each test + mockIsLoading = false; mockUseGetConfigurationSettingsQuery.mockReturnValue({ data: { notifications: { @@ -307,56 +297,6 @@ describe("RequestTableActions", () => { }, ); - describe("Button interactions", () => { - it("should open approve modal when approve button is clicked", async () => { - const user = userEvent.setup(); - render(); - - const approveBtn = screen.getByTestId("privacy-request-approve-btn"); - await user.click(approveBtn); - - const modal = screen.getByTestId("approve-modal"); - expect(modal).toBeInTheDocument(); - }); - - it("should open deny modal when deny button is clicked", async () => { - const user = userEvent.setup(); - render(); - - const denyBtn = screen.getByTestId("privacy-request-deny-btn"); - await user.click(denyBtn); - - const modal = screen.getByTestId("deny-modal"); - expect(modal).toBeInTheDocument(); - }); - - it("should open delete modal when delete button is clicked", async () => { - const user = userEvent.setup(); - render(); - - const deleteBtn = screen.getByTestId("privacy-request-delete-btn"); - await user.click(deleteBtn); - - const modal = screen.getByTestId("confirmation-modal"); - expect(modal).toBeInTheDocument(); - }); - - it("should open finalize modal when finalize button is clicked", async () => { - const user = userEvent.setup(); - const request = { - ...baseRequest, - status: PrivacyRequestStatus.REQUIRES_MANUAL_FINALIZATION, - }; - render(); - - const finalizeBtn = screen.getByTestId("privacy-request-finalize-btn"); - await user.click(finalizeBtn); - - const modal = screen.getByTestId("confirmation-modal"); - expect(modal).toBeInTheDocument(); - }); - }); - describe("Loading states", () => { beforeEach(() => { mockIsLoading = true; @@ -387,22 +327,16 @@ describe("RequestTableActions", () => { const user = userEvent.setup(); render(); - // Click approve button const approveBtn = screen.getByTestId("privacy-request-approve-btn"); await user.click(approveBtn); - // Verify modal opens const modal = screen.getByTestId("approve-modal"); expect(modal).toBeInTheDocument(); - // Confirm approval in modal const confirmBtn = screen.getByTestId("approve-modal-confirm"); await user.click(confirmBtn); - // Verify the mutation was called expect(mockHandleApproveRequest).toHaveBeenCalledTimes(1); - - // Verify modal closes expect(screen.queryByTestId("approve-modal")).not.toBeInTheDocument(); }); @@ -410,22 +344,16 @@ describe("RequestTableActions", () => { const user = userEvent.setup(); render(); - // Click deny button const denyBtn = screen.getByTestId("privacy-request-deny-btn"); await user.click(denyBtn); - // Verify modal opens const modal = screen.getByTestId("deny-modal"); expect(modal).toBeInTheDocument(); - // Confirm denial in modal const confirmBtn = screen.getByTestId("deny-modal-confirm"); await user.click(confirmBtn); - // Verify the mutation was called with reason expect(mockHandleDenyRequest).toHaveBeenCalledWith("Test reason"); - - // Verify modal closes expect(screen.queryByTestId("deny-modal")).not.toBeInTheDocument(); }); @@ -433,22 +361,16 @@ describe("RequestTableActions", () => { const user = userEvent.setup(); render(); - // Click delete button const deleteBtn = screen.getByTestId("privacy-request-delete-btn"); await user.click(deleteBtn); - // Verify modal opens const modal = screen.getByTestId("confirmation-modal"); expect(modal).toBeInTheDocument(); - // Confirm deletion in modal const confirmBtn = screen.getByTestId("confirmation-modal-confirm"); await user.click(confirmBtn); - // Verify the mutation was called expect(mockHandleDeleteRequest).toHaveBeenCalledTimes(1); - - // Verify modal closes expect( screen.queryByTestId("confirmation-modal"), ).not.toBeInTheDocument(); @@ -462,22 +384,16 @@ describe("RequestTableActions", () => { }; render(); - // Click finalize button const finalizeBtn = screen.getByTestId("privacy-request-finalize-btn"); await user.click(finalizeBtn); - // Verify modal opens const modal = screen.getByTestId("confirmation-modal"); expect(modal).toBeInTheDocument(); - // Confirm finalization in modal const confirmBtn = screen.getByTestId("confirmation-modal-confirm"); await user.click(confirmBtn); - // Verify the mutation was called expect(mockHandleFinalizeRequest).toHaveBeenCalledTimes(1); - - // Verify modal closes expect( screen.queryByTestId("confirmation-modal"), ).not.toBeInTheDocument(); @@ -491,13 +407,11 @@ describe("RequestTableActions", () => { const approveBtn = screen.getByTestId("privacy-request-approve-btn"); - // Navigate to button and activate with Enter await user.tab(); expect(approveBtn).toHaveFocus(); await user.keyboard("{Enter}"); - // Verify modal opens const modal = screen.getByTestId("approve-modal"); expect(modal).toBeInTheDocument(); }); From b9c8c018ac6dcac458e00a3315c47275c0c72f96 Mon Sep 17 00:00:00 2001 From: Lucano Vera Date: Fri, 19 Dec 2025 04:17:01 -0300 Subject: [PATCH 5/5] fix tests --- .../src/features/privacy-requests/RequestTableActions.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx index e4fead16f3..2a2ee0ae34 100644 --- a/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx +++ b/clients/admin-ui/src/features/privacy-requests/RequestTableActions.test.tsx @@ -42,6 +42,7 @@ jest.mock("~/features/common/Restrict", () => ({ })); jest.mock("fidesui", () => { + // eslint-disable-next-line global-require const react = require("react"); return {