Repository: tjtanjin/react-chatbotify (ts)

Issue:
[Task] Add unit test cases for ChatBotFooter component
**Task Description:**
Currently, we run unit tests using jest which can be found [**here**](https://github.com/tjtanjin/react-chatbotify/tree/main/__tests__). 

However, the test cases are not yet comprehensive enough and work can be done to cover more grounds. Specifically for this issue, we hope to add test cases for the `ChatBotFooter` component.

**Deliverable(s):**
New unit test cases that cover the testing of the `ChatBotFooter` component.

**Additional Context:**
- The `ChatBotFooter` components can be found [**here**](https://github.com/tjtanjin/react-chatbotify/tree/main/src/components/ChatBotFooter)
- An example for `AudioButton` can be found [**here**](https://github.com/tjtanjin/react-chatbotify/blob/main/__tests__/components/AudioButton.test.tsx) which may serve as a good source for reference


Pull request description:
test: Add the unit test for chatbot footer component
#### Description

Added the Unit Test for the Chat Bot Footer with simple tests on buttons, icons, and text.

Closes #158 

#### 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?

Added the unit test for Footer component with jest and react.

#### 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/ChatBotFooter/ChatBotFooter.tsx b/src/components/ChatBotFooter/ChatBotFooter.tsx
index f821011..712dd34 100644
--- a/src/components/ChatBotFooter/ChatBotFooter.tsx
+++ b/src/components/ChatBotFooter/ChatBotFooter.tsx
@@ -19,9 +19,9 @@ const ChatBotFooter = ({ buttons }: { buttons: JSX.Element[] }) => {
 	const { styles } = useStylesContext();
 
 	return (
-		<div style={styles.footerStyle} className="rcb-chat-footer-container">
+		<div data-testid="chatbot-footer-container" style={styles.footerStyle} className="rcb-chat-footer-container">
 			<div className="rcb-chat-footer">
-				{buttons?.map((button: JSX.Element, index: number) => 
+				{buttons?.map((button: JSX.Element, index: number) =>
 					<Fragment key={index}>{button}</Fragment>
 				)}
 			</div>


Test patch:
diff --git a/__tests__/__mocks__/fileMock.ts b/__tests__/__mocks__/fileMock.ts
index b5d78d1..4c54900 100644
--- a/__tests__/__mocks__/fileMock.ts
+++ b/__tests__/__mocks__/fileMock.ts
@@ -1,2 +1,2 @@
 // __mocks__/fileMock.ts
-export default "";
\ No newline at end of file
+export const botAvatar = "../../assets/bot_avatar.svg";
\ No newline at end of file
diff --git a/__tests__/components/ChatBotFooter/ChatBotFooter.test.tsx b/__tests__/components/ChatBotFooter/ChatBotFooter.test.tsx
new file mode 100644
index 0000000..af8da60
--- /dev/null
+++ b/__tests__/components/ChatBotFooter/ChatBotFooter.test.tsx
@@ -0,0 +1,51 @@
+import React from "react";
+
+import { expect } from "@jest/globals";
+import { render, screen } from "@testing-library/react";
+import "@testing-library/jest-dom/jest-globals";
+
+import ChatBotFooter from "../../../src/components/ChatBotFooter/ChatBotFooter"
+
+/**
+ * Helper function to render AudioButton with different settings.
+ *
+ * @param initialSettings initial settings for the TestChatBotProvider
+ */
+/**
+ * Contains footer buttons and text.
+ * 
+ * @param buttons list of buttons to render in the footer
+ */
+
+
+const renderChatBotFooter = ({ buttons }: { buttons: JSX.Element[] }) => {
+	return render(
+
+			<ChatBotFooter buttons={buttons}></ChatBotFooter>
+
+	);
+};
+
+
+describe("ChatBotFooter Component", () => {
+	it("renders the chatbotfooter container component", () => {
+		const buttons = [
+			<button key="mock-button-1" role="button">Mock Button 1</button>,
+			<button key="mock-button-2" role="button">Mock Button 2</button>
+		];
+		renderChatBotFooter({ buttons })
+
+		const footerContainer = screen.getByTestId("chatbot-footer-container");
+		expect(footerContainer).toBeInTheDocument();
+
+		const footerbuttons =screen.getAllByRole("button");
+		expect(footerbuttons).toHaveLength(2);
+		expect(footerbuttons[0]).toBeInTheDocument();
+		expect(footerbuttons[1]).toBeInTheDocument();
+
+		const icon = footerContainer.querySelector("span");
+		expect(icon).toBeInTheDocument();
+	})
+
+
+});
\ No newline at end of file
diff --git a/__tests__/components/ChatBotHeader/ChatBotHeader.test.tsx b/__tests__/components/ChatBotHeader/ChatBotHeader.test.tsx
new file mode 100644
index 0000000..442e077
--- /dev/null
+++ b/__tests__/components/ChatBotHeader/ChatBotHeader.test.tsx
@@ -0,0 +1,136 @@
+import React from "react";
+
+import { expect } from "@jest/globals";
+import { render, screen } from "@testing-library/react";
+import "@testing-library/jest-dom/jest-globals";
+
+import ChatBotHeader  from "../../../src/components/ChatBotHeader/ChatBotHeader"
+
+import { useSettingsContext } from "../../../src/context/SettingsContext";
+import { useStylesContext } from "../../../src/context/StylesContext";
+import { useButtonInternal } from "../../../src/hooks/internal/useButtonsInternal";
+import { botAvatar } from "../../__mocks__/fileMock";
+
+jest.mock("../../../src/context/SettingsContext");
+jest.mock("../../../src/context/StylesContext");
+jest.mock("../../../src/hooks/internal/useButtonsInternal")
+
+describe("ChatBotHeader Component", () => {
+	
+	beforeEach(() => {
+		const mockHeaderButtons = [
+			<button key="mock-button-1" role="button">Mock Button 1</button>,
+			<button key="mock-button-2" role="button">Mock Button 2</button>
+		];
+
+		(useButtonInternal as jest.Mock).mockReturnValue({
+			headerButtons: mockHeaderButtons,
+		});
+
+		(useSettingsContext as jest.Mock).mockReturnValue({
+			settings: {
+				header: {
+					avatar: botAvatar,
+					showAvatar: true,
+					title: "Mock Title"
+				}
+			},
+		});
+
+		(useStylesContext as jest.Mock).mockReturnValue({
+			styles: {
+				headerStyle: {
+					background: 'black',
+					color: 'white',
+				}
+			}
+		});
+	});
+
+	// this test strictly checks the div layout to ensure that the styles are being applied correctly
+	it("renders header with correct div layout, styles, mocked buttons with avatar when showAvatar is true", () => {
+		render(<ChatBotHeader buttons={useButtonInternal().headerButtons}/>);
+
+		// First, get the header text by title, then get the header container and avatar
+		// There is no other way to directly get the header container based on the current component structure
+		// We can modify the component to add a ref or use data-testid to get the header container
+		const headerText = screen.getByText("Mock Title");
+		const headerContainer = headerText.parentNode?.parentNode?.querySelector(".rcb-chat-header-container");
+		const avatar = headerContainer?.querySelector(".rcb-bot-avatar");
+
+		// check if the elements are in the document
+		expect(headerText).toBeInTheDocument();
+		expect(headerContainer).toBeInTheDocument();
+		expect(avatar).toBeInTheDocument();
+
+		// check header container class and style
+		expect(headerContainer).toHaveClass("rcb-chat-header-container");
+		expect(headerContainer).toHaveStyle({
+			background: 'black',
+			color: 'white',
+		});
+
+		// check avatar icon and class
+		expect(avatar).toHaveClass("rcb-bot-avatar");
+		expect(avatar).toHaveStyle({
+			backgroundImage: `url(${botAvatar})`,
+		});
+
+		// get all header buttons
+		const headerButtons = screen.getAllByRole("button");
+
+		// make sure there are two buttons as expected
+		expect(headerButtons).toHaveLength(2);
+
+		// check header buttons to make sure they are in the document and have the correct text
+		expect(headerButtons[0]).toBeInTheDocument();
+		expect(headerButtons[1]).toBeInTheDocument();
+		expect(headerButtons[0]).toHaveTextContent("Mock Button 1");
+		expect(headerButtons[1]).toHaveTextContent("Mock Button 2");
+	});
+
+	it("renders header with correct div layout, styles, mocked buttons without avatar when showAvatar is false", () => {
+		(useSettingsContext as jest.Mock).mockReturnValue({
+			settings: {
+				header: {
+					showAvatar: false,
+					title: "Mock Title"
+				}
+			},
+		});
+		render(<ChatBotHeader buttons={useButtonInternal().headerButtons}/>);
+
+		// First, get the header text by title, then get the header container and avatar
+		// There is no other way to directly get the header container based on the current component structure
+		// We can modify the component to add a ref or use data-testid to get the header container
+		const headerText = screen.getByText("Mock Title");
+		const headerContainer = headerText.parentNode?.parentNode?.querySelector(".rcb-chat-header-container");
+		const avatar = headerContainer?.querySelector(".rcb-bot-avatar");
+
+		// check if the elements are in the document
+		expect(headerText).toBeInTheDocument();
+		expect(headerContainer).toBeInTheDocument();
+
+		// check to make sure the avatar is not in the document
+		expect(avatar).not.toBeInTheDocument();
+
+		// check header container class and style
+		expect(headerContainer).toHaveClass("rcb-chat-header-container");
+		expect(headerContainer).toHaveStyle({
+			background: 'black',
+			color: 'white',
+		});
+
+		// get all header buttons
+		const headerButtons = screen.getAllByRole("button");
+
+		// make sure there are two buttons as expected
+		expect(headerButtons).toHaveLength(2);
+
+		// check header buttons to make sure they are in the document and have the correct text
+		expect(headerButtons[0]).toBeInTheDocument();
+		expect(headerButtons[1]).toBeInTheDocument();
+		expect(headerButtons[0]).toHaveTextContent("Mock Button 1");
+		expect(headerButtons[1]).toHaveTextContent("Mock Button 2");
+	});
+});
\ No newline at end of file