fix(ci): add changelog fragment for immersion changes

This commit is contained in:
2026-03-22 19:07:07 -07:00
parent 8928bfdf7e
commit 8da3a26855
17 changed files with 1109 additions and 18 deletions

View File

@@ -0,0 +1,99 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { renderToStaticMarkup } from 'react-dom/server';
import type { MediaLibraryItem } from '../types/stats';
import { groupMediaLibraryItems, resolveMediaArtworkUrl } from './media-library-grouping';
import { CoverImage } from '../components/library/CoverImage';
const youtubeEpisodeA: MediaLibraryItem = {
videoId: 1,
canonicalTitle: 'Episode 1',
totalSessions: 2,
totalActiveMs: 12_000,
totalCards: 3,
totalTokensSeen: 120,
lastWatchedMs: 3_000,
hasCoverArt: 1,
youtubeVideoId: 'yt-1',
videoUrl: 'https://www.youtube.com/watch?v=yt-1',
videoTitle: 'Video 1',
videoThumbnailUrl: 'https://i.ytimg.com/vi/yt-1/hqdefault.jpg',
channelId: 'UC123',
channelName: 'Creator Name',
channelUrl: 'https://www.youtube.com/channel/UC123',
channelThumbnailUrl: 'https://yt3.googleusercontent.com/channel-avatar=s88',
uploaderId: '@creator',
uploaderUrl: 'https://www.youtube.com/@creator',
description: 'desc',
};
const youtubeEpisodeB: MediaLibraryItem = {
...youtubeEpisodeA,
videoId: 2,
canonicalTitle: 'Episode 2',
youtubeVideoId: 'yt-2',
videoUrl: 'https://www.youtube.com/watch?v=yt-2',
videoTitle: 'Video 2',
videoThumbnailUrl: 'https://i.ytimg.com/vi/yt-2/hqdefault.jpg',
lastWatchedMs: 4_000,
};
const localVideo: MediaLibraryItem = {
videoId: 3,
canonicalTitle: 'Local Movie',
totalSessions: 1,
totalActiveMs: 5_000,
totalCards: 0,
totalTokensSeen: 40,
lastWatchedMs: 2_000,
hasCoverArt: 1,
youtubeVideoId: null,
videoUrl: null,
videoTitle: null,
videoThumbnailUrl: null,
channelId: null,
channelName: null,
channelUrl: null,
channelThumbnailUrl: null,
uploaderId: null,
uploaderUrl: null,
description: null,
};
test('groupMediaLibraryItems groups youtube videos by channel and leaves local media standalone', () => {
const groups = groupMediaLibraryItems([youtubeEpisodeA, localVideo, youtubeEpisodeB]);
assert.equal(groups.length, 2);
assert.equal(groups[0]?.title, 'Creator Name');
assert.equal(groups[0]?.items.length, 2);
assert.equal(groups[0]?.items[0]?.videoId, 2);
assert.equal(groups[0]?.imageUrl, 'https://yt3.googleusercontent.com/channel-avatar=s88');
assert.equal(groups[1]?.title, 'Local Movie');
assert.equal(groups[1]?.items.length, 1);
});
test('resolveMediaArtworkUrl prefers youtube thumbnails for video and channel images', () => {
assert.equal(
resolveMediaArtworkUrl(youtubeEpisodeA, 'video'),
'https://i.ytimg.com/vi/yt-1/hqdefault.jpg',
);
assert.equal(
resolveMediaArtworkUrl(youtubeEpisodeA, 'channel'),
'https://yt3.googleusercontent.com/channel-avatar=s88',
);
assert.equal(resolveMediaArtworkUrl(localVideo, 'video'), null);
assert.equal(resolveMediaArtworkUrl(localVideo, 'channel'), null);
});
test('CoverImage renders explicit remote artwork when src is provided', () => {
const markup = renderToStaticMarkup(
<CoverImage
videoId={youtubeEpisodeA.videoId}
title={youtubeEpisodeA.canonicalTitle}
src={youtubeEpisodeA.videoThumbnailUrl}
className="w-8 h-8"
/>,
);
assert.match(markup, /src="https:\/\/i\.ytimg\.com\/vi\/yt-1\/hqdefault\.jpg"/);
});