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:
90
lib/__tests__/updateCheck.test.ts
Normal file
90
lib/__tests__/updateCheck.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { compareVersions, isNewerVersion, parseLatestTag } from '@/lib/updateCheck';
|
||||
|
||||
describe('compareVersions', () => {
|
||||
it.each([
|
||||
['0.12.0', '0.12.0', 0],
|
||||
['0.12.1', '0.12.0', 1],
|
||||
['0.12.0', '0.12.1', -1],
|
||||
['1.0.0', '0.99.99', 1],
|
||||
['0.13.0', '0.9.0', 1],
|
||||
])('%s vs %s', (a, b, expected) => {
|
||||
expect(Math.sign(compareVersions(a, b))).toBe(expected);
|
||||
});
|
||||
|
||||
/** 10 must not sort before 9, which is what a string compare would do. */
|
||||
it('compares numerically, not lexically', () => {
|
||||
expect(compareVersions('0.10.0', '0.9.0')).toBeGreaterThan(0);
|
||||
expect(compareVersions('0.2.0', '0.10.0')).toBeLessThan(0);
|
||||
});
|
||||
|
||||
it('tolerates the leading v the releases API uses', () => {
|
||||
expect(compareVersions('v0.12.0', '0.12.0')).toBe(0);
|
||||
expect(compareVersions('V0.13.0', '0.12.0')).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('treats missing segments as zero', () => {
|
||||
expect(compareVersions('1', '1.0.0')).toBe(0);
|
||||
expect(compareVersions('1.1', '1.0.9')).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('compares a pre-release on its numbers alone', () => {
|
||||
expect(compareVersions('0.13.0-rc.1', '0.12.0')).toBeGreaterThan(0);
|
||||
expect(compareVersions('0.12.0-rc.1', '0.12.0')).toBe(0);
|
||||
});
|
||||
|
||||
it('does not crash on rubbish', () => {
|
||||
expect(() => compareVersions('', 'x.y.z')).not.toThrow();
|
||||
expect(compareVersions('x.y.z', '')).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNewerVersion', () => {
|
||||
it('is true only for a strictly newer release', () => {
|
||||
expect(isNewerVersion('0.13.0', '0.12.0')).toBe(true);
|
||||
expect(isNewerVersion('0.12.0', '0.12.0')).toBe(false);
|
||||
expect(isNewerVersion('0.11.0', '0.12.0')).toBe(false);
|
||||
});
|
||||
|
||||
/** A downgraded tag must never be advertised as an update. */
|
||||
it('does not nag when running ahead of the latest release', () => {
|
||||
expect(isNewerVersion('v0.12.0', '0.13.0-dev')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseLatestTag', () => {
|
||||
it('reads tag_name', () => {
|
||||
expect(parseLatestTag({ tag_name: 'v0.13.0' })).toBe('v0.13.0');
|
||||
});
|
||||
|
||||
it('trims surrounding whitespace', () => {
|
||||
expect(parseLatestTag({ tag_name: ' v0.13.0 ' })).toBe('v0.13.0');
|
||||
});
|
||||
|
||||
it.each([
|
||||
['null', null],
|
||||
['a string', 'v0.13.0'],
|
||||
['an array', []],
|
||||
['an error object served with 200', { message: 'API rate limit exceeded' }],
|
||||
['a non-string tag', { tag_name: 42 }],
|
||||
['a tag that is not a version', { tag_name: 'nightly' }],
|
||||
// The check only guarded the *start* of the tag, so anything beginning
|
||||
// with a digit slipped through and was then compared as a version. A
|
||||
// date-style tag reads as version 2024 and leaves every instance showing
|
||||
// an update that does not exist, permanently.
|
||||
['a date-style tag', { tag_name: '2024-01-05' }],
|
||||
['a tag with a digit and then rubbish', { tag_name: '1abc' }],
|
||||
])('returns null for %s', (_label, payload) => {
|
||||
expect(parseLatestTag(payload)).toBeNull();
|
||||
});
|
||||
|
||||
it('still accepts the shapes the project actually tags', () => {
|
||||
expect(parseLatestTag({ tag_name: 'v0.13.0' })).toBe('v0.13.0');
|
||||
expect(parseLatestTag({ tag_name: '0.13.0' })).toBe('0.13.0');
|
||||
expect(parseLatestTag({ tag_name: 'v1.0' })).toBe('v1.0');
|
||||
expect(parseLatestTag({ tag_name: 'v1.2.3-rc1' })).toBe('v1.2.3-rc1');
|
||||
// Build metadata is ignorable for ordering, so rejecting it would mean
|
||||
// never mentioning a release that is genuinely newer.
|
||||
expect(parseLatestTag({ tag_name: 'v1.2.3+build.7' })).toBe('v1.2.3+build.7');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user