From 69237773452c7a33eaf9dd4135f02d416cefce96 Mon Sep 17 00:00:00 2001 From: LyAhn Date: Mon, 6 Jul 2026 20:41:42 +0100 Subject: [PATCH] test(frontend): cover mediaSrc, changelog, and timeline grouping Unit tests for three previously-untested pure functions: mediaSrc's UI Lab path handling, getChangelogForVersion's version normalization, and groupImages' date bucketing/sorting. --- src/changelog.test.ts | 34 ++++++++++++++++ src/components/timeline/timelineModel.test.ts | 40 +++++++++++++++++++ src/lib/mediaSrc.test.ts | 37 +++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 src/changelog.test.ts create mode 100644 src/components/timeline/timelineModel.test.ts create mode 100644 src/lib/mediaSrc.test.ts diff --git a/src/changelog.test.ts b/src/changelog.test.ts new file mode 100644 index 0000000..a8a0937 --- /dev/null +++ b/src/changelog.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest' +import { getChangelogForVersion } from './changelog' + +describe('getChangelogForVersion', () => { + it('returns null for a null/undefined version', () => { + expect(getChangelogForVersion(null)).toBeNull() + expect(getChangelogForVersion(undefined)).toBeNull() + }) + + it('never surfaces the in-progress Unreleased section', () => { + expect(getChangelogForVersion('Unreleased')).toBeNull() + expect(getChangelogForVersion('unreleased')).toBeNull() + }) + + it('returns null for a version with no matching entry', () => { + expect(getChangelogForVersion('99.9.9')).toBeNull() + }) + + it('resolves a plain released version', () => { + const entry = getChangelogForVersion('0.1.1') + expect(entry?.version).toBe('0.1.1') + expect(entry?.date).toBe('2026-06-23') + }) + + it('strips a leading "v" from the version string', () => { + expect(getChangelogForVersion('v0.1.1')?.version).toBe('0.1.1') + }) + + it('strips dev/UI-lab build suffixes so they resolve to the base version', () => { + expect(getChangelogForVersion('0.1.1-dev')?.version).toBe('0.1.1') + expect(getChangelogForVersion('0.1.1-ui')?.version).toBe('0.1.1') + expect(getChangelogForVersion('0.1.1-beta.1')?.version).toBe('0.1.1') + }) +}) diff --git a/src/components/timeline/timelineModel.test.ts b/src/components/timeline/timelineModel.test.ts new file mode 100644 index 0000000..144eec3 --- /dev/null +++ b/src/components/timeline/timelineModel.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest' +import { makeImage } from '../../test/factories' +import { groupImages } from './timelineModel' + +describe('groupImages', () => { + it('buckets images by year-month, preferring taken_at over modified_at', () => { + const groups = groupImages([ + makeImage({ id: 1, taken_at: '2026-03-05T00:00:00Z', modified_at: '2026-01-01T00:00:00Z' }), + makeImage({ id: 2, taken_at: null, modified_at: '2026-03-20T00:00:00Z' }), + ]) + + expect(groups).toHaveLength(1) + expect(groups[0].key).toBe('2026-03') + expect(groups[0].images.map((i) => i.id)).toEqual([1, 2]) + }) + + it('sorts groups chronologically ascending', () => { + const groups = groupImages([ + makeImage({ id: 1, taken_at: '2026-06-01T00:00:00Z' }), + makeImage({ id: 2, taken_at: '2026-01-01T00:00:00Z' }), + makeImage({ id: 3, taken_at: '2026-03-01T00:00:00Z' }), + ]) + + expect(groups.map((g) => g.key)).toEqual(['2026-01', '2026-03', '2026-06']) + }) + + it('buckets images with no date under "unknown" and sorts it last', () => { + const groups = groupImages([ + makeImage({ id: 1, taken_at: null, modified_at: null }), + makeImage({ id: 2, taken_at: '2026-01-01T00:00:00Z' }), + ]) + + expect(groups.map((g) => g.key)).toEqual(['2026-01', 'unknown']) + expect(groups[1].label).toBe('Unknown Date') + }) + + it('returns no groups for an empty image list', () => { + expect(groupImages([])).toEqual([]) + }) +}) diff --git a/src/lib/mediaSrc.test.ts b/src/lib/mediaSrc.test.ts new file mode 100644 index 0000000..0d709f3 --- /dev/null +++ b/src/lib/mediaSrc.test.ts @@ -0,0 +1,37 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' + +vi.mock('@tauri-apps/api/core', () => ({ + convertFileSrc: (path: string) => `asset://localhost/${path}`, +})) + +import { mediaSrc } from './mediaSrc' + +afterEach(() => { + vi.unstubAllEnvs() +}) + +describe('mediaSrc', () => { + it('returns null for a null path', () => { + expect(mediaSrc(null)).toBeNull() + }) + + it('delegates to convertFileSrc outside UI Lab mode', () => { + expect(mediaSrc('C:/media/image.jpg')).toBe('asset://localhost/C:/media/image.jpg') + }) + + it('passes through absolute/http paths unchanged in UI Lab mode', () => { + vi.stubEnv('MODE', 'ui') + expect(mediaSrc('/dev-media/image.jpg')).toBe('/dev-media/image.jpg') + expect(mediaSrc('http://example.com/image.jpg')).toBe('http://example.com/image.jpg') + }) + + it('rewrites mock:// paths to /dev-media/ in UI Lab mode', () => { + vi.stubEnv('MODE', 'ui') + expect(mediaSrc('mock://folder/image.jpg')).toBe('/dev-media/folder/image.jpg') + }) + + it('falls back to convertFileSrc for other paths in UI Lab mode', () => { + vi.stubEnv('MODE', 'ui') + expect(mediaSrc('C:/media/image.jpg')).toBe('asset://localhost/C:/media/image.jpg') + }) +})