import { screen, fireEvent, within } from "@testing-library/react"; import ClarificationContainer from "../ClarificationContainer"; import { useGetClarificationCopy } from "../hooks/useGetClarificationCopy"; import { useGetUnreadComments } from "../hooks/useGetUnreadComments"; import { MAX_COMMENTS } from "../constants"; import NewCommentIndicator from "../NewCommentIndicator"; import { buildCommentEntityKey } from "../utils"; import { renderWithTheme } from "../../../../testHelpers"; import { APPROVAL_REQUEST_STATES } from "../../../../constants"; import { renderHook } from "@testing-library/react-hooks"; import type { ApprovalClarificationFlowMessage } from "../../../../types"; jest.mock( "@zendeskgarden/svg-icons/src/12/circle-sm-fill.svg", () => "svg-mock" ); jest.mock("../hooks/useGetUnreadComments"); jest.mock("../NewCommentIndicator"); jest.mock("@zendeskgarden/svg-icons/src/16/headset-fill.svg", () => "svg-mock"); const createClarificationFlowMessage = ( index: number ): ApprovalClarificationFlowMessage => ({ id: `id-${index}`, author: { email: `user${index}@example.com`, id: 456, avatar: `https://i.pravatar.cc/150?img=${index}`, name: `User ${index}`, }, message: `Comment message ${index}`, created_at: "2024-01-01T12:00:00Z", }); describe("ClarificationContainer", () => { const mockMessages = Array.from({ length: 2 }, (_, i) => createClarificationFlowMessage(i + 1) ); const approvalRequestId = "req-123"; const baseLocale = "en-US"; const defaultProps = { baseLocale, approvalRequestId, currentUserId: 456, createdByUserId: 123, currentUserName: "Jane Doe", currentUserAvatarUrl: "https://example.com/avatar.png", status: APPROVAL_REQUEST_STATES.ACTIVE, clarificationFlowMessages: mockMessages, hasUserViewedBefore: true, }; beforeEach(() => { (useGetUnreadComments as jest.Mock).mockReturnValue({ unreadComments: [], firstUnreadCommentKey: null, markCommentAsVisible: jest.fn(), markAllCommentsAsRead: jest.fn(), }); (NewCommentIndicator as jest.Mock).mockImplementation(() => (
)); }); beforeAll(() => { class IntersectionObserverMock { callback: IntersectionObserverCallback; constructor(callback: IntersectionObserverCallback) { this.callback = callback; } observe(): void {} unobserve(): void {} disconnect(): void {} } Object.defineProperty(window, "IntersectionObserver", { writable: true, configurable: true, value: IntersectionObserverMock, }); Object.defineProperty(global, "IntersectionObserver", { writable: true, configurable: true, value: IntersectionObserverMock, }); }); it("renders title and description copy", () => { const { result } = renderHook(() => useGetClarificationCopy()); renderWithTheme(); expect(screen.getByText(result.current.title)).toBeInTheDocument(); expect(screen.getByText(result.current.description)).toBeInTheDocument(); }); it("does not display description if commenting is not allowed (terminal status)", () => { const { result } = renderHook(() => useGetClarificationCopy()); renderWithTheme( ); expect(screen.getByText(result.current.title)).toBeInTheDocument(); expect( screen.queryByText(result.current.description) ).not.toBeInTheDocument(); }); it("renders ClarificationComment components for each message", () => { renderWithTheme( ); const commentListArea = screen.getByTestId("comment-list-area"); mockMessages.forEach(({ message }) => { expect(within(commentListArea).getByText(message)).toBeInTheDocument(); }); }); it("renders NewCommentIndicator above the first unread comment if unread exists and not terminal", () => { const firstUnreadCommentKey = buildCommentEntityKey( approvalRequestId, mockMessages[0]! ); (useGetUnreadComments as jest.Mock).mockReturnValue({ unreadComments: [mockMessages[0]], firstUnreadCommentKey, markCommentAsVisible: jest.fn(), markAllCommentsAsRead: jest.fn(), }); renderWithTheme( ); expect(screen.getByTestId("new-comment-indicator")).toBeInTheDocument(); }); it("does not render NewCommentIndicator if status is terminal", () => { const firstUnreadCommentKey = buildCommentEntityKey( approvalRequestId, mockMessages[0]! ); (useGetUnreadComments as jest.Mock).mockReturnValue({ unreadComments: [mockMessages[0]], firstUnreadCommentKey, markCommentAsVisible: jest.fn(), markAllCommentsAsRead: jest.fn(), }); renderWithTheme( ); expect( screen.queryByTestId("new-comment-indicator") ).not.toBeInTheDocument(); }); it("renders ClarificationCommentForm if canComment is true", () => { const messages = Array.from({ length: MAX_COMMENTS - 1 }, (_, i) => createClarificationFlowMessage(i + 1) ); renderWithTheme( ); expect(screen.getByRole("textbox")).toBeInTheDocument(); }); it("calls markAllCommentsAsRead on window beforeunload event", () => { const markAllCommentsAsReadMock = jest.fn(); (useGetUnreadComments as jest.Mock).mockReturnValue({ unreadComments: [], firstUnreadCommentKey: null, markCommentAsVisible: jest.fn(), markAllCommentsAsRead: markAllCommentsAsReadMock, }); renderWithTheme(); fireEvent(window, new Event("beforeunload")); expect(markAllCommentsAsReadMock).toHaveBeenCalled(); }); it("cleans up beforeunload listener on unmount", () => { const addEventListenerSpy = jest.spyOn(window, "addEventListener"); const removeEventListenerSpy = jest.spyOn(window, "removeEventListener"); const { unmount } = renderWithTheme( ); expect(addEventListenerSpy).toHaveBeenCalledWith( "beforeunload", expect.any(Function) ); unmount(); expect(removeEventListenerSpy).toHaveBeenCalledWith( "beforeunload", expect.any(Function) ); }); });