Files
immich-folio/lib/__tests__/assetLocation.test.ts
Ralksta a813433891 release: v0.13.0 (#535)
Twenty-nine commits since v0.12.0. See CHANGELOG.md for the full list
and the upgrade notes.

Ten additions — the SEO package (site URL, robots.txt, sitemap.xml,
JSON-LD), slideshow mode, opt-in original downloads, copy-link,
previous/next album navigation, alt text from the Immich description,
per-album location precision, the update notice, a Help tab, and a
subpage cover grid separate from its photo grid. Two fixes: CJK album
slugs, and the lightbox control bar.

Photo of the Day (#476) was built on `dev` and reverted as not planned;
it never appears in a release.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01DmsHVRL9yQvckUB1ZpVWre

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 10:08:12 +02:00

90 lines
3.4 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { LocationPrecision } from '@/lib/mapPrecision';
/**
* The per-album `location:` setting governed the map and nothing else, so an
* album asking to be absent from it still had its city named in the lightbox
* info panel. These pin the join: an asset inherits the setting of the albums
* that carry it.
*/
const albumLocationPrecision: Record<string, LocationPrecision> = {};
vi.mock('@/lib/config', async () => {
const actual = await vi.importActual<typeof import('@/lib/config')>('@/lib/config');
return {
...actual,
getConfig: () => ({ albumLocationPrecision }),
};
});
const getAlbum = vi.fn();
vi.mock('@/lib/immich', () => ({
immich: { getAlbum: (id: string) => getAlbum(id) },
}));
import { assetLocationPrecision } from '@/lib/assetLocation';
const ASSET = 'asset-1';
const OTHER = 'asset-2';
/** An album whose assets are just ids. */
const album = (id: string, assetIds: string[]) => ({
id,
assets: assetIds.map((a) => ({ id: a })),
});
beforeEach(() => {
for (const key of Object.keys(albumLocationPrecision)) delete albumLocationPrecision[key];
getAlbum.mockReset();
});
describe('assetLocationPrecision', () => {
it('is exact when no album restricts anything, without asking Immich at all', async () => {
await expect(assetLocationPrecision(ASSET)).resolves.toBe('exact');
// The common case must not cost a single album fetch.
expect(getAlbum).not.toHaveBeenCalled();
});
it('takes the setting of an album that carries the asset', async () => {
albumLocationPrecision['album-a'] = 'country';
getAlbum.mockResolvedValue(album('album-a', [ASSET]));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('country');
});
it('ignores an album that restricts but does not carry the asset', async () => {
albumLocationPrecision['album-a'] = 'hidden';
getAlbum.mockResolvedValue(album('album-a', [OTHER]));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('exact');
});
// Same rule as a merged map marker: the cautious setting governs.
it('takes the strictest when several albums carry the asset', async () => {
albumLocationPrecision['album-a'] = 'city';
albumLocationPrecision['album-b'] = 'hidden';
getAlbum.mockImplementation(async (id: string) => album(id, [ASSET]));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('hidden');
});
it('only asks about albums that actually restrict something', async () => {
albumLocationPrecision['album-a'] = 'exact';
albumLocationPrecision['album-b'] = 'city';
getAlbum.mockImplementation(async (id: string) => album(id, [ASSET]));
await assetLocationPrecision(ASSET);
expect(getAlbum).toHaveBeenCalledTimes(1);
expect(getAlbum).toHaveBeenCalledWith('album-b');
});
// Failing open would publish the position the setting exists to withhold.
it('withholds the place when an album cannot be read', async () => {
albumLocationPrecision['album-a'] = 'city';
getAlbum.mockRejectedValue(new Error('Immich is down'));
await expect(assetLocationPrecision(ASSET)).resolves.toBe('hidden');
});
it('withholds it when the album is gone from the allowlist', async () => {
albumLocationPrecision['album-a'] = 'city';
getAlbum.mockResolvedValue(null);
await expect(assetLocationPrecision(ASSET)).resolves.toBe('hidden');
});
});