import React from "react";
import { render, waitFor } from "@testing-library/react-native";
import { FormatFileThumbnail } from "./FormatFileThumbnail";
import {
FILE_MOCK_FILE,
FILE_MOCK_IMAGE,
FILE_MOCK_VIDEO,
FILE_UPLOAD_MOCK_FILE,
FILE_UPLOAD_MOCK_IMAGE,
} from "./components/_mocks/mockFiles";
import type { File, FileUpload } from "./types";
import { StatusCode } from "./types";
import { tokens } from "../utils/design";
const mockCreateThumbnail = jest.fn(async () => ({
thumbnail: "thumbnail",
error: false,
}));
afterEach(() => {
jest.clearAllMocks();
});
describe("FormatFileThumbnail", () => {
describe("rendering media files", () => {
it("renders an image file upload", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({
progress: 1,
status: StatusCode.Completed,
});
const { getByTestId } = renderThumbnail(file);
expect(getByTestId("test-image")).toBeDefined();
});
it("renders an external image file", () => {
const { getByTestId } = renderThumbnail(FILE_MOCK_IMAGE);
expect(getByTestId("test-image")).toBeDefined();
});
it("creates a thumbnail for media files", () => {
renderThumbnail(
FILE_UPLOAD_MOCK_IMAGE({
progress: 1,
status: StatusCode.Completed,
}),
);
expect(mockCreateThumbnail).toHaveBeenCalledTimes(1);
});
it("does not create a thumbnail for non-media files", () => {
renderThumbnail(
FILE_UPLOAD_MOCK_FILE({
progress: 1,
status: StatusCode.Completed,
}),
);
expect(mockCreateThumbnail).not.toHaveBeenCalled();
});
});
describe("rendering non-media files", () => {
it("renders a file upload", () => {
const file = FILE_UPLOAD_MOCK_FILE({
progress: 1,
status: StatusCode.Completed,
});
const { getByTestId } = renderThumbnail(file);
expect(getByTestId("test-file")).toBeDefined();
});
it("renders an external file", () => {
const { getByTestId } = renderThumbnail(FILE_MOCK_FILE);
expect(getByTestId("test-file")).toBeDefined();
});
});
describe("upload progress", () => {
it("shows a progress bar when upload is in progress", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({ progress: 0.5 });
const { getByTestId } = renderThumbnail(file);
expect(getByTestId("format-file-progress-bar-container")).toBeDefined();
});
it("shows a progress overlay with correct styles", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({ progress: 0.5 });
const { getByTestId } = renderThumbnail(file);
const progressBarContainer = getByTestId(
"format-file-progress-bar-container",
);
const overlayStyles = progressBarContainer.props.style;
expect(overlayStyles).toEqual(
expect.arrayContaining([expect.objectContaining({ height: "100%" })]),
);
expect(overlayStyles).toEqual(
expect.arrayContaining([
expect.objectContaining({
backgroundColor: tokens["color-overlay--dimmed"],
}),
]),
);
});
it("renders a progress bar that advances with upload percentage", async () => {
jest.useFakeTimers();
const file = FILE_UPLOAD_MOCK_IMAGE({ progress: 0.9 });
const { getByTestId } = renderThumbnail(file);
jest.advanceTimersByTime(500);
const innerProgressBar = await waitFor(() =>
getByTestId("format-file-inner-progress-bar"),
);
const width = parseInt(innerProgressBar.props.style.width, 10);
expect(width).toBeGreaterThan(20);
jest.useRealTimers();
});
});
describe("error state", () => {
it("renders an error icon for a failed image upload", () => {
const file = {
...FILE_UPLOAD_MOCK_IMAGE({
progress: 0.9,
status: StatusCode.Failed,
}),
};
const { getByTestId } = renderThumbnail(file);
expect(getByTestId("format-file-error-icon")).toBeDefined();
});
it("renders a helpful error accessibility label", () => {
const file = {
...FILE_UPLOAD_MOCK_IMAGE({
progress: 0.9,
status: StatusCode.Failed,
}),
};
const tree = renderThumbnail(file);
expect(tree.getByLabelText("Failed to upload.")).toBeDefined();
});
});
describe("accessibility", () => {
it("renders with a custom accessibility label", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({
progress: 1,
status: StatusCode.Completed,
});
const { getByLabelText } = renderThumbnail(file);
expect(getByLabelText("Custom Label")).toBeDefined();
});
});
describe("size prop", () => {
it("applies size dimensions to the outer container", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({
progress: 1,
status: StatusCode.Completed,
});
const { getByTestId } = render(
,
);
const container = getByTestId("thumbnail-container");
const containerStyle = container.props.style;
expect(containerStyle).toEqual(
expect.arrayContaining([
expect.objectContaining({ width: 100, height: 100 }),
]),
);
});
it("applies container visual styles (border, radius, overflow)", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({
progress: 1,
status: StatusCode.Completed,
});
const { getByTestId } = render(
,
);
const container = getByTestId("thumbnail-container");
const containerStyle = container.props.style;
expect(containerStyle).toEqual(
expect.arrayContaining([
expect.objectContaining({
borderWidth: tokens["border-base"],
borderColor: tokens["color-border"],
borderRadius: tokens["radius-base"],
overflow: "hidden",
}),
]),
);
});
it("does not apply marginBottom from the base container styles", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({
progress: 1,
status: StatusCode.Completed,
});
const { getByTestId } = render(
,
);
const container = getByTestId("thumbnail-container");
const containerStyle = container.props.style;
// marginBottom is overridden to 0 since spacing is a layout concern
expect(containerStyle).toEqual(
expect.arrayContaining([expect.objectContaining({ marginBottom: 0 })]),
);
});
});
describe("video files", () => {
it("shows the play icon for video files by default", () => {
const { getByTestId } = renderThumbnail(FILE_MOCK_VIDEO);
expect(getByTestId("video")).toBeDefined();
});
it("hides the play icon when showFileTypeIndicator is false", () => {
const { queryByTestId } = render(
,
);
expect(queryByTestId("video")).toBeNull();
});
});
describe("does NOT render BottomSheet or TouchableOpacity", () => {
it("does not render a bottom sheet", () => {
const file = FILE_UPLOAD_MOCK_IMAGE({
progress: 1,
status: StatusCode.Completed,
});
const { queryByLabelText } = renderThumbnail(file);
// BottomSheet options should not exist
expect(queryByLabelText("Remove image")).toBeNull();
expect(queryByLabelText("Preview image")).toBeNull();
});
});
});
function renderThumbnail(file: FileUpload | File) {
return render(
,
);
}
describe("FormatFileThumbnail surfaceColor", () => {
it("applies the provided surfaceColor to the outer container", () => {
const { getByTestId } = render(
,
);
const container = getByTestId("thumbnail-container");
const styles = Array.isArray(container.props.style)
? container.props.style.filter(Boolean)
: [container.props.style];
const merged = Object.assign({}, ...styles);
expect(merged.backgroundColor).toBe("#F4ECD9");
});
it("preserves the themed background when surfaceColor is omitted", () => {
const { getByTestId } = render(
,
);
const container = getByTestId("thumbnail-container");
const styles = Array.isArray(container.props.style)
? container.props.style.filter(Boolean)
: [container.props.style];
const merged = Object.assign({}, ...styles);
// The default themed background token resolves to the surface
// colour, not the consumer-supplied #F4ECD9.
expect(merged.backgroundColor).not.toBe("#F4ECD9");
expect(merged.backgroundColor).toBeTruthy();
});
});