mattermost-mobile/app/components/upload_item_shared/index.test.tsx
Rajat Dabade 71fed8aa50
(Android) Common component for upload item for main application and share extension (#9028)
* Common component for upload item for main application and share extension

* Addressed review comments

* intl fixes

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2025-09-11 12:54:54 +05:30

545 lines
18 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import DatabaseManager from '@database/manager';
import {fireEvent, renderWithEverything} from '@test/intl-test-helper';
import TestHelper from '@test/test_helper';
import UploadItemShared, {type UploadItemFile} from './index';
import type {Database} from '@nozbe/watermelondb';
// Mock the dependencies
jest.mock('@components/files/file_icon', () => 'FileIcon');
jest.mock('@components/files/image_file', () => 'ImageFile');
jest.mock('@components/progress_bar', () => 'ProgressBar');
jest.mock('@utils/file', () => ({
isImage: jest.fn(),
getFormattedFileSize: jest.fn(),
}));
const {isImage, getFormattedFileSize} = require('@utils/file');
describe('UploadItemShared', () => {
const serverUrl = 'serverUrl';
let database: Database;
beforeEach(async () => {
jest.clearAllMocks();
database = (await TestHelper.setupServerDatabase(serverUrl)).database;
getFormattedFileSize.mockReturnValue('1024 KB');
});
afterAll(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
const createMockFile = (overrides: Partial<UploadItemFile> = {}): UploadItemFile => ({
id: 'test-id',
clientId: 'test-client-id',
name: 'test-file.jpg',
extension: 'jpg',
size: 1024,
uri: 'file://test-uri',
failed: false,
width: 800,
height: 600,
mime_type: 'image/jpeg',
...overrides,
});
describe('File Type Display Behavior', () => {
it('should display image thumbnail for image files', () => {
isImage.mockReturnValue(true);
const imageFile = createMockFile({
name: 'vacation.jpg',
mime_type: 'image/jpeg',
});
const {getByTestId, queryByText} = renderWithEverything(
<UploadItemShared
file={imageFile}
testID='test-upload'
/>,
{database},
);
// Should show image component
expect(getByTestId('test-upload')).toBeTruthy();
// Should NOT show file info text for images
expect(queryByText('vacation.jpg')).toBeNull();
expect(queryByText('JPG')).toBeNull();
});
it('should display file info for document files', () => {
isImage.mockReturnValue(false);
const docFile = createMockFile({
name: 'report.pdf',
extension: 'pdf',
mime_type: 'application/pdf',
});
const {getByText} = renderWithEverything(
<UploadItemShared
file={docFile}
testID='test-upload'
/>,
{database},
);
// Should show file info
expect(getByText('report.pdf')).toBeTruthy();
expect(getByText('PDF 1024 KB')).toBeTruthy();
});
it('should handle files without extension gracefully', () => {
isImage.mockReturnValue(false);
const fileWithoutExt = createMockFile({
name: 'document_no_extension',
extension: undefined,
mime_type: 'application/octet-stream',
});
const {getByText} = renderWithEverything(
<UploadItemShared
file={fileWithoutExt}
testID='test-upload'
/>,
{database},
);
expect(getByText('document_no_extension')).toBeTruthy();
expect(getByText('DOCUMENT_NO_EXTENSION 1024 KB')).toBeTruthy(); // Shows extracted extension and size
});
it('should extract extension from filename when extension field is missing', () => {
isImage.mockReturnValue(false);
const fileWithNameExt = createMockFile({
name: 'document.docx',
extension: '',
mime_type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
const {getByText} = renderWithEverything(
<UploadItemShared
file={fileWithNameExt}
testID='test-upload'
/>,
{database},
);
expect(getByText('document.docx')).toBeTruthy();
expect(getByText('DOCX 1024 KB')).toBeTruthy();
});
});
describe('User Interactions', () => {
it('should call onPress when user taps the file', () => {
isImage.mockReturnValue(false);
const onPress = jest.fn();
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
onPress={onPress}
testID='test-upload'
/>,
{database},
);
fireEvent.press(getByTestId('test-upload'));
expect(onPress).toHaveBeenCalledTimes(1);
});
it('should call onRetry when user taps retry button on failed upload', () => {
isImage.mockReturnValue(false);
const onRetry = jest.fn();
const failedFile = createMockFile({
failed: true,
});
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={failedFile}
onRetry={onRetry}
showRetryButton={true}
testID='test-upload'
/>,
{database},
);
const component = getByTestId('test-upload');
expect(component).toBeTruthy();
const retryButton = getByTestId('retry-button');
expect(retryButton).toBeTruthy();
fireEvent.press(retryButton);
expect(onRetry).toHaveBeenCalledTimes(1);
});
it('should not crash when tapped without onPress handler', () => {
isImage.mockReturnValue(false);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
testID='test-upload'
/>,
{database},
);
// Should not crash when pressed without onPress
expect(() => {
fireEvent.press(getByTestId('test-upload'));
}).not.toThrow();
});
});
describe('Upload Progress and States', () => {
it('should show progress bar during active upload', () => {
isImage.mockReturnValue(false);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
loading={true}
progress={0.5}
testID='test-upload'
/>,
{database},
);
// Should show progress bar component
expect(getByTestId('test-upload')).toBeTruthy();
});
it('should not show progress bar for completed uploads', () => {
isImage.mockReturnValue(false);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
loading={false}
testID='test-upload'
/>,
{database},
);
// Should render without progress bar
expect(getByTestId('test-upload')).toBeTruthy();
});
it('should not show progress bar for failed uploads', () => {
isImage.mockReturnValue(false);
const failedFile = createMockFile({
failed: true,
});
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={failedFile}
loading={true}
testID='test-upload'
/>,
{database},
);
// Should render without progress bar when failed (even if loading=true)
expect(getByTestId('test-upload')).toBeTruthy();
});
it('should show retry button only when file is failed and showRetryButton is true', () => {
isImage.mockReturnValue(false);
const failedFile = createMockFile({
failed: true,
});
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={failedFile}
showRetryButton={true}
onRetry={jest.fn()}
testID='test-upload'
/>,
{database},
);
expect(getByTestId('test-upload')).toBeTruthy();
});
it('should not show retry button for successful uploads', () => {
isImage.mockReturnValue(false);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
showRetryButton={true}
testID='test-upload'
/>,
{database},
);
expect(getByTestId('test-upload')).toBeTruthy();
});
});
describe('Share Extension vs Main App Context', () => {
it('should handle share extension context for images', () => {
isImage.mockReturnValue(true);
const imageFile = createMockFile({
name: 'shared-photo.jpg',
uri: 'file://local-path/photo.jpg',
});
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={imageFile}
isShareExtension={true}
testID='test-upload'
/>,
{database},
);
// Should render in share extension mode
expect(getByTestId('test-upload')).toBeTruthy();
});
it('should handle main app context for images', () => {
isImage.mockReturnValue(true);
const imageFile = createMockFile({
name: 'main-app-photo.jpg',
});
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={imageFile}
isShareExtension={false}
forwardRef={React.createRef()}
inViewPort={true}
testID='test-upload'
/>,
{database},
);
// Should render in main app mode with gallery integration
expect(getByTestId('test-upload')).toBeTruthy();
});
});
describe('Layout Modes', () => {
it('should use full width layout for non-image files when specified', () => {
isImage.mockReturnValue(false);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
fullWidth={true}
testID='test-upload'
/>,
{database},
);
expect(getByTestId('test-upload')).toBeTruthy();
});
it('should not apply full width to image files even when specified', () => {
isImage.mockReturnValue(true);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
fullWidth={true}
testID='test-upload'
/>,
{database},
);
// Images should maintain their thumbnail size regardless of fullWidth
expect(getByTestId('test-upload')).toBeTruthy();
});
});
describe('Error States and Visual Feedback', () => {
it('should apply error styling when hasError is true', () => {
isImage.mockReturnValue(false);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
hasError={true}
testID='test-upload'
/>,
{database},
);
// Should render with error styling
expect(getByTestId('test-upload')).toBeTruthy();
});
it('should not apply error styling when hasError is false', () => {
isImage.mockReturnValue(false);
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={createMockFile()}
hasError={false}
testID='test-upload'
/>,
{database},
);
// Should render without error styling
expect(getByTestId('test-upload')).toBeTruthy();
});
});
describe('Data Resilience', () => {
it('should handle files with missing name gracefully', () => {
isImage.mockReturnValue(false);
const fileWithoutName = createMockFile({
name: undefined,
});
const {getByText} = renderWithEverything(
<UploadItemShared
file={fileWithoutName}
testID='test-upload'
/>,
{database},
);
// Should show fallback text
expect(getByText('Unknown file')).toBeTruthy();
});
it('should handle files with zero size', () => {
isImage.mockReturnValue(false);
getFormattedFileSize.mockReturnValue('0 KB');
const emptyFile = createMockFile({
size: 0,
});
const {getByText} = renderWithEverything(
<UploadItemShared
file={emptyFile}
testID='test-upload'
/>,
{database},
);
expect(getByText('JPG 0 KB')).toBeTruthy(); // Extension + size format
});
it('should handle files with no URI', () => {
isImage.mockReturnValue(false);
const fileWithoutUri = createMockFile({
uri: undefined,
});
// Should not crash with missing URI
expect(() => {
renderWithEverything(
<UploadItemShared
file={fileWithoutUri}
testID='test-upload'
/>,
{database},
);
}).not.toThrow();
});
it('should handle files with no mime type', () => {
isImage.mockReturnValue(false);
const fileWithoutMimeType = createMockFile({
mime_type: undefined,
});
expect(() => {
renderWithEverything(
<UploadItemShared
file={fileWithoutMimeType}
testID='test-upload'
/>,
{database},
);
}).not.toThrow();
});
});
describe('Complex Interaction Scenarios', () => {
it('should handle retry flow for failed uploads', () => {
isImage.mockReturnValue(false);
const onRetry = jest.fn();
const failedFile = createMockFile({
failed: true,
name: 'failed-upload.pdf',
extension: 'pdf',
});
const {getByText} = renderWithEverything(
<UploadItemShared
file={failedFile}
onRetry={onRetry}
showRetryButton={true}
testID='test-upload'
/>,
{database},
);
// Should show file info even when failed
expect(getByText('failed-upload.pdf')).toBeTruthy();
expect(getByText('PDF 1024 KB')).toBeTruthy(); // Correct extension + size format
});
it('should handle image files in error state', () => {
isImage.mockReturnValue(true);
const errorImageFile = createMockFile({
name: 'corrupted.jpg',
failed: true,
});
const {getByTestId, queryByText} = renderWithEverything(
<UploadItemShared
file={errorImageFile}
hasError={true}
showRetryButton={true}
onRetry={jest.fn()}
testID='test-upload'
/>,
{database},
);
// Should show image thumbnail even in error state
expect(getByTestId('test-upload')).toBeTruthy();
// Should not show file info for images (even in error state)
expect(queryByText('corrupted.jpg')).toBeNull();
});
it('should handle simultaneous loading and error states correctly', () => {
isImage.mockReturnValue(false);
const conflictedFile = createMockFile({
failed: true,
});
const {getByTestId} = renderWithEverything(
<UploadItemShared
file={conflictedFile}
loading={true}
hasError={true}
showRetryButton={true}
onRetry={jest.fn()}
testID='test-upload'
/>,
{database},
);
// Should prioritize failed state over loading state
expect(getByTestId('test-upload')).toBeTruthy();
});
});
});