Files
mission-control/tests/stores/focus-store.test.ts
teernisse 7404acdfb4 feat: add Tauri event hook and lore.db file watcher
Adds real-time updates when lore syncs new GitLab data by watching
the lore.db file for changes.

React hook (src/hooks/useTauriEvents.ts):
- useTauriEvent(): Subscribe to a single Tauri event with auto-cleanup
- useTauriEvents(): Subscribe to multiple events with a handler map
- Typed payloads for each event type:
  - global-shortcut-triggered: toggle-window | quick-capture
  - lore-data-changed: void (refresh trigger)
  - sync-status: started | completed | failed
  - error-notification: code + message

Rust watcher (src-tauri/src/watcher.rs):
- Watches lore's data directory for lore.db modifications
- Uses notify crate with 2-second poll interval
- Emits "lore-data-changed" event to frontend on file change
- Handles atomic writes by watching parent directory
- Gracefully handles missing lore.db (logs warning, skips watcher)

Test coverage:
- Hook subscription and cleanup behavior
- Focus store test fix: clear localStorage before each test

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-26 09:55:30 -05:00

225 lines
6.7 KiB
TypeScript

import { describe, it, expect, beforeEach } from "vitest";
import { useFocusStore } from "@/stores/focus-store";
import { makeFocusItem } from "../helpers/fixtures";
describe("useFocusStore", () => {
beforeEach(() => {
// Clear persisted state before each test
localStorage.clear();
// Reset store between tests
useFocusStore.setState({
current: null,
queue: [],
isLoading: false,
error: null,
});
});
describe("setItems", () => {
it("sets first item as current and rest as queue", () => {
const items = [
makeFocusItem({ id: "a", title: "First" }),
makeFocusItem({ id: "b", title: "Second" }),
makeFocusItem({ id: "c", title: "Third" }),
];
useFocusStore.getState().setItems(items);
const state = useFocusStore.getState();
expect(state.current?.id).toBe("a");
expect(state.queue).toHaveLength(2);
expect(state.queue[0].id).toBe("b");
expect(state.queue[1].id).toBe("c");
});
it("sets current to null when empty", () => {
useFocusStore.getState().setItems([]);
const state = useFocusStore.getState();
expect(state.current).toBeNull();
expect(state.queue).toHaveLength(0);
});
it("clears loading and error on setItems", () => {
useFocusStore.setState({ isLoading: true, error: "old error" });
useFocusStore.getState().setItems([makeFocusItem()]);
const state = useFocusStore.getState();
expect(state.isLoading).toBe(false);
expect(state.error).toBeNull();
});
});
describe("act", () => {
it("advances to next item in queue", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "a" }),
makeFocusItem({ id: "b" }),
makeFocusItem({ id: "c" }),
]);
const next = useFocusStore.getState().act("start");
expect(next?.id).toBe("b");
expect(useFocusStore.getState().current?.id).toBe("b");
expect(useFocusStore.getState().queue).toHaveLength(1);
});
it("returns null when queue is empty", () => {
useFocusStore.getState().setItems([makeFocusItem({ id: "only" })]);
const next = useFocusStore.getState().act("skip");
expect(next).toBeNull();
expect(useFocusStore.getState().current).toBeNull();
expect(useFocusStore.getState().queue).toHaveLength(0);
});
it("works with defer_1h action", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "a" }),
makeFocusItem({ id: "b" }),
]);
useFocusStore.getState().act("defer_1h", "in a meeting");
expect(useFocusStore.getState().current?.id).toBe("b");
});
it("works with defer_tomorrow action", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "a" }),
makeFocusItem({ id: "b" }),
]);
useFocusStore.getState().act("defer_tomorrow");
expect(useFocusStore.getState().current?.id).toBe("b");
});
});
describe("setFocus", () => {
it("promotes a queue item to current", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "a", title: "First" }),
makeFocusItem({ id: "b", title: "Second" }),
makeFocusItem({ id: "c", title: "Third" }),
]);
useFocusStore.getState().setFocus("c");
const state = useFocusStore.getState();
expect(state.current?.id).toBe("c");
// Previous current and other queue items are in queue
expect(state.queue.map((i) => i.id)).toEqual(
expect.arrayContaining(["a", "b"])
);
expect(state.queue).toHaveLength(2);
});
it("does nothing for unknown item ID", () => {
useFocusStore.getState().setItems([makeFocusItem({ id: "a" })]);
useFocusStore.getState().setFocus("nonexistent");
expect(useFocusStore.getState().current?.id).toBe("a");
});
});
describe("reorderQueue", () => {
it("moves an item from one position to another", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "focus" }),
makeFocusItem({ id: "a" }),
makeFocusItem({ id: "b" }),
makeFocusItem({ id: "c" }),
]);
useFocusStore.getState().reorderQueue(2, 0);
const ids = useFocusStore.getState().queue.map((i) => i.id);
expect(ids).toEqual(["c", "a", "b"]);
});
it("does nothing for same from/to index", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "focus" }),
makeFocusItem({ id: "a" }),
makeFocusItem({ id: "b" }),
]);
useFocusStore.getState().reorderQueue(0, 0);
const ids = useFocusStore.getState().queue.map((i) => i.id);
expect(ids).toEqual(["a", "b"]);
});
it("does nothing for out-of-bounds indices", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "focus" }),
makeFocusItem({ id: "a" }),
]);
useFocusStore.getState().reorderQueue(-1, 0);
useFocusStore.getState().reorderQueue(0, 5);
expect(useFocusStore.getState().queue.map((i) => i.id)).toEqual(["a"]);
});
it("does not affect current focus", () => {
useFocusStore.getState().setItems([
makeFocusItem({ id: "focus" }),
makeFocusItem({ id: "a" }),
makeFocusItem({ id: "b" }),
]);
useFocusStore.getState().reorderQueue(1, 0);
expect(useFocusStore.getState().current?.id).toBe("focus");
});
});
describe("setLoading / setError", () => {
it("sets loading state", () => {
useFocusStore.getState().setLoading(true);
expect(useFocusStore.getState().isLoading).toBe(true);
});
it("sets error state", () => {
useFocusStore.getState().setError("something broke");
expect(useFocusStore.getState().error).toBe("something broke");
});
});
describe("persistence", () => {
it("persists current and queue to localStorage", () => {
const items = [
makeFocusItem({ id: "a", title: "First" }),
makeFocusItem({ id: "b", title: "Second" }),
];
useFocusStore.getState().setItems(items);
const stored = localStorage.getItem("mc-focus-store");
expect(stored).not.toBeNull();
const parsed = JSON.parse(stored!);
expect(parsed.state.current.id).toBe("a");
expect(parsed.state.queue).toHaveLength(1);
expect(parsed.state.queue[0].id).toBe("b");
});
it("does not persist isLoading or error", () => {
useFocusStore.setState({ isLoading: true, error: "oops" });
const stored = localStorage.getItem("mc-focus-store");
expect(stored).not.toBeNull();
const parsed = JSON.parse(stored!);
expect(parsed.state).not.toHaveProperty("isLoading");
expect(parsed.state).not.toHaveProperty("error");
});
});
});