Repository: tjtanjin/react-chatbotify
Pull request #178 (2024-10-10 00:42:52)

test: add unit test for ChatHistoryLineBreak component
#### Description

Added unit test for ChatHistoryLineBreak component.

Closes [#161](https://github.com/tjtanjin/react-chatbotify/issues/161)

#### What change does this PR introduce?

Please select the relevant option(s).

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update (changes to docs/code comments)
- [x] Unit test

#### What is the proposed approach?

Implement unit test using Jest and react testing library.

#### Checklist:

- [x] The commit message follows our adopted [guidelines](https://www.conventionalcommits.org/en/v1.0.0/)
- [x] Testing has been done for the change(s) added (for bug fixes/features)
- [x] Relevant comments/docs have been added/updated (for bug fixes/features)


Patch:
diff --git a/src/components/ChatBotTooltip/ChatBotTooltip.tsx b/src/components/ChatBotTooltip/ChatBotTooltip.tsx
index 0e4b676..28d2cc9 100644
--- a/src/components/ChatBotTooltip/ChatBotTooltip.tsx
+++ b/src/components/ChatBotTooltip/ChatBotTooltip.tsx
@@ -87,6 +87,7 @@ const ChatBotTooltip = () => {
 		<>
 			{!settings.general?.embedded &&
 				<div 
+					data-testid="chat-tooltip"
 					style={tooltipStyle}
 					className={`rcb-chat-tooltip ${showTooltip ? "rcb-tooltip-show" : "rcb-tooltip-hide"}`}
 					onClick={() => openChat(true)}
diff --git a/src/components/ChatHistoryLineBreak/ChatHistoryLineBreak.tsx b/src/components/ChatHistoryLineBreak/ChatHistoryLineBreak.tsx
index 94d9fd2..5d7bfd6 100644
--- a/src/components/ChatHistoryLineBreak/ChatHistoryLineBreak.tsx
+++ b/src/components/ChatHistoryLineBreak/ChatHistoryLineBreak.tsx
@@ -16,7 +16,11 @@ const ChatHistoryLineBreak = () => {
 
 	return (
 		<div className="rcb-line-break-container">
-			<div style={styles.chatHistoryLineBreakStyle} className="rcb-line-break-text">
+			<div
+				style={styles.chatHistoryLineBreakStyle}
+				className="rcb-line-break-text"
+				data-testid="chat-history-line-break-text"
+			>
 				{settings.chatHistory?.chatHistoryLineBreakText}
 			</div>
 		</div>


Test patch:
diff --git a/__tests__/components/ChatBotTooltip/ChatBotTooltip.test.tsx b/__tests__/components/ChatBotTooltip/ChatBotTooltip.test.tsx
new file mode 100644
index 0000000..961633d
--- /dev/null
+++ b/__tests__/components/ChatBotTooltip/ChatBotTooltip.test.tsx
@@ -0,0 +1,121 @@
+import React from "react";
+import "@testing-library/jest-dom"; 
+import { render, screen, fireEvent } from "@testing-library/react";
+import ChatBotTooltip from "../../../src/components/ChatBotTooltip/ChatBotTooltip";
+import { useIsDesktopInternal } from "../../../src/hooks/internal/useIsDesktopInternal";
+import { useChatWindowInternal } from "../../../src/hooks/internal/useChatWindowInternal";
+import { useSettingsContext } from "../../../src/context/SettingsContext";
+import { useStylesContext } from "../../../src/context/StylesContext";
+
+// Mock the contexts and hooks
+jest.mock("../../../src/hooks/internal/useIsDesktopInternal");
+jest.mock("../../../src/hooks/internal/useChatWindowInternal");
+jest.mock("../../../src/context/SettingsContext");
+jest.mock("../../../src/context/StylesContext");
+
+describe("ChatBotTooltip Component", () => {
+	// Set up default mock return values before each test
+	beforeEach(() => {
+		(useIsDesktopInternal as jest.Mock).mockReturnValue(true); 
+		(useChatWindowInternal as jest.Mock).mockReturnValue({
+			isChatWindowOpen: false,
+			openChat: jest.fn(), 
+		});
+		(useSettingsContext as jest.Mock).mockReturnValue({
+			settings: {
+				general: { primaryColor: "blue", secondaryColor: "#000", embedded: false },
+				tooltip: { text: "Chatbot Tooltip" }, 
+			},
+		});
+		(useStylesContext as jest.Mock).mockReturnValue({
+			styles: {
+				chatButtonStyle: { width: 75 },
+				chatWindowStyle: { width: 375 },
+				tooltipStyle: {}, 
+			},
+		});
+	}); 
+
+	// Clear all mocks after each test case to avoid test interference
+	afterEach(() => {
+		jest.clearAllMocks();
+	});
+
+	// Test: Render the tooltip when mode is "ALWAYS" and on desktop
+	it('renders tooltip when mode is "ALWAYS" and isDesktop is true', () => {
+		// Mock the context to return settings with mode "ALWAYS"
+		(useSettingsContext as jest.Mock).mockReturnValueOnce({
+			settings: {
+				tooltip: { mode: "ALWAYS", text: "Chatbot Tooltip" },
+			},
+		});
+
+		// Render the component and ensure the tooltip is shown
+		render(<ChatBotTooltip />);
+		const tooltip = screen.getByTestId("chat-tooltip");
+		expect(tooltip).toBeInTheDocument(); 
+		expect(tooltip).toHaveClass("rcb-tooltip-show"); 
+	});
+
+	// Test: Ensure the tooltip is hidden when mode is "NEVER"
+	it('hides tooltip when mode is "NEVER"', () => {
+		// Mock the context to return settings with mode "NEVER"
+		(useSettingsContext as jest.Mock).mockReturnValueOnce({
+			settings: {
+				tooltip: { mode: "NEVER", text: "Chatbot Tooltip" },
+			},
+		});
+
+		// Render the component and ensure the tooltip is rendered but hidden
+		render(<ChatBotTooltip />);
+		const tooltip = screen.getByTestId("chat-tooltip");
+		expect(tooltip).toBeInTheDocument();
+		expect(tooltip).toHaveClass("rcb-tooltip-hide");
+	});
+
+	// Test: Show the tooltip when mode is "START" (when component is rendered for the first time)
+	it('shows tooltip when mode is "START" and shownTooltipOnStart is false', () => {
+		// Mock the context to return settings with mode "START"
+		(useSettingsContext as jest.Mock).mockReturnValueOnce({
+			settings: {
+				tooltip: { mode: "START", text: "Chatbot Tooltip" },
+			},
+		});
+
+		// Render the component and ensure the tooltip is shown
+		render(<ChatBotTooltip />);
+		const tooltip = screen.getByText("Chatbot Tooltip");
+		expect(tooltip).toBeInTheDocument(); 
+	});
+
+	// Test: Ensure the tooltip shows when mode is "CLOSE"
+	it('shows tooltip when mode is "CLOSE"', () => {
+		// Mock the context to return settings with mode "CLOSE"
+		(useSettingsContext as jest.Mock).mockReturnValueOnce({
+			settings: {
+				tooltip: { mode: "CLOSE", text: "Chatbot Tooltip" },
+			},
+		});
+
+		// Render the component and ensure the tooltip is shown
+		render(<ChatBotTooltip />);
+		const tooltip = screen.getByText("Chatbot Tooltip");
+		expect(tooltip).toBeInTheDocument();
+	});
+
+	// Test: Ensure clicking the tooltip calls the openChat function
+	it("calls openChat function when tooltip is clicked", () => {
+		// Mock the function that opens the chat
+		const mockOpenChat = jest.fn();
+		(useChatWindowInternal as jest.Mock).mockReturnValue({
+			isChatWindowOpen: false,
+			openChat: mockOpenChat,
+		});
+
+		// Render the component, simulate click event and verify that openChat is called
+		render(<ChatBotTooltip />);
+		const tooltip = screen.getByText("Chatbot Tooltip");
+		fireEvent.click(tooltip); 
+		expect(mockOpenChat).toHaveBeenCalledWith(true); 
+	});
+});
diff --git a/__tests__/components/ChatHistoryLineBreak/ChatHistoryLineBreak.test.tsx b/__tests__/components/ChatHistoryLineBreak/ChatHistoryLineBreak.test.tsx
new file mode 100644
index 0000000..e2deab7
--- /dev/null
+++ b/__tests__/components/ChatHistoryLineBreak/ChatHistoryLineBreak.test.tsx
@@ -0,0 +1,87 @@
+import React from "react";
+
+import { expect } from "@jest/globals";
+import { render, screen} from "@testing-library/react";
+import "@testing-library/jest-dom/jest-globals";
+
+import ChatHistoryLineBreak from "../../../src/components/ChatHistoryLineBreak/ChatHistoryLineBreak";
+import { useSettingsContext } from "../../../src/context/SettingsContext";
+import { useStylesContext } from "../../../src/context/StylesContext";
+import { DefaultStyles } from "../../../src/constants/internal/DefaultStyles";
+import { DefaultSettings } from "../../../src/constants/internal/DefaultSettings";
+
+jest.mock("../../../src/context/SettingsContext");
+jest.mock("../../../src/context/StylesContext");
+
+/**
+ * Test for ChatHistoryLineBreak component.
+ */
+describe("ChatHistoryLineBreak Component", () => {
+	const mockText = DefaultSettings.chatHistory?.chatHistoryLineBreakText ?? "test break line";
+	const mockColor = DefaultStyles.chatHistoryLineBreakStyle?.color ?? "blue";
+	
+	// Mock default settings and styles before each test
+	beforeEach(() => {
+		(useSettingsContext as jest.Mock).mockReturnValue({
+			settings: {
+				chatHistory: {
+					chatHistoryLineBreakText: mockText
+				}
+			}
+		});
+
+		(useStylesContext as jest.Mock).mockReturnValue({
+			styles: {
+				chatHistoryLineBreakStyle: {
+					color: mockColor
+				}
+			}
+		});
+	});
+
+	it("renders mock line break text and style", () => {
+		render(<ChatHistoryLineBreak />);
+
+		// check if the default text is rendered
+		const lineBreak = screen.getByText(mockText);
+		expect(lineBreak).toBeInTheDocument();
+
+		// check if line break color is correct
+		expect(lineBreak).toHaveStyle(`color: ${mockColor}`);
+	});
+
+	it("renders empty div when chatHistoryLineBreakText is not provided", () => {
+		// Mock settings without chatHistoryLineBreakText
+		(useSettingsContext as jest.Mock).mockReturnValue({
+			settings: {
+				chatHistory: {} // Simulate missing chatHistoryLineBreakText
+			}
+		});
+
+		render(<ChatHistoryLineBreak />);
+
+		// check that line break div exists but text is empty
+		const lineBreak = screen.getByTestId("chat-history-line-break-text");
+		expect(lineBreak).toBeInTheDocument();
+		expect(lineBreak).toBeEmptyDOMElement();
+
+		// check if line break color is applied even when chatHistoryLineBreakText is empty
+		expect(lineBreak).toHaveStyle(`color: ${DefaultStyles.chatHistoryLineBreakStyle?.color ?? mockColor}`);
+	});
+
+	it("renders empty when chatHistory is not provided", () => {
+		(useSettingsContext as jest.Mock).mockReturnValue({
+			settings: {}
+		});
+
+		render(<ChatHistoryLineBreak />);
+
+		// check that line break div exists but text is empty
+		const lineBreak = screen.getByTestId("chat-history-line-break-text");
+		expect(lineBreak).toBeInTheDocument();
+		expect(lineBreak).toBeEmptyDOMElement();
+
+		// check line break color is applied even when chatHistory is empty
+		expect(lineBreak).toHaveStyle(`color: ${DefaultStyles.chatHistoryLineBreakStyle?.color ?? mockColor}`);
+	});
+});