import React, { useEffect, useState } from 'react'; import type { SharedValue } from 'react-native-reanimated'; import { render, screen, userEvent, waitFor } from '@testing-library/react-native'; import { Attachment, LocalMessage } from 'stream-chat'; import { WithComponents } from '../../../contexts/componentsContext/ComponentsContext'; import { ImageGalleryContext, ImageGalleryContextValue, } from '../../../contexts/imageGalleryContext/ImageGalleryContext'; import { OverlayProvider } from '../../../contexts/overlayContext/OverlayProvider'; import { generateImageAttachment, generateVideoAttachment, } from '../../../mock-builders/generator/attachment'; import { generateMessage } from '../../../mock-builders/generator/message'; import { NativeHandlers } from '../../../native'; import { ImageGalleryStateStore } from '../../../state-store/image-gallery-state-store'; import { ImageGallery, ImageGalleryProps } from '../ImageGallery'; jest.mock('../../../native.ts', () => { const { View } = require('react-native'); return { isFileSystemAvailable: jest.fn(() => true), isImageMediaLibraryAvailable: jest.fn(() => true), isShareImageAvailable: jest.fn(() => true), isVideoPlayerAvailable: jest.fn(() => true), NativeHandlers: { deleteFile: jest.fn(), saveFile: jest.fn(), shareImage: jest.fn(), Video: View, }, }; }); const ImageGalleryComponentVideo = (props: ImageGalleryProps) => { const [imageGalleryStateStore] = useState(() => new ImageGalleryStateStore()); useEffect(() => { const unsubscribe = imageGalleryStateStore.registerSubscriptions(); return () => { unsubscribe(); }; }, [imageGalleryStateStore]); const attachment = generateVideoAttachment({ type: 'video' }); imageGalleryStateStore.openImageGallery({ messages: [ generateMessage({ attachments: [attachment], }) as unknown as LocalMessage, ], selectedAttachmentUrl: attachment.asset_url, }); return ( }}> {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} ); }; const ImageGalleryComponentImage = ( props: ImageGalleryProps & { attachment: Attachment; }, ) => { const [imageGalleryStateStore] = useState(() => new ImageGalleryStateStore()); useEffect(() => { const unsubscribe = imageGalleryStateStore.registerSubscriptions(); return () => { unsubscribe(); }; }, [imageGalleryStateStore]); imageGalleryStateStore.openImageGallery({ messages: [ generateMessage({ attachments: [props.attachment], }) as unknown as LocalMessage, ], selectedAttachmentUrl: props.attachment.image_url as string, }); return ( }}> {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} ); }; describe('ImageGalleryFooter', () => { it('render image gallery footer component with default footer elements', async () => { render(); await waitFor(() => { expect(screen.getByLabelText('Share Button')).toBeTruthy(); expect(screen.getByLabelText('Center element')).toBeTruthy(); expect(screen.getByLabelText('Grid Icon')).toBeTruthy(); }); }); it('render image gallery footer component with Share Button and Grid Icon', async () => { render(); await waitFor(() => { expect(screen.getByLabelText('Share Button')).toBeTruthy(); expect(screen.getByLabelText('Grid Icon')).toBeTruthy(); }); }); it('should trigger the share button onPress Handler with local attachment and no mime_type', async () => { const user = userEvent.setup(); const saveFileMock = jest.spyOn(NativeHandlers, 'saveFile'); const shareImageMock = jest.spyOn(NativeHandlers, 'shareImage'); const deleteFileMock = jest.spyOn(NativeHandlers, 'deleteFile'); const attachment = generateImageAttachment(); render(); await waitFor(() => { expect(screen.getByLabelText('Share Button')).toBeTruthy(); }); await user.press(screen.getByLabelText('Share Button')); await waitFor(() => { expect(saveFileMock).not.toHaveBeenCalled(); expect(shareImageMock).toHaveBeenCalledWith({ type: 'image/jpeg', url: attachment.image_url, }); expect(deleteFileMock).not.toHaveBeenCalled(); }); }); it('should trigger the share button onPress Handler with local attachment and existing mime_type', async () => { const user = userEvent.setup(); const saveFileMock = jest.spyOn(NativeHandlers, 'saveFile'); const shareImageMock = jest.spyOn(NativeHandlers, 'shareImage'); const deleteFileMock = jest.spyOn(NativeHandlers, 'deleteFile'); const attachment = { ...generateImageAttachment(), mime_type: 'image/png' }; render(); await waitFor(() => { expect(screen.getByLabelText('Share Button')).toBeTruthy(); }); await user.press(screen.getByLabelText('Share Button')); await waitFor(() => { expect(saveFileMock).not.toHaveBeenCalled(); expect(shareImageMock).toHaveBeenCalledWith({ type: 'image/png', url: attachment.image_url, }); expect(deleteFileMock).not.toHaveBeenCalled(); }); }); it('should trigger the share button onPress Handler with cdn attachment', async () => { const user = userEvent.setup(); const saveFileMock = jest .spyOn(NativeHandlers, 'saveFile') .mockResolvedValue('file:///local/asset/url'); const shareImageMock = jest.spyOn(NativeHandlers, 'shareImage'); const deleteFileMock = jest.spyOn(NativeHandlers, 'deleteFile'); const attachment = { ...generateImageAttachment(), image_url: 'https://my-image-service/image/123456', mime_type: 'image/png', }; render(); await waitFor(() => { expect(screen.getByLabelText('Share Button')).toBeTruthy(); }); await user.press(screen.getByLabelText('Share Button')); await waitFor(() => { expect(saveFileMock).toHaveBeenCalled(); expect(shareImageMock).toHaveBeenCalledWith({ type: 'image/png', url: 'file:///local/asset/url', }); expect(deleteFileMock).toHaveBeenCalled(); }); }); });