Fix warnings and update tracability
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 14s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped
Traceability Validation / Check Requirement Traces (push) Failing after 1s

This commit is contained in:
2026-02-28 20:54:25 +01:00
parent c5be9eb18c
commit 3a9c126dfe
59 changed files with 1285 additions and 396 deletions
@@ -67,9 +67,9 @@
// Load related artists (by genre)
try {
if (artist.genres && artist.genres.length > 0) {
const relatedResult = await repo.getItems(undefined, {
const relatedResult = await repo.getItems(artist.id, {
includeItemTypes: ["MusicArtist"],
genreIds: artist.genres.slice(0, 2),
genres: artist.genres.slice(0, 2),
limit: 12,
sortBy: "CommunityRating",
sortOrder: "Descending"
@@ -297,7 +297,7 @@ describe.skip("Async Image Loading Pattern", () => {
const promises = items.map(item =>
mockRepository.getImageUrl(item.id, "Primary")
.then(url => imageUrls.set(item.id, url))
.then((url: string) => imageUrls.set(item.id, url))
.catch(() => imageUrls.set(item.id, ""))
);
@@ -1,3 +1,4 @@
<!-- TRACES: UR-035 | JA-029 | DR-040, DR-044 -->
<script lang="ts">
import type { Person, PersonType } from "$lib/api/types";
import { goto } from "$app/navigation";
@@ -1,3 +1,4 @@
<!-- TRACES: UR-035 | JA-029 | DR-040 -->
<script lang="ts">
import { goto } from "$app/navigation";
import type { Person } from "$lib/api/types";
@@ -8,7 +8,7 @@
import ResultsCounter from "$lib/components/common/ResultsCounter.svelte";
import CachedImage from "$lib/components/common/CachedImage.svelte";
import { useServerReachabilityReload } from "$lib/composables/useServerReachabilityReload";
import type { Genre, MediaItem } from "$lib/api/types";
import type { Genre, MediaItem, ItemType } from "$lib/api/types";
/**
* Generic genre browser supporting Movies, Music Albums, and TV Series
@@ -20,7 +20,7 @@
*/
export interface GenreConfig {
itemTypes: string[]; // ["Movie"] or ["MusicAlbum"] or ["Series"]
itemTypes: ItemType[]; // ["Movie"] or ["MusicAlbum"] or ["Series"]
title: string; // "Movie Genres" or "Genres" or "TV Genres"
backPath: string; // "/library" or "/library/music"
genreIcon: string; // SVG path for genre icon
@@ -1,3 +1,4 @@
<!-- TRACES: UR-007, UR-029, UR-030 | DR-007, DR-032, DR-033 -->
<script lang="ts">
import { onMount } from "svelte";
import { goto } from "$app/navigation";
@@ -9,7 +10,7 @@
import BackButton from "$lib/components/common/BackButton.svelte";
import ResultsCounter from "$lib/components/common/ResultsCounter.svelte";
import { useServerReachabilityReload } from "$lib/composables/useServerReachabilityReload";
import type { MediaItem } from "$lib/api/types";
import type { MediaItem, Library, ItemType } from "$lib/api/types";
import LibraryGrid from "./LibraryGrid.svelte";
import TrackList from "./TrackList.svelte";
@@ -23,7 +24,7 @@
*/
export interface MediaListConfig {
itemType: string; // "MusicAlbum", "MusicArtist", "Playlist", "Audio"
itemType: ItemType; // "MusicAlbum", "MusicArtist", "Playlist", "Audio"
title: string; // "Albums", "Artists", "Playlists", "Tracks"
backPath: string; // "/library/music"
searchPlaceholder?: string;
@@ -127,7 +128,7 @@
const searchPlaceholder = $derived(config.searchPlaceholder || `Search ${config.title.toLowerCase()}...`);
function handleItemClick(item: MediaItem) {
function handleItemClick(item: MediaItem | Library) {
// Navigate to detail page for browseable items
goto(`/library/${item.id}`);
}
@@ -45,7 +45,7 @@ describe.skip("GenericMediaListPage", () => {
describe("Component Initialization", () => {
it("should render with title and search bar", () => {
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -67,7 +67,7 @@ describe.skip("GenericMediaListPage", () => {
it("should load items on mount", async () => {
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -87,7 +87,7 @@ describe.skip("GenericMediaListPage", () => {
it("should display sort options", () => {
const config = {
itemType: "MusicAlbum",
itemType: "MusicAlbum" as const,
title: "Albums",
backPath: "/library/music",
searchPlaceholder: "Search albums...",
@@ -115,7 +115,7 @@ describe.skip("GenericMediaListPage", () => {
vi.useFakeTimers();
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -169,7 +169,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -215,7 +215,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -260,7 +260,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -309,7 +309,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "MusicAlbum",
itemType: "MusicAlbum" as const,
title: "Albums",
backPath: "/library/music",
searchPlaceholder: "Search albums...",
@@ -349,7 +349,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -395,7 +395,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -433,7 +433,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "MusicAlbum",
itemType: "MusicAlbum" as const,
title: "Albums",
backPath: "/library/music",
searchPlaceholder: "Search albums...",
@@ -479,7 +479,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -515,7 +515,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -558,7 +558,7 @@ describe.skip("GenericMediaListPage", () => {
);
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -581,7 +581,7 @@ describe.skip("GenericMediaListPage", () => {
describe("Display Component Props", () => {
it("should support grid display component", () => {
const config = {
itemType: "MusicAlbum",
itemType: "MusicAlbum" as const,
title: "Albums",
backPath: "/library/music",
searchPlaceholder: "Search albums...",
@@ -599,7 +599,7 @@ describe.skip("GenericMediaListPage", () => {
it("should support tracklist display component", () => {
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -619,7 +619,7 @@ describe.skip("GenericMediaListPage", () => {
describe("Config Simplification", () => {
it("should not require searchFields in config", () => {
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -639,7 +639,7 @@ describe.skip("GenericMediaListPage", () => {
it("should not require compareFn in sort options", () => {
const config = {
itemType: "Audio",
itemType: "Audio" as const,
title: "Tracks",
backPath: "/library/music",
searchPlaceholder: "Search tracks...",
@@ -115,9 +115,11 @@
<!-- Download indicator -->
{#if showDownloadStatus && (isDownloaded || isDownloading)}
{#if isDownloaded}
<svg class="w-4 h-4 text-green-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" title="Downloaded">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v12m0 0l-4-4m4 4l4-4" />
</svg>
<span title="Downloaded">
<svg class="w-4 h-4 text-green-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" aria-label="Downloaded">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v12m0 0l-4-4m4 4l4-4" />
</svg>
</span>
{:else if isDownloading}
<div class="w-4 h-4 relative flex-shrink-0" title="Downloading...">
<svg class="w-4 h-4 -rotate-90" viewBox="0 0 24 24">
+26 -13
View File
@@ -35,7 +35,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "abc123",
};
@@ -67,7 +68,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "abc123",
};
@@ -100,7 +102,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem1 = {
id: "item1",
name: "Album 1",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "tag1",
};
@@ -118,7 +121,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem2 = {
id: "item2",
name: "Album 2",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "tag2",
};
@@ -136,7 +140,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "abc123",
};
@@ -159,7 +164,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
// primaryImageTag is undefined
};
@@ -182,7 +188,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "abc123",
};
@@ -206,7 +213,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "abc123",
};
@@ -231,7 +239,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "tag123",
};
@@ -259,7 +268,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "abc123",
};
@@ -292,14 +302,16 @@ describe.skip("MediaCard - Async Image Loading", () => {
const item1 = {
id: "item1",
name: "Album 1",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "tag1",
};
const item2 = {
id: "item2",
name: "Album 2",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "tag2",
};
@@ -331,7 +343,8 @@ describe.skip("MediaCard - Async Image Loading", () => {
const mediaItem = {
id: "item123",
name: "Test Album",
type: "MusicAlbum",
type: "MusicAlbum" as const,
serverId: "server-1",
primaryImageTag: "abc123",
};
@@ -1,5 +1,6 @@
<!-- TRACES: UR-036 | JA-030, JA-031 | DR-041 -->
<script lang="ts">
import type { MediaItem } from "$lib/api/types";
import type { MediaItem, Library } from "$lib/api/types";
import { auth } from "$lib/stores/auth";
import { onMount } from "svelte";
import LibraryGrid from "./LibraryGrid.svelte";
@@ -39,7 +40,7 @@
}
}
function handleItemClick(item: MediaItem) {
function handleItemClick(item: MediaItem | Library) {
goto(`/library/${item.id}`);
}
</script>
@@ -322,16 +322,10 @@ describe("TrackList Logic Tests", () => {
it("should invoke player when no custom callback", async () => {
const invokeMock = (invoke as any).mockResolvedValue(undefined);
// Simulate unified handler without custom callback
const onTrackClick = undefined;
if (onTrackClick) {
await onTrackClick(mockTracks[0], 0);
} else {
// This branch executes - default handler
await invoke("player_play_queue", {
request: { items: [], startIndex: 0, shuffle: false },
});
}
// Simulate unified handler without custom callback - default handler runs
await invoke("player_play_queue", {
request: { items: [], startIndex: 0, shuffle: false },
});
expect(invokeMock).toHaveBeenCalled();
});
@@ -332,8 +332,6 @@ describe.skip("TrackList", () => {
// NOTE: This test is skipped because stream URLs are no longer fetched by frontend.
// The code now uses player_play_tracks which sends trackIds to backend.
// Backend handles all stream URL generation, so this error path no longer exists.
alertSpy.mockRestore();
});
});