release: v0.16.0

See CHANGELOG.md for the full v0.16.0 entry.

---------

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>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
This commit is contained in:
Ralksta
2026-09-22 14:12:13 +02:00
committed by GitHub
parent c3f92ca4ec
commit a3a6319c29
120 changed files with 6818 additions and 975 deletions

View File

@@ -0,0 +1,116 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
vi.mock('@/lib/env', () => ({
env: {
IMMICH_API_URL: 'http://localhost:2283',
IMMICH_API_KEY: 'test-key',
SITE_TITLE: 'Test Gallery',
SITE_SUBTITLE: '',
CACHE_TTL: 300,
IMMICH_TIMEOUT_MS: 15000,
RATE_LIMIT_RPM: 120,
TRUSTED_PROXY_HOPS: 0,
},
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 { getConfig, invalidateConfigCache, deriveGallery } from '@/lib/config';
import { loadYaml } from '@/lib/config/parser';
import type { GalleryYaml } from '@/lib/config/schema';
const ALBUM_ID = '11111111-1111-1111-1111-111111111111';
function withSettings(settings: unknown) {
vi.mocked(loadYaml).mockImplementation((filename: string) => {
if (filename === 'gallery.yaml') return { albums: [ALBUM_ID] };
if (filename === 'settings.yaml') return settings;
return null;
});
}
afterEach(() => {
invalidateConfigCache();
vi.clearAllMocks();
});
/**
* `--grid-columns` is emitted unclamped in app/[...path]/page.tsx, so a bad
* `columns` reaches `repeat()` invalid and collapses the whole grid to one
* column with no error anywhere (#633). The cover grid already clamped
* (buildCoverGridVars); the site-wide photo grid did not.
*/
describe('site-wide grid.columns / grid.gap clamping (#633)', () => {
it('clamps a negative columns count to the minimum', () => {
withSettings({ grid: { columns: -1 } });
expect(getConfig().grid.columns).toBe(1);
});
it('clamps an out-of-range columns count to the maximum', () => {
withSettings({ grid: { columns: 99 } });
expect(getConfig().grid.columns).toBe(6);
});
it('falls back to the default when columns is not a number', () => {
withSettings({ grid: { columns: 'three' as unknown as number } });
expect(getConfig().grid.columns).toBe(1); // clamp()'s min, not silently NaN
});
it('keeps a legitimate columns value', () => {
withSettings({ grid: { columns: 4 } });
expect(getConfig().grid.columns).toBe(4);
});
it('defaults columns to 3 when unset', () => {
withSettings({});
expect(getConfig().grid.columns).toBe(3);
});
it('clamps gap the same way', () => {
withSettings({ grid: { gap: -5 } });
expect(getConfig().grid.gap).toBe(0);
withSettings({ grid: { gap: 9999 } });
invalidateConfigCache();
expect(getConfig().grid.gap).toBe(48);
});
});
/**
* Same guard for a subpage's or an album's own grid override — the merged
* value (site-wide default overridden by this) is what actually reaches
* `--grid-columns`.
*/
describe('per-subpage grid override clamping (#633)', () => {
it('clamps a negative subpage columns override', () => {
const gallery = {
subpages: [{ name: 'Trips', albums: [ALBUM_ID], grid: { columns: -1 } }],
} as unknown as GalleryYaml;
const derived = deriveGallery(gallery);
expect(derived.subpages[0].grid?.columns).toBe(1);
});
it('clamps an out-of-range subpage columns override', () => {
const gallery = {
subpages: [{ name: 'Trips', albums: [ALBUM_ID], grid: { columns: 40 } }],
} as unknown as GalleryYaml;
const derived = deriveGallery(gallery);
expect(derived.subpages[0].grid?.columns).toBe(6);
});
it('keeps a legitimate subpage columns override', () => {
const gallery = {
subpages: [{ name: 'Trips', albums: [ALBUM_ID], grid: { columns: 2 } }],
} as unknown as GalleryYaml;
const derived = deriveGallery(gallery);
expect(derived.subpages[0].grid?.columns).toBe(2);
});
});