fix tests
Some checks failed
Traceability Validation / Check Requirement Traces (push) Has been cancelled
🏗️ Build and Test JellyTau / Build APK and Run Tests (push) Has been cancelled

This commit is contained in:
Duncan Tourolle 2026-02-14 16:39:46 +01:00
parent b8363aa993
commit 59270e8a4f
14 changed files with 86 additions and 37 deletions

View File

@ -84,6 +84,7 @@ RUN cd src-tauri && cargo fetch && cd ..
FROM builder AS test FROM builder AS test
WORKDIR /app WORKDIR /app
RUN echo "Running tests..." && \ RUN echo "Running tests..." && \
bunx svelte-kit sync && \
bun run test && \ bun run test && \
cd src-tauri && cargo test && cd .. && \ cd src-tauri && cargo test && cd .. && \
echo "All tests passed!" echo "All tests passed!"

View File

@ -12,7 +12,7 @@ services:
- .:/app - .:/app
environment: environment:
- RUST_BACKTRACE=1 - RUST_BACKTRACE=1
command: bash -c "bun test && cd src-tauri && cargo test && cd .. && echo 'All tests passed!'" command: bash -c "bunx svelte-kit sync && bun test && cd src-tauri && cargo test && cd .. && echo 'All tests passed!'"
# Android build service - builds APK after tests pass # Android build service - builds APK after tests pass
android-build: android-build:

View File

