release: v0.20.0
This commit is contained in:
164
lib/__tests__/zoom-config.test.ts
Normal file
164
lib/__tests__/zoom-config.test.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
vi.mock('@/lib/env', () => ({
|
||||
env: {
|
||||
IMMICH_API_URL: 'http://localhost:2283',
|
||||
IMMICH_API_KEY: 'test-key',
|
||||
SITE_TITLE: 'Test',
|
||||
SITE_SUBTITLE: '',
|
||||
CACHE_TTL: 300,
|
||||
RATE_LIMIT_RPM: 120,
|
||||
},
|
||||
normalizeApiUrl: (raw: string) => raw.replace(/\/+$/, ''),
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/secret', () => ({
|
||||
resolveAuthSecret: () => 'test-auth-secret-32-chars-long-min',
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/config/parser', () => ({
|
||||
loadYaml: vi.fn(),
|
||||
clearYamlCache: vi.fn(),
|
||||
validateUuid: (id: string) => id,
|
||||
}));
|
||||
|
||||
import { deriveGallery, getConfig, invalidateConfigCache, resolveZoom } from '@/lib/config';
|
||||
import { loadYaml } from '@/lib/config/parser';
|
||||
import type { GalleryYaml } from '@/lib/config/schema';
|
||||
|
||||
const A = '11111111-1111-1111-1111-111111111111';
|
||||
const B = '22222222-2222-2222-2222-222222222222';
|
||||
|
||||
/**
|
||||
* Lightbox zoom (#467): a site-wide switch in settings.yaml, off by default,
|
||||
* overridden by a subpage and then by an album, each in either direction.
|
||||
*/
|
||||
describe('resolveZoom', () => {
|
||||
const config = (zoom: boolean, standaloneAlbumZoom: Record<string, boolean> = {}) => ({
|
||||
zoom,
|
||||
standaloneAlbumZoom,
|
||||
});
|
||||
|
||||
it('follows the site setting when nothing more specific is set', () => {
|
||||
expect(resolveZoom(config(false), A)).toBe(false);
|
||||
expect(resolveZoom(config(true), A)).toBe(true);
|
||||
expect(resolveZoom(config(true), A, {})).toBe(true);
|
||||
});
|
||||
|
||||
it('lets a subpage override the site, both ways', () => {
|
||||
expect(resolveZoom(config(false), A, { zoom: true })).toBe(true);
|
||||
expect(resolveZoom(config(true), A, { zoom: false })).toBe(false);
|
||||
});
|
||||
|
||||
it('lets an album entry override its subpage and the site, both ways', () => {
|
||||
expect(resolveZoom(config(false), A, { zoom: false, albumZoom: { [A]: true } })).toBe(true);
|
||||
expect(resolveZoom(config(true), A, { zoom: true, albumZoom: { [A]: false } })).toBe(false);
|
||||
// Only that album.
|
||||
expect(resolveZoom(config(true), B, { zoom: true, albumZoom: { [A]: false } })).toBe(true);
|
||||
});
|
||||
|
||||
it('reads the standalone override only for the standalone route', () => {
|
||||
const c = config(true, { [A]: false });
|
||||
expect(resolveZoom(c, A)).toBe(false);
|
||||
// On a subpage the standalone entry has no say.
|
||||
expect(resolveZoom(c, A, {})).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deriveGallery keeps album zoom per route (review of #830)', () => {
|
||||
/*
|
||||
* The reported leak: one album on an open page with zoom off and on a
|
||||
* password page with zoom on. A single map let the last entry decide for
|
||||
* both, so the open page served zoom its owner had switched off.
|
||||
*/
|
||||
const pages = (first: boolean) => {
|
||||
const open = { name: 'Public', albums: [{ [A]: { zoom: false } }] };
|
||||
const locked = { name: 'Client', password: 'pw', albums: [{ [A]: { zoom: true } }] };
|
||||
return deriveGallery({ subpages: first ? [open, locked] : [locked, open] } as GalleryYaml);
|
||||
};
|
||||
|
||||
it.each([true, false])('keeps each page’s own value (open page first: %s)', (openFirst) => {
|
||||
const derived = pages(openFirst);
|
||||
const open = derived.subpages.find((sp) => sp.slug === 'public')!;
|
||||
const locked = derived.subpages.find((sp) => sp.slug === 'client')!;
|
||||
const cfg = { zoom: false, standaloneAlbumZoom: derived.standaloneAlbumZoom };
|
||||
expect(resolveZoom(cfg, A, open)).toBe(false);
|
||||
expect(resolveZoom(cfg, A, locked)).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps a standalone entry apart from a subpage entry for the same album', () => {
|
||||
const derived = deriveGallery({
|
||||
albums: [{ [A]: { zoom: true } }],
|
||||
subpages: [{ name: 'S', albums: [{ [A]: { zoom: false } }] }],
|
||||
} as GalleryYaml);
|
||||
expect(derived.standaloneAlbumZoom).toEqual({ [A]: true });
|
||||
expect(derived.subpages[0].albumZoom).toEqual({ [A]: false });
|
||||
});
|
||||
|
||||
it('collects section entries on their subpage, and false wins within one page', () => {
|
||||
const derived = deriveGallery({
|
||||
subpages: [
|
||||
{
|
||||
name: 'S',
|
||||
albums: [{ [B]: { zoom: true } }],
|
||||
sections: [
|
||||
{ title: 'One', albums: [{ [A]: { zoom: true } }] },
|
||||
{ title: 'Two', albums: [{ [A]: { zoom: false } }] },
|
||||
],
|
||||
},
|
||||
{ name: 'T', albums: [A] },
|
||||
],
|
||||
} as GalleryYaml);
|
||||
expect(derived.subpages[0].albumZoom).toEqual({ [A]: false, [B]: true });
|
||||
expect(derived.subpages[1].albumZoom).toBeUndefined();
|
||||
});
|
||||
|
||||
it('ignores anything but a boolean', () => {
|
||||
const odd = deriveGallery({ albums: [{ [A]: { zoom: 'yes' } }] } as unknown as GalleryYaml);
|
||||
expect(odd.standaloneAlbumZoom).toEqual({});
|
||||
});
|
||||
|
||||
it('keeps a subpage’s zoom in both gallery.yaml shapes', () => {
|
||||
const list = deriveGallery({
|
||||
subpages: [
|
||||
{ name: 'On', albums: [A], zoom: true },
|
||||
{ name: 'Off', albums: [B], zoom: false },
|
||||
{ name: 'Unset', albums: [A] },
|
||||
],
|
||||
} as GalleryYaml);
|
||||
expect(list.subpages.map((sp) => sp.zoom)).toEqual([true, false, undefined]);
|
||||
|
||||
const record = deriveGallery({
|
||||
subpages: { Travel: { albums: [{ [A]: { zoom: false } }], zoom: true } },
|
||||
} as unknown as GalleryYaml);
|
||||
expect(record.subpages[0].zoom).toBe(true);
|
||||
expect(record.subpages[0].albumZoom).toEqual({ [A]: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('the site setting', () => {
|
||||
beforeEach(() => invalidateConfigCache());
|
||||
|
||||
const withSettings = (settings: Record<string, unknown>) =>
|
||||
vi.mocked(loadYaml).mockImplementation((filename: string) => {
|
||||
if (filename === 'gallery.yaml') return { albums: [{ [A]: { zoom: false } }] };
|
||||
if (filename === 'settings.yaml') return settings;
|
||||
return null;
|
||||
});
|
||||
|
||||
it('is off unless settings.yaml says true', () => {
|
||||
withSettings({});
|
||||
expect(getConfig().zoom).toBe(false);
|
||||
invalidateConfigCache();
|
||||
withSettings({ zoom: 'true' });
|
||||
expect(getConfig().zoom).toBe(false);
|
||||
invalidateConfigCache();
|
||||
withSettings({ zoom: true });
|
||||
expect(getConfig().zoom).toBe(true);
|
||||
});
|
||||
|
||||
it('carries the standalone album overrides into the config', () => {
|
||||
withSettings({ zoom: true });
|
||||
expect(getConfig().standaloneAlbumZoom).toEqual({ [A]: false });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user