🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m57s
Traceability Validation / Check Requirement Traces (push) Successful in 22s
Build & Release / Run Tests (push) Successful in 5m13s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m30s
Build & Release / Build Linux (push) Successful in 17m52s
Build & Release / Build Android (push) Failing after 58s
Build & Release / Create Release (push) Has been skipped
Navigation: - Split conflated "back" into navigateUp (deterministic route parent) and a history-safe navigateBack that tracks in-app depth via afterNavigate instead of history.length. Fixes the resume-from-background trap where a stale WebView stack left the header arrow stuck on the current page. - /library self-corrects for music/tv/movies (which have dedicated landing pages): a leftover currentLibrary no longer forces the inline content-list view, so "up"/back shows the libraries overview. Live TV / channels / other types still render inline. Startup (unblock first paint): - auth.initialize() no longer awaits security-status, player-config, or session verification before flipping isInitialized. These run fire-and-forget after the session is restored, so the library overview paints without waiting on several serial IPC round-trips. Versioning / CI: - tauri.conf.json + package.json aligned to 0.0.15 (the tag series had drifted to 0.1.0, whose formula-derived versionCode 1000 outran the v0.0.x tags). - Release workflow now pins a monotonic Android versionCode (1000 + major*10000 + minor*100 + patch) so tagged builds never downgrade below prior installs and always increase in semver order. Tests: navigation (4), auth (29), playbackMode (23) green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
3.0 KiB
TypeScript
96 lines
3.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
const goto = vi.fn();
|
|
// Capture the afterNavigate callback so tests can simulate navigations and thus
|
|
// drive the in-app depth counter that canGoBack/navigateBack rely on.
|
|
let afterNavigateCb: ((nav: { from: unknown; to: unknown; delta?: number }) => void) | null =
|
|
null;
|
|
vi.mock("$app/navigation", () => ({
|
|
goto: (...args: unknown[]) => goto(...args),
|
|
afterNavigate: (cb: (nav: any) => void) => {
|
|
afterNavigateCb = cb;
|
|
},
|
|
}));
|
|
|
|
import {
|
|
navigateUp,
|
|
navigateBack,
|
|
canGoBack,
|
|
registerNavigationTracking,
|
|
__resetNavigationDepthForTest,
|
|
} from "./navigation";
|
|
|
|
/** Simulate a SvelteKit navigation to move the depth counter. */
|
|
function nav(opts: { from?: boolean; delta?: number }) {
|
|
afterNavigateCb?.({
|
|
from: opts.from === false ? null : {},
|
|
to: {},
|
|
delta: opts.delta,
|
|
});
|
|
}
|
|
|
|
describe("navigation", () => {
|
|
beforeEach(() => {
|
|
goto.mockClear();
|
|
// registerNavigationTracking is idempotent; the first call in the suite wins
|
|
// and wires afterNavigateCb. Ensure it is registered, then reset depth so
|
|
// each case starts from the entry page (module state persists otherwise).
|
|
registerNavigationTracking();
|
|
__resetNavigationDepthForTest();
|
|
});
|
|
|
|
describe("navigateUp", () => {
|
|
it("always goes to the given parent path, never touching history", () => {
|
|
const back = vi.fn();
|
|
vi.spyOn(history, "back").mockImplementation(back);
|
|
|
|
navigateUp("/library/music");
|
|
|
|
expect(goto).toHaveBeenCalledWith("/library/music");
|
|
expect(back).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("navigateBack / canGoBack", () => {
|
|
it("falls back to the path when there is no in-app history yet", () => {
|
|
// Fresh session: only the initial load happened (from == null), so depth
|
|
// stays at 0 and there is nothing to pop.
|
|
nav({ from: false });
|
|
expect(canGoBack()).toBe(false);
|
|
|
|
const back = vi.fn();
|
|
vi.spyOn(history, "back").mockImplementation(back);
|
|
|
|
navigateBack("/library");
|
|
|
|
expect(goto).toHaveBeenCalledWith("/library");
|
|
expect(back).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("pops history after a real in-app forward navigation", () => {
|
|
nav({ from: false }); // initial load
|
|
nav({}); // navigated deeper within the app
|
|
expect(canGoBack()).toBe(true);
|
|
|
|
const back = vi.fn();
|
|
vi.spyOn(history, "back").mockImplementation(back);
|
|
|
|
navigateBack("/library");
|
|
|
|
expect(back).toHaveBeenCalledOnce();
|
|
expect(goto).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not go below zero when the user backs out to the entry page", () => {
|
|
nav({ from: false }); // load
|
|
nav({}); // forward → depth 1
|
|
nav({ delta: -1 }); // back → depth 0
|
|
nav({ delta: -1 }); // extra back (e.g. stale delta) must not underflow
|
|
expect(canGoBack()).toBe(false);
|
|
|
|
navigateBack("/library/music");
|
|
expect(goto).toHaveBeenCalledWith("/library/music");
|
|
});
|
|
});
|
|
});
|