Files
copenlight/src/modules/approval-requests/components/approval-request/clarification/tests/ClarificationContainer.test.tsx
Ivan Carlos de Almeida 6fa41a771d
Some checks failed
Build, Push, Publish / Build & Release (push) Failing after 2s
Sync Repo / sync (push) Failing after 2s
first load
2025-12-16 04:40:00 -03:00

228 lines
6.7 KiB
TypeScript

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(() => (
<div data-testid="new-comment-indicator" />
));
});
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(<ClarificationContainer {...defaultProps} />);
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(
<ClarificationContainer {...defaultProps} status="approved" />
);
expect(screen.getByText(result.current.title)).toBeInTheDocument();
expect(
screen.queryByText(result.current.description)
).not.toBeInTheDocument();
});
it("renders ClarificationComment components for each message", () => {
renderWithTheme(
<ClarificationContainer
{...defaultProps}
clarificationFlowMessages={mockMessages}
/>
);
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(
<ClarificationContainer
{...defaultProps}
clarificationFlowMessages={mockMessages}
/>
);
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(
<ClarificationContainer
{...defaultProps}
status="approved"
clarificationFlowMessages={mockMessages}
/>
);
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(
<ClarificationContainer
{...defaultProps}
clarificationFlowMessages={messages}
/>
);
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(<ClarificationContainer {...defaultProps} />);
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(
<ClarificationContainer {...defaultProps} />
);
expect(addEventListenerSpy).toHaveBeenCalledWith(
"beforeunload",
expect.any(Function)
);
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith(
"beforeunload",
expect.any(Function)
);
});
});