generated from nhcarrigan/template
feat: multiple UI improvements, font settings, and memory file display names #175
@@ -1,4 +1,5 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { get } from "svelte/store";
|
||||
import {
|
||||
configStore,
|
||||
maskPaths,
|
||||
@@ -7,6 +8,11 @@ import {
|
||||
applyTheme,
|
||||
applyCustomThemeColors,
|
||||
clearCustomThemeColors,
|
||||
isDarkTheme,
|
||||
isStreamerMode,
|
||||
isCompactMode,
|
||||
shouldHidePaths,
|
||||
showThinkingBlocks,
|
||||
MIN_FONT_SIZE,
|
||||
MAX_FONT_SIZE,
|
||||
DEFAULT_FONT_SIZE,
|
||||
@@ -843,4 +849,288 @@ describe("config store", () => {
|
||||
expect(lastSaved.font_size).toBe(16);
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadConfig error path", () => {
|
||||
it("resets to default config and logs error when loadConfig fails", async () => {
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
await configStore.updateConfig({ theme: "light" });
|
||||
|
||||
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
vi.mocked(invoke).mockRejectedValue(new Error("Backend unavailable"));
|
||||
|
||||
await configStore.loadConfig();
|
||||
|
||||
expect(configStore.getConfig().theme).toBe("dark");
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to load config:", expect.any(Error));
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("configStore sidebar methods", () => {
|
||||
it("openSidebar sets isSidebarOpen to true", () => {
|
||||
configStore.closeSidebar();
|
||||
configStore.openSidebar();
|
||||
expect(get(configStore.isSidebarOpen)).toBe(true);
|
||||
});
|
||||
|
||||
it("closeSidebar sets isSidebarOpen to false", () => {
|
||||
configStore.openSidebar();
|
||||
configStore.closeSidebar();
|
||||
expect(get(configStore.isSidebarOpen)).toBe(false);
|
||||
});
|
||||
|
||||
it("toggleSidebar switches from false to true", () => {
|
||||
configStore.closeSidebar();
|
||||
configStore.toggleSidebar();
|
||||
expect(get(configStore.isSidebarOpen)).toBe(true);
|
||||
});
|
||||
|
||||
it("toggleSidebar switches from true to false", () => {
|
||||
configStore.openSidebar();
|
||||
configStore.toggleSidebar();
|
||||
expect(get(configStore.isSidebarOpen)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("configStore setTheme method", () => {
|
||||
beforeEach(async () => {
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("setTheme updates the theme via invoke", async () => {
|
||||
await configStore.setTheme("light");
|
||||
expect(configStore.getConfig().theme).toBe("light");
|
||||
});
|
||||
|
||||
it("setTheme with custom colors updates custom_theme_colors", async () => {
|
||||
const colors: CustomThemeColors = {
|
||||
bg_primary: "#001122",
|
||||
bg_secondary: null,
|
||||
bg_terminal: null,
|
||||
accent_primary: null,
|
||||
accent_secondary: null,
|
||||
text_primary: null,
|
||||
text_secondary: null,
|
||||
border_color: null,
|
||||
};
|
||||
|
||||
await configStore.setTheme("custom", colors);
|
||||
|
||||
expect(configStore.getConfig().theme).toBe("custom");
|
||||
expect(configStore.getConfig().custom_theme_colors.bg_primary).toBe("#001122");
|
||||
});
|
||||
});
|
||||
|
||||
describe("configStore setCustomThemeColors with custom theme active", () => {
|
||||
beforeEach(async () => {
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
await configStore.setTheme("custom");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
clearCustomThemeColors();
|
||||
});
|
||||
|
||||
it("applies custom colors to DOM when current theme is custom", async () => {
|
||||
const colors: CustomThemeColors = {
|
||||
bg_primary: "#aabbcc",
|
||||
bg_secondary: null,
|
||||
bg_terminal: null,
|
||||
accent_primary: null,
|
||||
accent_secondary: null,
|
||||
text_primary: null,
|
||||
text_secondary: null,
|
||||
border_color: null,
|
||||
};
|
||||
|
||||
await configStore.setCustomThemeColors(colors);
|
||||
|
||||
expect(document.documentElement.style.getPropertyValue("--bg-primary")).toBe("#aabbcc");
|
||||
});
|
||||
});
|
||||
|
||||
describe("configStore font size methods", () => {
|
||||
beforeEach(async () => {
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
await configStore.updateConfig({ font_size: DEFAULT_FONT_SIZE });
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("setFontSize updates to the given value", async () => {
|
||||
await configStore.setFontSize(18);
|
||||
expect(configStore.getConfig().font_size).toBe(18);
|
||||
});
|
||||
|
||||
it("setFontSize clamps to minimum", async () => {
|
||||
await configStore.setFontSize(1);
|
||||
expect(configStore.getConfig().font_size).toBe(MIN_FONT_SIZE);
|
||||
});
|
||||
|
||||
it("setFontSize clamps to maximum", async () => {
|
||||
await configStore.setFontSize(99);
|
||||
expect(configStore.getConfig().font_size).toBe(MAX_FONT_SIZE);
|
||||
});
|
||||
|
||||
it("increaseFontSize increases font size by 2", async () => {
|
||||
await configStore.increaseFontSize();
|
||||
expect(configStore.getConfig().font_size).toBe(DEFAULT_FONT_SIZE + 2);
|
||||
});
|
||||
|
||||
it("increaseFontSize does not exceed maximum", async () => {
|
||||
await configStore.setFontSize(MAX_FONT_SIZE);
|
||||
await configStore.increaseFontSize();
|
||||
expect(configStore.getConfig().font_size).toBe(MAX_FONT_SIZE);
|
||||
});
|
||||
|
||||
it("decreaseFontSize decreases font size by 2", async () => {
|
||||
await configStore.decreaseFontSize();
|
||||
expect(configStore.getConfig().font_size).toBe(DEFAULT_FONT_SIZE - 2);
|
||||
});
|
||||
|
||||
it("decreaseFontSize does not go below minimum", async () => {
|
||||
await configStore.setFontSize(MIN_FONT_SIZE);
|
||||
await configStore.decreaseFontSize();
|
||||
expect(configStore.getConfig().font_size).toBe(MIN_FONT_SIZE);
|
||||
});
|
||||
|
||||
it("resetFontSize restores the default font size", async () => {
|
||||
await configStore.setFontSize(20);
|
||||
await configStore.resetFontSize();
|
||||
expect(configStore.getConfig().font_size).toBe(DEFAULT_FONT_SIZE);
|
||||
});
|
||||
});
|
||||
|
||||
describe("configStore removeAutoGrantedTool", () => {
|
||||
beforeEach(async () => {
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
await configStore.updateConfig({ auto_granted_tools: [] });
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("removes an existing tool", async () => {
|
||||
await configStore.addAutoGrantedTool("Bash");
|
||||
await configStore.removeAutoGrantedTool("Bash");
|
||||
expect(configStore.getConfig().auto_granted_tools).not.toContain("Bash");
|
||||
});
|
||||
|
||||
it("is a no-op when the tool is not in the list", async () => {
|
||||
await configStore.removeAutoGrantedTool("NonExistentTool");
|
||||
expect(configStore.getConfig().auto_granted_tools).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("configStore toggle methods", () => {
|
||||
beforeEach(async () => {
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
await configStore.updateConfig({ streamer_mode: false, compact_mode: false });
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("toggleStreamerMode flips streamer_mode from false to true", async () => {
|
||||
await configStore.toggleStreamerMode();
|
||||
expect(configStore.getConfig().streamer_mode).toBe(true);
|
||||
});
|
||||
|
||||
it("toggleStreamerMode flips streamer_mode from true to false", async () => {
|
||||
await configStore.updateConfig({ streamer_mode: true });
|
||||
await configStore.toggleStreamerMode();
|
||||
expect(configStore.getConfig().streamer_mode).toBe(false);
|
||||
});
|
||||
|
||||
it("toggleCompactMode flips compact_mode from false to true", async () => {
|
||||
await configStore.toggleCompactMode();
|
||||
expect(configStore.getConfig().compact_mode).toBe(true);
|
||||
});
|
||||
|
||||
it("toggleCompactMode flips compact_mode from true to false", async () => {
|
||||
await configStore.updateConfig({ compact_mode: true });
|
||||
await configStore.toggleCompactMode();
|
||||
expect(configStore.getConfig().compact_mode).toBe(false);
|
||||
});
|
||||
|
||||
it("setCompactMode enables compact mode", async () => {
|
||||
await configStore.setCompactMode(true);
|
||||
expect(configStore.getConfig().compact_mode).toBe(true);
|
||||
});
|
||||
|
||||
it("setCompactMode disables compact mode", async () => {
|
||||
await configStore.updateConfig({ compact_mode: true });
|
||||
await configStore.setCompactMode(false);
|
||||
expect(configStore.getConfig().compact_mode).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("derived stores (live subscriptions)", () => {
|
||||
beforeEach(async () => {
|
||||
vi.mocked(invoke).mockResolvedValue(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("isDarkTheme is true when theme is dark", async () => {
|
||||
await configStore.updateConfig({ theme: "dark" });
|
||||
expect(get(isDarkTheme)).toBe(true);
|
||||
});
|
||||
|
||||
it("isDarkTheme is false when theme is not dark", async () => {
|
||||
await configStore.updateConfig({ theme: "light" });
|
||||
expect(get(isDarkTheme)).toBe(false);
|
||||
});
|
||||
|
||||
it("isStreamerMode reflects streamer_mode config", async () => {
|
||||
await configStore.updateConfig({ streamer_mode: true });
|
||||
expect(get(isStreamerMode)).toBe(true);
|
||||
await configStore.updateConfig({ streamer_mode: false });
|
||||
expect(get(isStreamerMode)).toBe(false);
|
||||
});
|
||||
|
||||
it("isCompactMode reflects compact_mode config", async () => {
|
||||
await configStore.updateConfig({ compact_mode: true });
|
||||
expect(get(isCompactMode)).toBe(true);
|
||||
await configStore.updateConfig({ compact_mode: false });
|
||||
expect(get(isCompactMode)).toBe(false);
|
||||
});
|
||||
|
||||
it("shouldHidePaths is true when both streamer flags are enabled", async () => {
|
||||
await configStore.updateConfig({ streamer_mode: true, streamer_hide_paths: true });
|
||||
expect(get(shouldHidePaths)).toBe(true);
|
||||
});
|
||||
|
||||
it("shouldHidePaths is false when streamer_mode is disabled", async () => {
|
||||
await configStore.updateConfig({ streamer_mode: false, streamer_hide_paths: true });
|
||||
expect(get(shouldHidePaths)).toBe(false);
|
||||
});
|
||||
|
||||
it("showThinkingBlocks is true when show_thinking_blocks is enabled", async () => {
|
||||
await configStore.updateConfig({ show_thinking_blocks: true });
|
||||
expect(get(showThinkingBlocks)).toBe(true);
|
||||
});
|
||||
|
||||
it("showThinkingBlocks is false when show_thinking_blocks is disabled", async () => {
|
||||
await configStore.updateConfig({ show_thinking_blocks: false });
|
||||
expect(get(showThinkingBlocks)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user