@ -369,6 +369,7 @@ pub fn repository_get_image_url(
/// Get subtitle URL for a media item /// Get subtitle URL for a media item
#[tauri::command] #[tauri::command]
#[allow(dead_code)]
pub fn repository_get_subtitle_url( pub fn repository_get_subtitle_url(
manager: State<'_, RepositoryManagerWrapper>, manager: State<'_, RepositoryManagerWrapper>,
handle: String, handle: String,
@ -383,6 +384,7 @@ pub fn repository_get_subtitle_url(
/// Get video download URL with quality preset /// Get video download URL with quality preset
#[tauri::command] #[tauri::command]
#[allow(dead_code)]
pub fn repository_get_video_download_url( pub fn repository_get_video_download_url(
manager: State<'_, RepositoryManagerWrapper>, manager: State<'_, RepositoryManagerWrapper>,
handle: String, handle: String,

View File

@ -147,6 +147,8 @@ pub trait MediaRepository: Send + Sync {
) -> String; ) -> String;
/// Get subtitle URL (synchronous - just constructs URL) /// Get subtitle URL (synchronous - just constructs URL)
/// Called by frontend via Tauri invoke (getSubtitleUrl in VideoPlayer.svelte)
#[allow(dead_code)]
fn get_subtitle_url( fn get_subtitle_url(
&self, &self,
item_id: &str, item_id: &str,
@ -156,6 +158,8 @@ pub trait MediaRepository: Send + Sync {
) -> String; ) -> String;
/// Get video download URL (synchronous - just constructs URL) /// Get video download URL (synchronous - just constructs URL)
/// Called by frontend via Tauri invoke (getVideoDownloadUrl in VideoDownloadButton.svelte)
#[allow(dead_code)]
fn get_video_download_url( fn get_video_download_url(
&self, &self,
item_id: &str, item_id: &str,

View File

@ -6,6 +6,21 @@
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
export interface AutoplaySettings {
enabled: boolean;
countdownSeconds: number;
}
export async function getAutoplaySettings(): Promise<AutoplaySettings> {
return invoke("player_get_autoplay_settings");
}
export async function setAutoplaySettings(
settings: AutoplaySettings
): Promise<AutoplaySettings> {
return invoke("player_set_autoplay_settings", { settings });
}
export async function cancelAutoplayCountdown(): Promise<void> { export async function cancelAutoplayCountdown(): Promise<void> {
return invoke("player_cancel_autoplay_countdown"); return invoke("player_cancel_autoplay_countdown");
} }

View File

@ -2,7 +2,11 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { RepositoryClient } from "./repository-client"; import { RepositoryClient } from "./repository-client";
vi.mock("@tauri-apps/api/core"); const mockInvoke = vi.fn();
vi.mock("@tauri-apps/api/core", () => ({
invoke: mockInvoke,
}));
/** /**
* Integration tests documenting Phase 1 & 2 refactoring: * Integration tests documenting Phase 1 & 2 refactoring:

View File

@ -2,7 +2,11 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { RepositoryClient } from "./repository-client"; import { RepositoryClient } from "./repository-client";
vi.mock("@tauri-apps/api/core"); const mockInvoke = vi.fn();
vi.mock("@tauri-apps/api/core", () => ({
invoke: mockInvoke,
}));
describe("RepositoryClient", () => { describe("RepositoryClient", () => {
let client: RepositoryClient; let client: RepositoryClient;

View File

@ -25,8 +25,8 @@
playlist: { singular: "playlist", plural: "playlists" }, playlist: { singular: "playlist", plural: "playlists" },
}; };
const labels = itemTypeLabels[itemType] || { singular: itemType, plural: `${itemType}s` }; const labels = $derived(itemTypeLabels[itemType] || { singular: itemType, plural: `${itemType}s` });
const label = count === 1 ? labels.singular : labels.plural; const label = $derived(count === 1 ? labels.singular : labels.plural);
</script> </script>
<p class={`text-sm text-gray-400 ${className}`}> <p class={`text-sm text-gray-400 ${className}`}>

View File

@ -160,6 +160,7 @@
</script> </script>
<div <div
role="region"
class="relative h-[500px] rounded-xl overflow-hidden group mb-8 touch-pan-y" class="relative h-[500px] rounded-xl overflow-hidden group mb-8 touch-pan-y"
ontouchstart={handleTouchStart} ontouchstart={handleTouchStart}
ontouchmove={handleTouchMove} ontouchmove={handleTouchMove}

View File

@ -39,7 +39,7 @@
}; };
const circumference = 2 * Math.PI * 15; const circumference = 2 * Math.PI * 15;
const offset = circumference - (state.progress || 0) * circumference; const offset = $derived(circumference - (state.progress || 0) * circumference);
</script> </script>
<button <button

View File

@ -156,13 +156,14 @@
} }
} }
const aspectRatioClass = config.itemDisplayMode === "poster" ? "aspect-[2/3]" : "aspect-square"; const aspectRatioClass = $derived(config.itemDisplayMode === "poster" ? "aspect-[2/3]" : "aspect-square");
const gridColsClass = const gridColsClass = $derived(
config.itemDisplayMode === "poster" config.itemDisplayMode === "poster"
? "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5" ? "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5"
: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6"; : "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6"
const searchPlaceholder = config.searchPlaceholder || `Search ${config.title.toLowerCase()}...`; );
const noItemsMessage = config.noItemsMessage || `No ${config.itemTypes[0]?.toLowerCase() || "items"} found in this genre`; const searchPlaceholder = $derived(config.searchPlaceholder || `Search ${config.title.toLowerCase()}...`);
const noItemsMessage = $derived(config.noItemsMessage || `No ${config.itemTypes[0]?.toLowerCase() || "items"} found in this genre`);
</script> </script>
<div class="space-y-6"> <div class="space-y-6">

View File

@ -42,10 +42,14 @@
let loading = $state(true); let loading = $state(true);
let searchQuery = $state(""); let searchQuery = $state("");
let debouncedSearchQuery = $state(""); let debouncedSearchQuery = $state("");
let sortBy = $state<string>(config.defaultSort); let sortBy = $state<string>("");
let sortOrder = $state<"Ascending" | "Descending">("Ascending"); let sortOrder = $state<"Ascending" | "Descending">("Ascending");
let searchTimeout: ReturnType<typeof setTimeout> | null = null; let searchTimeout: ReturnType<typeof setTimeout> | null = null;
$effect(() => {
sortBy = config.defaultSort;
});
const { markLoaded } = useServerReachabilityReload(async () => { const { markLoaded } = useServerReachabilityReload(async () => {
await loadItems(); await loadItems();
}); });
@ -117,7 +121,7 @@
goto(config.backPath); goto(config.backPath);
} }
const searchPlaceholder = config.searchPlaceholder || `Search ${config.title.toLowerCase()}...`; const searchPlaceholder = $derived(config.searchPlaceholder || `Search ${config.title.toLowerCase()}...`);
function handleItemClick(item: MediaItem) { function handleItemClick(item: MediaItem) {
// Navigate to detail page for browseable items // Navigate to detail page for browseable items

View File

@ -254,13 +254,15 @@
<div class="text-gray-300 truncate flex flex-wrap items-center gap-1"> <div class="text-gray-300 truncate flex flex-wrap items-center gap-1">
{#if track.artistItems && track.artistItems.length > 0} {#if track.artistItems && track.artistItems.length > 0}
{#each track.artistItems as artist, idx} {#each track.artistItems as artist, idx}
<button <span
type="button" role="button"
tabindex="0"
onclick={(e) => handleArtistClick(artist.id, e)} onclick={(e) => handleArtistClick(artist.id, e)}
class="text-[var(--color-jellyfin)] hover:underline truncate" onkeydown={(e) => e.key === 'Enter' && handleArtistClick(artist.id, e)}
class="text-[var(--color-jellyfin)] hover:underline truncate cursor-pointer"
> >
{artist.name} {artist.name}
</button> </span>
{#if idx < track.artistItems.length - 1} {#if idx < track.artistItems.length - 1}
<span>,</span> <span>,</span>
{/if} {/if}
@ -275,13 +277,15 @@
{#if showAlbum} {#if showAlbum}
<div class="text-gray-300 truncate"> <div class="text-gray-300 truncate">
{#if track.albumId} {#if track.albumId}
<button <span
type="button" role="button"
tabindex="0"
onclick={(e) => handleAlbumClick(track.albumId, e)} onclick={(e) => handleAlbumClick(track.albumId, e)}
class="text-[var(--color-jellyfin)] hover:underline truncate" onkeydown={(e) => e.key === 'Enter' && handleAlbumClick(track.albumId, e)}
class="text-[var(--color-jellyfin)] hover:underline truncate cursor-pointer"
> >
{track.albumName || "-"} {track.albumName || "-"}
</button> </span>
{:else} {:else}
{track.albumName || "-"} {track.albumName || "-"}
{/if} {/if}
@ -361,13 +365,15 @@
{#if showArtist && showAlbum} {#if showArtist && showAlbum}
{#if track.artistItems && track.artistItems.length > 0} {#if track.artistItems && track.artistItems.length > 0}
{#each track.artistItems as artist, idx} {#each track.artistItems as artist, idx}
<button <span
type="button" role="button"
tabindex="0"
onclick={(e) => handleArtistClick(artist.id, e)} onclick={(e) => handleArtistClick(artist.id, e)}
class="text-[var(--color-jellyfin)] hover:underline" onkeydown={(e) => e.key === 'Enter' && handleArtistClick(artist.id, e)}
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
> >
{artist.name} {artist.name}
</button> </span>
{#if idx < track.artistItems.length - 1} {#if idx < track.artistItems.length - 1}
<span>,</span> <span>,</span>
{/if} {/if}
@ -377,26 +383,30 @@
{/if} {/if}
<span></span> <span></span>
{#if track.albumId} {#if track.albumId}
<button <span
type="button" role="button"
tabindex="0"
onclick={(e) => handleAlbumClick(track.albumId, e)} onclick={(e) => handleAlbumClick(track.albumId, e)}
class="text-[var(--color-jellyfin)] hover:underline" onkeydown={(e) => e.key === 'Enter' && handleAlbumClick(track.albumId, e)}
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
> >
{track.albumName || "-"} {track.albumName || "-"}
</button> </span>
{:else} {:else}
{track.albumName || "-"} {track.albumName || "-"}
{/if} {/if}
{:else if showArtist} {:else if showArtist}
{#if track.artistItems && track.artistItems.length > 0} {#if track.artistItems && track.artistItems.length > 0}
{#each track.artistItems as artist, idx} {#each track.artistItems as artist, idx}
<button <span
type="button" role="button"
tabindex="0"
onclick={(e) => handleArtistClick(artist.id, e)} onclick={(e) => handleArtistClick(artist.id, e)}
class="text-[var(--color-jellyfin)] hover:underline" onkeydown={(e) => e.key === 'Enter' && handleArtistClick(artist.id, e)}
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
> >
{artist.name} {artist.name}
</button> </span>
{#if idx < track.artistItems.length - 1} {#if idx < track.artistItems.length - 1}
<span>,</span> <span>,</span>
{/if} {/if}
@ -406,13 +416,15 @@
{/if} {/if}
{:else if showAlbum} {:else if showAlbum}
{#if track.albumId} {#if track.albumId}
<button <span
type="button" role="button"
tabindex="0"
onclick={(e) => handleAlbumClick(track.albumId, e)} onclick={(e) => handleAlbumClick(track.albumId, e)}
class="text-[var(--color-jellyfin)] hover:underline" onkeydown={(e) => e.key === 'Enter' && handleAlbumClick(track.albumId, e)}
class="text-[var(--color-jellyfin)] hover:underline cursor-pointer"
> >
{track.albumName || "-"} {track.albumName || "-"}
</button> </span>
{:else} {:else}
{track.albumName || "-"} {track.albumName || "-"}
{/if} {/if}

View File

@ -284,6 +284,7 @@
</button> </button>
<div <div
role="region"
class="px-4 py-3 flex items-center gap-4 touch-pan-y relative" class="px-4 py-3 flex items-center gap-4 touch-pan-y relative"
ontouchstart={handleTouchStart} ontouchstart={handleTouchStart}
ontouchmove={handleTouchMove} ontouchmove={handleTouchMove}