228 lines
6.7 KiB
TypeScript
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)
|
|
);
|
|
});
|
|
});
|