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>
This commit is contained in:
@@ -13,7 +13,7 @@ vi.mock('@/lib/config', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/immich', () => ({
|
||||
immich: { getAssetInfo: vi.fn() },
|
||||
immich: { getAssetInfo: vi.fn(), getAlbum: vi.fn() },
|
||||
ImmichUnavailableError: class ImmichUnavailableError extends Error {},
|
||||
}));
|
||||
|
||||
@@ -36,6 +36,7 @@ import { NextRequest } from 'next/server';
|
||||
|
||||
const mockConfig = getConfig as unknown as ReturnType<typeof vi.fn>;
|
||||
const mockAssetInfo = immich.getAssetInfo as unknown as ReturnType<typeof vi.fn>;
|
||||
const mockGetAlbum = immich.getAlbum as unknown as ReturnType<typeof vi.fn>;
|
||||
|
||||
const EXIF_ASSET = {
|
||||
exifInfo: {
|
||||
@@ -64,6 +65,9 @@ function configWith(raw?: ExifDisplayYaml, exifOnHover?: boolean) {
|
||||
exifOnHover: exifOnHover !== false,
|
||||
exif: resolveExifDisplay(raw, exifOnHover),
|
||||
rateLimitRpm: 120,
|
||||
// No album restricts its location, so the place comes through as the
|
||||
// asset carries it — the shape getConfig() always builds (#469).
|
||||
albumLocationPrecision: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -152,3 +156,40 @@ describe('GET /api/exif/[id] captions', () => {
|
||||
expect(res.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* `location:` used to govern the map and nothing else, so an album asking to
|
||||
* be absent from it still named its city here (#469).
|
||||
*/
|
||||
describe('GET /api/exif/[id] album location precision', () => {
|
||||
/** The album that carries the asset the mocked token decodes to. */
|
||||
const carrying = { id: 'album-a', assets: [{ id: 'asset-uuid' }] };
|
||||
|
||||
async function respond(precision: Record<string, string>) {
|
||||
mockConfig.mockReturnValue({ ...configWith(), albumLocationPrecision: precision });
|
||||
mockAssetInfo.mockResolvedValue(EXIF_ASSET);
|
||||
mockGetAlbum.mockResolvedValue(carrying);
|
||||
const res = await GET(new NextRequest('http://localhost/api/exif/token'), {
|
||||
params: Promise.resolve({ id: 'token' }),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
it('names the city when nothing restricts it', async () => {
|
||||
expect(await respond({})).toMatchObject({ city: 'Vienna', country: 'Austria' });
|
||||
});
|
||||
|
||||
it('drops the city but keeps the country at country precision', async () => {
|
||||
const body = await respond({ 'album-a': 'country' });
|
||||
expect(body.country).toBe('Austria');
|
||||
expect(body).not.toHaveProperty('city');
|
||||
});
|
||||
|
||||
it('names no place at all when the album is hidden', async () => {
|
||||
const body = await respond({ 'album-a': 'hidden' });
|
||||
expect(body).not.toHaveProperty('city');
|
||||
expect(body).not.toHaveProperty('country');
|
||||
// The rest of the panel is untouched.
|
||||
expect(body.model).toBe('X-T5');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user