First working POC
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
// Mock Tauri invoke
|
||||
const mockInvoke = vi.fn();
|
||||
vi.mock("@tauri-apps/api/core", () => ({
|
||||
invoke: (...args: unknown[]) => mockInvoke(...args),
|
||||
}));
|
||||
|
||||
// Mock the sessions store
|
||||
const mockSelectSession = vi.fn();
|
||||
vi.mock("./sessions", () => ({
|
||||
sessions: {
|
||||
selectSession: (...args: unknown[]) => mockSelectSession(...args),
|
||||
},
|
||||
selectedSession: {
|
||||
subscribe: vi.fn((callback: (value: null) => void) => {
|
||||
callback(null);
|
||||
return () => {};
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock auth store
|
||||
vi.mock("./auth", () => ({
|
||||
auth: {
|
||||
getRepository: vi.fn(() => ({
|
||||
getPlaybackInfo: vi.fn().mockResolvedValue({ streamUrl: "http://test.com/stream" }),
|
||||
getImageUrl: vi.fn().mockReturnValue("http://test.com/image"),
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("playbackMode store", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("initial state", () => {
|
||||
it("should have idle mode initially", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
const state = get(playbackMode);
|
||||
|
||||
expect(state.mode).toBe("idle");
|
||||
expect(state.remoteSessionId).toBeNull();
|
||||
expect(state.isTransferring).toBe(false);
|
||||
expect(state.transferError).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("setMode", () => {
|
||||
it("should update mode locally", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
playbackMode.setMode("local");
|
||||
let state = get(playbackMode);
|
||||
expect(state.mode).toBe("local");
|
||||
|
||||
playbackMode.setMode("remote", "session-123");
|
||||
state = get(playbackMode);
|
||||
expect(state.mode).toBe("remote");
|
||||
expect(state.remoteSessionId).toBe("session-123");
|
||||
});
|
||||
});
|
||||
|
||||
describe("disconnect", () => {
|
||||
it("should notify Rust backend and update local state when disconnecting", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
// Set up remote mode first
|
||||
playbackMode.setMode("remote", "session-123");
|
||||
let state = get(playbackMode);
|
||||
expect(state.mode).toBe("remote");
|
||||
expect(state.remoteSessionId).toBe("session-123");
|
||||
|
||||
// Mock successful Rust call
|
||||
mockInvoke.mockResolvedValueOnce(undefined);
|
||||
|
||||
// Call disconnect
|
||||
await playbackMode.disconnect();
|
||||
|
||||
// Verify Rust backend was notified with correct mode
|
||||
expect(mockInvoke).toHaveBeenCalledWith("playback_mode_set", { mode: "Idle" });
|
||||
|
||||
// Verify sessions.selectSession was called with null
|
||||
expect(mockSelectSession).toHaveBeenCalledWith(null);
|
||||
|
||||
// Verify local state updated
|
||||
state = get(playbackMode);
|
||||
expect(state.mode).toBe("idle");
|
||||
expect(state.remoteSessionId).toBeNull();
|
||||
expect(state.transferError).toBeNull();
|
||||
});
|
||||
|
||||
it("should do nothing if not in remote mode", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
// Ensure we're in idle mode
|
||||
playbackMode.setMode("idle");
|
||||
const state = get(playbackMode);
|
||||
expect(state.mode).toBe("idle");
|
||||
|
||||
// Call disconnect
|
||||
await playbackMode.disconnect();
|
||||
|
||||
// Verify Rust backend was NOT called
|
||||
expect(mockInvoke).not.toHaveBeenCalled();
|
||||
expect(mockSelectSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should handle errors gracefully", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
// Set up remote mode first
|
||||
playbackMode.setMode("remote", "session-123");
|
||||
|
||||
// Mock failed Rust call
|
||||
const error = new Error("Failed to set mode");
|
||||
mockInvoke.mockRejectedValueOnce(error);
|
||||
|
||||
// Call disconnect and expect it to throw
|
||||
await expect(playbackMode.disconnect()).rejects.toThrow("Failed to set mode");
|
||||
|
||||
// Verify error was stored in state
|
||||
const state = get(playbackMode);
|
||||
expect(state.transferError).toBe("Failed to set mode");
|
||||
});
|
||||
|
||||
it("should clear previous transfer errors on successful disconnect", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
// Set up remote mode with an existing error
|
||||
playbackMode.setMode("remote", "session-123");
|
||||
|
||||
// Manually set an error state (simulating a previous failed operation)
|
||||
// We'll use clearError then verify it's cleared on disconnect
|
||||
mockInvoke.mockResolvedValueOnce(undefined);
|
||||
|
||||
await playbackMode.disconnect();
|
||||
|
||||
const state = get(playbackMode);
|
||||
expect(state.transferError).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("transferToRemote", () => {
|
||||
it("should call Rust backend with session ID", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
mockInvoke.mockResolvedValueOnce(undefined);
|
||||
|
||||
await playbackMode.transferToRemote("session-456");
|
||||
|
||||
expect(mockInvoke).toHaveBeenCalledWith("playback_mode_transfer_to_remote", {
|
||||
sessionId: "session-456",
|
||||
});
|
||||
});
|
||||
|
||||
it("should update local state on success", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
mockInvoke.mockResolvedValueOnce(undefined);
|
||||
|
||||
await playbackMode.transferToRemote("session-456");
|
||||
|
||||
const state = get(playbackMode);
|
||||
expect(state.mode).toBe("remote");
|
||||
expect(state.remoteSessionId).toBe("session-456");
|
||||
expect(state.isTransferring).toBe(false);
|
||||
});
|
||||
|
||||
it("should set isTransferring during transfer", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
// Create a promise that we can control
|
||||
let resolveTransfer: () => void;
|
||||
const transferPromise = new Promise<void>((resolve) => {
|
||||
resolveTransfer = resolve;
|
||||
});
|
||||
mockInvoke.mockReturnValueOnce(transferPromise);
|
||||
|
||||
// Start the transfer (don't await)
|
||||
const transferPromiseResult = playbackMode.transferToRemote("session-789");
|
||||
|
||||
// Check that isTransferring is true during the transfer
|
||||
let state = get(playbackMode);
|
||||
expect(state.isTransferring).toBe(true);
|
||||
|
||||
// Resolve the transfer
|
||||
resolveTransfer!();
|
||||
await transferPromiseResult;
|
||||
|
||||
// Check that isTransferring is false after
|
||||
state = get(playbackMode);
|
||||
expect(state.isTransferring).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("clearError", () => {
|
||||
it("should clear transfer error", async () => {
|
||||
const { playbackMode } = await import("./playbackMode");
|
||||
|
||||
// Set up remote mode and simulate a failed transfer to set an error
|
||||
playbackMode.setMode("remote", "session-123");
|
||||
mockInvoke.mockRejectedValueOnce(new Error("Test error"));
|
||||
|
||||
try {
|
||||
await playbackMode.disconnect();
|
||||
} catch {
|
||||
// Expected to throw
|
||||
}
|
||||
|
||||
let state = get(playbackMode);
|
||||
expect(state.transferError).toBe("Test error");
|
||||
|
||||
// Clear the error
|
||||
playbackMode.clearError();
|
||||
|
||||
state = get(playbackMode);
|
||||
expect(state.transferError).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("derived stores", () => {
|
||||
it("isRemoteMode should be true when mode is remote", async () => {
|
||||
const { playbackMode, isRemoteMode } = await import("./playbackMode");
|
||||
|
||||
playbackMode.setMode("remote", "session-123");
|
||||
|
||||
const isRemote = get(isRemoteMode);
|
||||
expect(isRemote).toBe(true);
|
||||
});
|
||||
|
||||
it("isLocalMode should be true when mode is local", async () => {
|
||||
const { playbackMode, isLocalMode } = await import("./playbackMode");
|
||||
|
||||
playbackMode.setMode("local");
|
||||
|
||||
const isLocal = get(isLocalMode);
|
||||
expect(isLocal).toBe(true);
|
||||
});
|
||||
|
||||
it("isIdleMode should be true when mode is idle", async () => {
|
||||
const { playbackMode, isIdleMode } = await import("./playbackMode");
|
||||
|
||||
playbackMode.setMode("idle");
|
||||
|
||||
const isIdle = get(isIdleMode);
|
||||
expect(isIdle).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user