import React from "react"; import type { RenderAPI } from "@testing-library/react-native"; import { fireEvent, render, waitFor } from "@testing-library/react-native"; import { Alert } from "react-native"; import type { File } from "."; import { FormatFile } from "."; import { FILE_MOCK_FILE, FILE_MOCK_IMAGE, FILE_MOCK_PDF, FILE_MOCK_VIDEO, FILE_UPLOAD_MOCK_FILE, FILE_UPLOAD_MOCK_IMAGE, FILE_UPLOAD_MOCK_PDF, } from "./components/_mocks/mockFiles"; import type { BottomSheetOptionsSuffix } from "./components/FormatFileBottomSheet"; import type { FileUpload } from "./types"; import { StatusCode } from "./types"; import { tokens } from "../utils/design"; let Platform: { OS: "ios" | "android" }; const onRemove = jest.fn(); const mockOnPreview = jest.fn(); const mockCreateThumbnail = jest.fn(async () => ({ thumbnail: "thumbnail", error: false, })); beforeEach(() => { Platform = require("react-native").Platform; }); afterEach(() => { jest.clearAllMocks(); }); const renderFormatFile = ( file: FileUpload | File, bottomSheetOptionsSuffix?: BottomSheetOptionsSuffix, showFileTypeIndicator?: boolean, ) => { return render( Alert.alert("alert")} onRemove={onRemove} bottomSheetOptionsSuffix={bottomSheetOptionsSuffix} showFileTypeIndicator={showFileTypeIndicator} onPreviewPress={mockOnPreview} createThumbnail={mockCreateThumbnail} />, ); }; function basicRenderTestWithValue() { const progressBarAnimationTime = 500; it.each([ [ "file", FILE_UPLOAD_MOCK_FILE({ progress: 1, status: StatusCode.Completed }), ], [ "image", FILE_UPLOAD_MOCK_IMAGE({ progress: 1, status: StatusCode.Completed }), ], ])( "renders a %s with custom label and hint", (bottomSheetOptionsSuffix, file) => { const { getByLabelText, getByHintText } = renderFormatFile( file, bottomSheetOptionsSuffix as BottomSheetOptionsSuffix, ); expect(getByLabelText("Custom Label")).toBeDefined(); expect(getByHintText("Custom Hint Text")).toBeDefined(); }, ); describe.each([ ["file", FILE_UPLOAD_MOCK_FILE({ progress: 0.9 })], ["image", FILE_UPLOAD_MOCK_IMAGE({ progress: 0.9 })], ])("when a local %s is being uploaded", (testIdType, file) => { const testId = `test-${testIdType}`; it("renders ProgressBar state when upload status is not completed", () => { const { getByTestId } = renderFormatFile(file); expect(getByTestId("format-file-progress-bar")).toBeDefined(); }); it("renders a helpful accessibility label", () => { const tree = renderFormatFile(file); expect(tree.getByLabelText("Upload in progress.")).toBeDefined(); }); it("renders an overlay on the image when upload status is not completed", () => { const { getByTestId } = renderFormatFile(file); const progressBarContainer = getByTestId( "format-file-progress-bar-container", ); const overlayStyles = progressBarContainer.props.style; // The container needs to have a height and color in order to show the overlay expect(overlayStyles).toEqual( expect.arrayContaining([expect.objectContaining({ height: "100%" })]), ); expect(overlayStyles).toEqual( expect.arrayContaining([ expect.objectContaining({ backgroundColor: tokens["color-overlay--dimmed"], }), ]), ); }); it("renders ProgressBar state advancing with the upload percentage", async () => { jest.useFakeTimers(); const { getByTestId } = renderFormatFile(file); jest.advanceTimersByTime(progressBarAnimationTime); const formatFileInnerProgressBar = await waitFor(() => getByTestId("format-file-inner-progress-bar"), ); const innerProgressBarWidth = parseInt( formatFileInnerProgressBar.props.style.width, 10, ); expect(innerProgressBarWidth).toBeGreaterThan(20); }); it("shows an alert for on tap", () => { const { getByTestId } = renderFormatFile(file); const spy = jest.spyOn(Alert, "alert"); fireEvent.press(getByTestId(testId)); expect(spy).toHaveBeenCalled(); }); }); describe.each([ [ "file", { ...FILE_UPLOAD_MOCK_FILE({ progress: 0.9, status: StatusCode.Failed }), status: StatusCode.Failed, }, ], [ "image", { ...FILE_UPLOAD_MOCK_IMAGE({ progress: 0.9, status: StatusCode.Failed }), status: StatusCode.Failed, }, ], ])("when a local %s upload has failed", (testIdType, file) => { it("renders an error icon", () => { const tree = renderFormatFile(file); expect(tree.getByTestId("format-file-error-icon")).toBeDefined(); }); it("renders a helpful accessibility label", () => { const tree = renderFormatFile(file); expect(tree.getByLabelText("Failed to upload.")).toBeDefined(); }); it("does not render an overlay", () => { const tree = renderFormatFile(file); expect( tree.queryByTestId("format-file-progress-bar-container"), ).toBeNull(); }); it("shows an alert for on tap", () => { const testId = `test-${testIdType}`; const tree = renderFormatFile(file); const spy = jest.spyOn(Alert, "alert"); fireEvent.press(tree.getByTestId(testId)); expect(spy).toHaveBeenCalled(); }); }); describe.each([ [ "local file", "test-file", FILE_UPLOAD_MOCK_FILE({ progress: 1, status: StatusCode.Completed }), ], [ "local image", "test-image", FILE_UPLOAD_MOCK_IMAGE({ progress: 1, status: StatusCode.Completed }), ], ["external file", "test-file", FILE_MOCK_FILE], ["external image", "test-image", FILE_MOCK_IMAGE], ])( "when a uploaded %s is being used", (bottomSheetOptionsSuffix, testId, file) => { let tree: RenderAPI; const removeLabel = `Remove ${bottomSheetOptionsSuffix}`; beforeEach(() => { jest.clearAllMocks(); jest.useFakeTimers(); tree = renderFormatFile( file, bottomSheetOptionsSuffix as BottomSheetOptionsSuffix, ); jest.advanceTimersByTime(progressBarAnimationTime); }); it("shows a BottomSheet with a remove option when tapped", async () => { const { getByTestId, findByLabelText } = tree; fireEvent.press(getByTestId(testId)); expect(await findByLabelText(removeLabel)).toBeDefined(); }); describe("when the BottomSheet remove option is tapped", () => { it("calls the onRemove action", async () => { const { getByTestId, findByLabelText } = tree; fireEvent.press(getByTestId(testId)); fireEvent.press(await findByLabelText(removeLabel)); expect(onRemove).toHaveBeenCalledTimes(1); }); }); it("creates a thumbnail when a media file is used", () => { const expectedCalls = testId.includes("image") ? 1 : 0; expect(mockCreateThumbnail).toHaveBeenCalledTimes(expectedCalls); }); }, ); describe("when the preview option is tapped", () => { it("calls onPreview with a valid image", async () => { const previewLabel = "Preview image"; const { getByTestId, findByLabelText } = renderFormatFile( FILE_UPLOAD_MOCK_IMAGE({ progress: 1, status: StatusCode.Completed }), "image", ); fireEvent.press(getByTestId("test-image")); fireEvent.press(await findByLabelText(previewLabel)); expect(mockOnPreview).toHaveBeenCalledTimes(1); }); it("calls onPreview with a valid pdf file", async () => { const previewLabel = "Preview file"; const { getByTestId, findByLabelText } = renderFormatFile( FILE_UPLOAD_MOCK_PDF({ progress: 1, status: StatusCode.Completed }), "file", ); fireEvent.press(getByTestId("test-file")); fireEvent.press(await findByLabelText(previewLabel)); expect(mockOnPreview).toHaveBeenCalledTimes(1); }); it("calls onPreview with a valid external PDF file", async () => { const previewLabel = "Preview file"; const { getByTestId, findByLabelText } = renderFormatFile( FILE_MOCK_PDF, "file", ); fireEvent.press(getByTestId("test-file")); fireEvent.press(await findByLabelText(previewLabel)); expect(mockOnPreview).toHaveBeenCalledTimes(1); }); it("does not show the preview option with an unaccepted file", () => { const previewLabel = "Preview file"; const { getByTestId, queryByLabelText } = renderFormatFile( FILE_UPLOAD_MOCK_FILE({ progress: 1, status: StatusCode.Completed }), "file", ); fireEvent.press(getByTestId("test-file")); expect(queryByLabelText(previewLabel)).toBeNull(); }); }); describe("when an uploaded video is being viewed", () => { it("shows the play icon if the file type indicator is not specified", () => { const { getByTestId } = renderFormatFile(FILE_MOCK_VIDEO, "video"); expect(getByTestId("video")).toBeDefined(); }); it("does not show the play icon if the file type indicator is set to false", () => { const { queryByTestId } = renderFormatFile( FILE_MOCK_VIDEO, "video", false, ); expect(queryByTestId("video")).toBeNull(); }); }); } describe("ios", () => { beforeEach(() => { Platform.OS = "ios"; }); basicRenderTestWithValue(); }); describe("android", () => { beforeEach(() => { Platform.OS = "android"; }); basicRenderTestWithValue(); });