diff --git a/__tests__/components/buttons/FileAttachmentbutton.test.tsx b/__tests__/components/buttons/FileAttachmentbutton.test.tsx new file mode 100644 index 00000000..ee68d483 --- /dev/null +++ b/__tests__/components/buttons/FileAttachmentbutton.test.tsx @@ -0,0 +1,201 @@ +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import FileAttachmentButton from "../../../src/components/Buttons/FileAttachmentButton/FileAttachmentButton"; +import { TestChatBotProvider } from "../../__mocks__/TestChatBotContext"; +import { useMessagesInternal } from "../../../src/hooks/internal/useMessagesInternal"; +import { useSubmitInputInternal } from "../../../src/hooks/internal/useSubmitInputInternal"; +import { usePathsInternal } from "../../../src/hooks/internal/usePathsInternal"; +import { getMediaFileDetails } from "../../../src/utils/mediaFileParser"; +import { useBotRefsContext } from "../../../src/context/BotRefsContext"; + +jest.mock("../../../src/hooks/internal/useMessagesInternal", () => ({ + useMessagesInternal: jest.fn(), +})); +jest.mock("../../../src/hooks/internal/useSubmitInputInternal", () => ({ + useSubmitInputInternal: jest.fn(), +})); +jest.mock("../../../src/hooks/internal/usePathsInternal", () => ({ + usePathsInternal: jest.fn(), +})); +jest.mock("../../../src/utils/mediaFileParser", () => ({ + getMediaFileDetails: jest.fn(), +})); +jest.mock("../../../src/context/BotRefsContext", () => ({ + ...jest.requireActual("../../../src/context/BotRefsContext"), + BotRefsProvider: ({ children }: { children: React.ReactNode }) => ( +
{children}
+ ), + useBotRefsContext: jest.fn(() => ({ + botIdRef: { current: "testBotId" }, + flowRef: { current: { currentPath: { file: jest.fn() } } }, + // ...other mocked values + })), +})); + +/** + * Helper function to render FileAttachmentButton with specific initial settings. + * @param blockAllowsAttachment Whether the block allows attachment + * @param showMediaDisplay Whether media display is enabled + */ +const renderFileAttachmentButton = (blockAllowsAttachment: boolean, showMediaDisplay: boolean) => { + const initialSettings = { + fileAttachment: { + sendFileName: true, // Ensure this is truthy + multiple: true, + accept: ".jpg,.png", + showMediaDisplay: showMediaDisplay, + icon: "attachment-icon-url", + iconDisabled: "attachment-icon-disabled-url", + }, + general: { + actionDisabledIcon: "disabled-icon-url", + }, + ariaLabel: { + fileAttachmentButton: "upload file", + }, + }; + const initialStyles = { + fileAttachmentButtonStyle: { cursor: "pointer" }, + fileAttachmentButtonDisabledStyle: { cursor: "not-allowed" }, + fileAttachmentIconStyle: { backgroundImage: "url(icon.png)" }, + fileAttachmentIconDisabledStyle: { backgroundImage: "url(disabled-icon.png)" }, + }; + + return render( + + + + ); +}; + +describe("FileAttachmentButton Component", () => { + const mockInjectMessage = jest.fn(); + const mockHandleSubmitText = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + + (useMessagesInternal as jest.Mock).mockReturnValue({ + injectMessage: mockInjectMessage, + }); + + (useSubmitInputInternal as jest.Mock).mockReturnValue({ + handleSubmitText: mockHandleSubmitText, + }); + + (usePathsInternal as jest.Mock).mockReturnValue({ + blockAllowsAttachment: true, + getCurrPath: jest.fn().mockReturnValue("currentPath"), + getPrevPath: jest.fn().mockReturnValue("previousPath"), + goToPath: jest.fn(), + }); + + (getMediaFileDetails as jest.Mock).mockResolvedValue({ + fileType: "image", + fileUrl: "http://example.com/test.png", + }); + }); + + it("renders with correct aria-label and initial state when attachment is allowed", () => { + renderFileAttachmentButton(true, true); + + const button = screen.getByRole("button", { name: "upload file" }); + expect(button).toBeInTheDocument(); + expect(button).toHaveClass("rcb-attach-button-enabled"); + + const input = screen.getByRole("file"); + expect(input).toBeEnabled(); + expect(input).toHaveAttribute("type", "file"); + expect(input).toHaveAttribute("multiple"); + expect(input).toHaveAttribute("accept", ".jpg,.png"); + }); + + it("renders with disabled state when attachment is not allowed", () => { + (usePathsInternal as jest.Mock).mockReturnValueOnce({ + blockAllowsAttachment: false, + getCurrPath: jest.fn(), + getPrevPath: jest.fn(), + goToPath: jest.fn(), + }); + renderFileAttachmentButton(false, true); + + const input = screen.getByRole("file"); + expect(input).toBeDisabled(); + + const button = screen.getByRole("button", { name: "upload file" }); + expect(button).toHaveClass("rcb-attach-button-disabled"); + }); + + it("displays correct icon for enabled state", () => { + renderFileAttachmentButton(true, true); + + const icon = screen.getByRole("button", { name: "upload file" }); + expect(icon).toBeInTheDocument(); + expect(icon).toHaveClass("rcb-attach-button-enabled"); + }); + + it("displays correct icon for disabled state", () => { + (usePathsInternal as jest.Mock).mockReturnValueOnce({ + blockAllowsAttachment: false, + getCurrPath: jest.fn(), + getPrevPath: jest.fn(), + goToPath: jest.fn(), + }); + + renderFileAttachmentButton(false, true); + + const icon = screen.getByRole("button", { name: "upload file" }); + expect(icon).toBeInTheDocument(); + expect(icon).toHaveClass("rcb-attach-button-disabled"); + }); + + it("does not proceed when getCurrPath returns null", () => { + // Mock getCurrPath to return null + (usePathsInternal as jest.Mock).mockReturnValueOnce({ + blockAllowsAttachment: true, + getCurrPath: jest.fn().mockReturnValue(null), // Simulate null return value + getPrevPath: jest.fn(), + goToPath: jest.fn(), + }); + + renderFileAttachmentButton(true, true); + + const input = screen.getByRole("file"); + const file = new File(["test content"], "test.png", { type: "image/png" }); + + fireEvent.change(input, { target: { files: [file] } }); + + // Expect no calls to mockHandleSubmitText or mockInjectMessage + expect(mockHandleSubmitText).not.toHaveBeenCalled(); + expect(mockInjectMessage).not.toHaveBeenCalled(); + }); + + it("handles file upload and calls the appropriate functions", async () => { + const mockFileHandler = jest.fn(); + const mockFlow = { currentPath: { file: mockFileHandler } }; + + (useBotRefsContext as jest.Mock).mockReturnValueOnce({ + ...jest.requireMock("../../../src/context/BotRefsContext").useBotRefsContext(), + flowRef: { current: mockFlow }, + inputRef: { current: { value: "test input" } }, + }); + + renderFileAttachmentButton(true, true); + + const input = screen.getByRole("file"); + const file = new File(["test content"], "test.png", { type: "image/png" }); + + fireEvent.change(input, { target: { files: [file] } }); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(mockFileHandler).toHaveBeenCalled(); + expect(mockFileHandler).toHaveBeenCalledWith( + expect.objectContaining({ + userInput: "test input", + files: [file], + }) + ); + }); +}); diff --git a/src/components/Buttons/FileAttachmentButton/FileAttachmentButton.tsx b/src/components/Buttons/FileAttachmentButton/FileAttachmentButton.tsx index 2323dbc1..992f8d19 100644 --- a/src/components/Buttons/FileAttachmentButton/FileAttachmentButton.tsx +++ b/src/components/Buttons/FileAttachmentButton/FileAttachmentButton.tsx @@ -167,6 +167,7 @@ const FileAttachmentButton = () => { >