import { createObjectURLMock } from '$lib/__mocks__/jsdom-url.mock'; import { sdkMock } from '$lib/__mocks__/sdk.mock'; import { ThumbnailFormat } from '@immich/sdk'; import { albumFactory } from '@test-data'; import '@testing-library/jest-dom'; import { fireEvent, render, waitFor, type RenderResult } from '@testing-library/svelte'; import AlbumCard from '../album-card.svelte'; const onShowContextMenu = vi.fn(); describe('AlbumCard component', () => { let sut: RenderResult; it.each([ { album: albumFactory.build({ albumThumbnailAssetId: null, shared: false, assetCount: 0 }), count: 0, shared: false, }, { album: albumFactory.build({ albumThumbnailAssetId: null, shared: true, assetCount: 0 }), count: 0, shared: true, }, { album: albumFactory.build({ albumThumbnailAssetId: null, shared: false, assetCount: 5 }), count: 5, shared: false, }, { album: albumFactory.build({ albumThumbnailAssetId: null, shared: true, assetCount: 2 }), count: 2, shared: true, }, ])('shows album data without thumbnail with count $count - shared: $shared', async ({ album, count, shared }) => { sut = render(AlbumCard, { album }); const albumImgElement = sut.getByTestId('album-image'); const albumNameElement = sut.getByTestId('album-name'); const albumDetailsElement = sut.getByTestId('album-details'); const detailsText = `${count} items` + (shared ? ' . Shared' : ''); expect(albumImgElement).toHaveAttribute('src'); expect(albumImgElement).toHaveAttribute('alt', album.albumName); await waitFor(() => expect(albumImgElement).toHaveAttribute('src')); expect(albumImgElement).toHaveAttribute('alt', album.albumName); expect(sdkMock.getAssetThumbnail).not.toHaveBeenCalled(); expect(albumNameElement).toHaveTextContent(album.albumName); expect(albumDetailsElement).toHaveTextContent(new RegExp(detailsText)); }); it('shows album data and loads the thumbnail image when available', async () => { const thumbnailFile = new File([new Blob()], 'fileThumbnail'); const thumbnailUrl = 'blob:thumbnailUrlOne'; sdkMock.getAssetThumbnail.mockResolvedValue(thumbnailFile); createObjectURLMock.mockReturnValueOnce(thumbnailUrl); const album = albumFactory.build({ albumThumbnailAssetId: 'thumbnailIdOne', shared: false, albumName: 'some album name', }); sut = render(AlbumCard, { album }); const albumImgElement = sut.getByTestId('album-image'); const albumNameElement = sut.getByTestId('album-name'); const albumDetailsElement = sut.getByTestId('album-details'); expect(albumImgElement).toHaveAttribute('alt', album.albumName); await waitFor(() => expect(albumImgElement).toHaveAttribute('src', thumbnailUrl)); expect(albumImgElement).toHaveAttribute('alt', album.albumName); expect(sdkMock.getAssetThumbnail).toHaveBeenCalledTimes(1); expect(sdkMock.getAssetThumbnail).toHaveBeenCalledWith({ id: 'thumbnailIdOne', format: ThumbnailFormat.Jpeg, }); expect(createObjectURLMock).toHaveBeenCalledWith(thumbnailFile); expect(albumNameElement).toHaveTextContent('some album name'); expect(albumDetailsElement).toHaveTextContent('0 items'); }); it('hides context menu when "onShowContextMenu" is undefined', () => { const album = Object.freeze(albumFactory.build({ albumThumbnailAssetId: null })); sut = render(AlbumCard, { album }); const contextButtonParent = sut.queryByTestId('context-button-parent'); expect(contextButtonParent).not.toBeInTheDocument(); }); describe('with rendered component - no thumbnail', () => { const album = Object.freeze(albumFactory.build({ albumThumbnailAssetId: null })); beforeEach(async () => { sut = render(AlbumCard, { album, onShowContextMenu }); const albumImgElement = sut.getByTestId('album-image'); await waitFor(() => expect(albumImgElement).toHaveAttribute('src')); }); it('dispatches "onShowContextMenu" event on context menu click with mouse coordinates', async () => { const contextMenuButton = sut.getByTestId('context-button-parent').children[0]; expect(contextMenuButton).toBeDefined(); // Mock getBoundingClientRect to return a bounding rectangle that will result in the expected position contextMenuButton.getBoundingClientRect = () => ({ x: 123, y: 456, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0, toJSON: () => ({}), }); await fireEvent( contextMenuButton, new MouseEvent('click', { clientX: 123, clientY: 456, }), ); expect(onShowContextMenu).toHaveBeenCalledTimes(1); expect(onShowContextMenu).toHaveBeenCalledWith(expect.objectContaining({ x: 123, y: 456 })); }); }); });