mirror of
https://github.com/earendil-works/pi.git
synced 2026-10-20 05:59:17 +08:00
Reuse the stable renderer, viewport layout, theme detection, and terminal appearance updates. Keep successful operation status derived from replicated lane state.
142 lines
5.3 KiB
TypeScript
142 lines
5.3 KiB
TypeScript
import type { TUI } from "@earendil-works/pi-tui";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { SettingsManager } from "../src/core/settings-manager.ts";
|
|
import { initTheme, type TerminalTheme, theme } from "../src/modes/interactive/theme/theme.ts";
|
|
import { InteractiveThemeController } from "../src/modes/interactive/theme/theme-controller.ts";
|
|
|
|
function createUi() {
|
|
const queryTerminalBackgroundColor = vi.fn();
|
|
const queryTerminalColorScheme = vi.fn();
|
|
const setTerminalColorSchemeNotifications = vi.fn();
|
|
let terminalColorSchemeListener: ((terminalTheme: TerminalTheme) => void) | undefined;
|
|
const unsubscribeTerminalColorScheme = vi.fn();
|
|
const ui = {
|
|
invalidate: vi.fn(),
|
|
requestRender: vi.fn(),
|
|
setTerminalColorSchemeNotifications,
|
|
onTerminalColorSchemeChange: vi.fn((listener: (terminalTheme: TerminalTheme) => void) => {
|
|
terminalColorSchemeListener = listener;
|
|
return unsubscribeTerminalColorScheme;
|
|
}),
|
|
queryTerminalBackgroundColor,
|
|
queryTerminalColorScheme,
|
|
} as unknown as TUI;
|
|
return {
|
|
ui,
|
|
queryTerminalBackgroundColor,
|
|
queryTerminalColorScheme,
|
|
setTerminalColorSchemeNotifications,
|
|
unsubscribeTerminalColorScheme,
|
|
emitTerminalColorScheme: (terminalTheme: TerminalTheme) => terminalColorSchemeListener?.(terminalTheme),
|
|
};
|
|
}
|
|
|
|
function createController(ui: TUI, getSettingsManager: () => SettingsManager, initialThemeSetting?: string) {
|
|
return new InteractiveThemeController(ui, {
|
|
getSettingsManager,
|
|
showError: vi.fn(),
|
|
onChanged: vi.fn(),
|
|
initialThemeSetting,
|
|
});
|
|
}
|
|
|
|
afterEach(() => {
|
|
initTheme("dark");
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
describe("InteractiveThemeController", () => {
|
|
it("uses the initial theme without persisting it", async () => {
|
|
const { ui, queryTerminalBackgroundColor } = createUi();
|
|
const manager = SettingsManager.inMemory({ theme: "dark" });
|
|
const setTheme = vi.spyOn(manager, "setTheme");
|
|
const flush = vi.spyOn(manager, "flush");
|
|
const controller = createController(ui, () => manager, "light");
|
|
|
|
expect(theme.name).toBe("light");
|
|
expect(controller.getThemeSelection()).toBe("light");
|
|
await controller.applyFromSettings();
|
|
|
|
expect(queryTerminalBackgroundColor).not.toHaveBeenCalled();
|
|
expect(setTheme).not.toHaveBeenCalled();
|
|
expect(flush).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("resolves a theme pair and follows terminal appearance changes", async () => {
|
|
vi.stubEnv("COLORFGBG", "15;0");
|
|
const { ui, queryTerminalColorScheme, setTerminalColorSchemeNotifications, emitTerminalColorScheme } = createUi();
|
|
queryTerminalColorScheme.mockResolvedValue("light");
|
|
const manager = SettingsManager.inMemory({ theme: "dark/light" });
|
|
const controller = createController(ui, () => manager, "light/dark");
|
|
|
|
expect(theme.name).toBe("dark");
|
|
await controller.applyFromSettings();
|
|
expect(theme.name).toBe("light");
|
|
expect(setTerminalColorSchemeNotifications).toHaveBeenCalledWith(true);
|
|
|
|
emitTerminalColorScheme("dark");
|
|
expect(theme.name).toBe("dark");
|
|
});
|
|
|
|
it("disables terminal appearance updates when disposed", async () => {
|
|
const { ui, queryTerminalColorScheme, setTerminalColorSchemeNotifications, unsubscribeTerminalColorScheme } =
|
|
createUi();
|
|
queryTerminalColorScheme.mockResolvedValue("light");
|
|
const manager = SettingsManager.inMemory({ theme: "light/dark" });
|
|
const controller = createController(ui, () => manager);
|
|
await controller.applyFromSettings();
|
|
|
|
controller.dispose();
|
|
|
|
expect(setTerminalColorSchemeNotifications).toHaveBeenLastCalledWith(false);
|
|
expect(unsubscribeTerminalColorScheme).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("detects the current terminal appearance when selecting a theme pair", async () => {
|
|
vi.stubEnv("COLORFGBG", "");
|
|
const { ui, queryTerminalColorScheme } = createUi();
|
|
queryTerminalColorScheme.mockResolvedValue("light");
|
|
const manager = SettingsManager.inMemory({ theme: "dark" });
|
|
const controller = createController(ui, () => manager);
|
|
|
|
expect(theme.name).toBe("dark");
|
|
await controller.setThemeSetting("light/dark");
|
|
expect(theme.name).toBe("light");
|
|
expect(queryTerminalColorScheme).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("lets an explicit selection replace the initial theme", async () => {
|
|
const { ui } = createUi();
|
|
const firstManager = SettingsManager.inMemory({ theme: "dark" });
|
|
const secondManager = SettingsManager.inMemory({ theme: "light" });
|
|
let manager = firstManager;
|
|
const controller = createController(ui, () => manager, "light");
|
|
await controller.applyFromSettings();
|
|
|
|
expect(controller.setThemeName("dark")).toEqual({ success: true });
|
|
manager = secondManager;
|
|
await controller.applyFromSettings();
|
|
|
|
expect(controller.getThemeSelection()).toBe("dark");
|
|
expect(theme.name).toBe("dark");
|
|
});
|
|
|
|
it("reloads theme settings when no initial theme was supplied", async () => {
|
|
const { ui } = createUi();
|
|
const firstManager = SettingsManager.inMemory({ theme: "dark" });
|
|
const secondManager = SettingsManager.inMemory({ theme: "light" });
|
|
let manager = firstManager;
|
|
const controller = createController(ui, () => manager);
|
|
await controller.applyFromSettings();
|
|
|
|
firstManager.applyOverrides({ theme: "light" });
|
|
await controller.applyFromSettings();
|
|
expect(theme.name).toBe("light");
|
|
|
|
secondManager.applyOverrides({ theme: "dark" });
|
|
manager = secondManager;
|
|
await controller.applyFromSettings();
|
|
expect(theme.name).toBe("dark");
|
|
});
|
|
});
|