mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-10-29 17:40:28 +00:00
Add AlbumCard UI tests
- add libraries for component UI testing - implement AlbumCard UI tests
This commit is contained in:
144
web/src/lib/components/album-page/__tests__/album-card.spec.ts
Normal file
144
web/src/lib/components/album-page/__tests__/album-card.spec.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { jest, describe, it } from '@jest/globals';
|
||||
import { render, RenderResult, waitFor, fireEvent } from '@testing-library/svelte';
|
||||
import { createObjectURLMock } from '$lib/__mocks__/jsdom-url.mock';
|
||||
import { api, ThumbnailFormat } from '@api';
|
||||
import { albumFactory } from '@test-data';
|
||||
import AlbumCard from '../album-card.svelte';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
jest.mock('@api');
|
||||
|
||||
const apiMock: jest.MockedObject<typeof api> = api as jest.MockedObject<typeof api>;
|
||||
|
||||
describe('AlbumCard component', () => {
|
||||
let sut: RenderResult<AlbumCard>;
|
||||
|
||||
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' : '');
|
||||
// TODO: is this a bug?
|
||||
expect(albumImgElement).toHaveAttribute('src', '/api/asset/thumbnail/null?format=WEBP');
|
||||
expect(albumImgElement).toHaveAttribute('alt', album.id);
|
||||
|
||||
await waitFor(() => expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png'));
|
||||
|
||||
expect(albumImgElement).toHaveAttribute('alt', album.id);
|
||||
expect(apiMock.assetApi.getAssetThumbnail).not.toHaveBeenCalled();
|
||||
|
||||
expect(albumNameElement).toHaveTextContent(album.albumName);
|
||||
expect(albumDetailsElement).toHaveTextContent(new RegExp(detailsText));
|
||||
}
|
||||
);
|
||||
|
||||
it('shows album data and and loads the thumbnail image when available', async () => {
|
||||
const thumbnailBlob = new Blob();
|
||||
const thumbnailUrl = 'blob:thumbnailUrlOne';
|
||||
apiMock.assetApi.getAssetThumbnail.mockResolvedValue({
|
||||
data: thumbnailBlob,
|
||||
config: {},
|
||||
headers: {},
|
||||
status: 200,
|
||||
statusText: ''
|
||||
});
|
||||
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');
|
||||
// TODO: is this expected?
|
||||
expect(albumImgElement).toHaveAttribute(
|
||||
'src',
|
||||
'/api/asset/thumbnail/thumbnailIdOne?format=WEBP'
|
||||
);
|
||||
expect(albumImgElement).toHaveAttribute('alt', album.id);
|
||||
|
||||
await waitFor(() => expect(albumImgElement).toHaveAttribute('src', thumbnailUrl));
|
||||
|
||||
expect(albumImgElement).toHaveAttribute('alt', album.id);
|
||||
expect(apiMock.assetApi.getAssetThumbnail).toHaveBeenCalledTimes(1);
|
||||
expect(apiMock.assetApi.getAssetThumbnail).toHaveBeenCalledWith(
|
||||
'thumbnailIdOne',
|
||||
ThumbnailFormat.Jpeg,
|
||||
{ responseType: 'blob' }
|
||||
);
|
||||
expect(createObjectURLMock).toHaveBeenCalledWith(thumbnailBlob);
|
||||
|
||||
expect(albumNameElement).toHaveTextContent('some album name');
|
||||
expect(albumDetailsElement).toHaveTextContent('0 items');
|
||||
});
|
||||
|
||||
describe('with rendered component - no thumbnail', () => {
|
||||
const album = Object.freeze(albumFactory.build({ albumThumbnailAssetId: null }));
|
||||
|
||||
beforeEach(async () => {
|
||||
sut = render(AlbumCard, { album });
|
||||
|
||||
const albumImgElement = sut.getByTestId('album-image');
|
||||
await waitFor(() => expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png'));
|
||||
});
|
||||
|
||||
it('dispatches custom "click" event with the album in context', async () => {
|
||||
const onClickHandler = jest.fn();
|
||||
sut.component.$on('click', onClickHandler);
|
||||
const albumCardElement = sut.getByTestId('album-card');
|
||||
|
||||
await fireEvent.click(albumCardElement);
|
||||
expect(onClickHandler).toHaveBeenCalledTimes(1);
|
||||
expect(onClickHandler).toHaveBeenCalledWith(expect.objectContaining({ detail: album }));
|
||||
});
|
||||
|
||||
it('dispatches custom "click" event on context menu click with mouse coordinates', async () => {
|
||||
const onClickHandler = jest.fn();
|
||||
sut.component.$on('showalbumcontextmenu', onClickHandler);
|
||||
|
||||
const contextMenuBtnParent = sut.getByTestId('context-button-parent');
|
||||
|
||||
await fireEvent(
|
||||
contextMenuBtnParent,
|
||||
new MouseEvent('click', {
|
||||
clientX: 123,
|
||||
clientY: 456
|
||||
})
|
||||
);
|
||||
|
||||
expect(onClickHandler).toHaveBeenCalledTimes(1);
|
||||
expect(onClickHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ detail: { x: 123, y: 456 } })
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -53,11 +53,13 @@
|
||||
<div
|
||||
class="h-[339px] w-[275px] hover:cursor-pointer mt-4 relative"
|
||||
on:click={() => dispatchClick('click', album)}
|
||||
data-testid="album-card"
|
||||
>
|
||||
<div
|
||||
id={`icon-${album.id}`}
|
||||
class="absolute top-2 right-2"
|
||||
on:click|stopPropagation|preventDefault={showAlbumContextMenu}
|
||||
data-testid="context-button-parent"
|
||||
>
|
||||
<CircleIconButton
|
||||
logo={DotsVertical}
|
||||
@@ -72,15 +74,16 @@
|
||||
src={imageData}
|
||||
alt={album.id}
|
||||
class={`object-cover h-full w-full transition-all z-0 rounded-xl duration-300 hover:shadow-lg`}
|
||||
data-testid="album-image"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<p class="text-sm font-medium text-gray-800">
|
||||
<p class="text-sm font-medium text-gray-800" data-testid="album-name">
|
||||
{album.albumName}
|
||||
</p>
|
||||
|
||||
<span class="text-xs flex gap-2">
|
||||
<span class="text-xs flex gap-2" data-testid="album-details">
|
||||
<p>{album.assetCount} items</p>
|
||||
|
||||
{#if album.shared}
|
||||
|
||||
Reference in New Issue
Block a user