132 lines
4.7 KiB
TypeScript
132 lines
4.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
|
|
import * as themeModule from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
import * as nativesModule from "@oh-my-pi/pi-natives";
|
|
import { MacOSAppearance } from "@oh-my-pi/pi-natives";
|
|
|
|
const originalPlatform = process.platform;
|
|
const originalColorfgbg = Bun.env.COLORFGBG;
|
|
const originalZellij = Bun.env.ZELLIJ;
|
|
|
|
type ThemeTestGlobals = {
|
|
platform?: NodeJS.Platform;
|
|
colorfgbg?: string;
|
|
zellij?: string;
|
|
};
|
|
|
|
const withThemeTestGlobals = (globals: ThemeTestGlobals = {}) => {
|
|
Object.defineProperty(process, "platform", {
|
|
value: globals.platform ?? "darwin",
|
|
configurable: true,
|
|
writable: true,
|
|
});
|
|
|
|
if (globals.colorfgbg === undefined) delete Bun.env.COLORFGBG;
|
|
else Bun.env.COLORFGBG = globals.colorfgbg;
|
|
|
|
if (globals.zellij === undefined) delete Bun.env.ZELLIJ;
|
|
else Bun.env.ZELLIJ = globals.zellij;
|
|
|
|
return {
|
|
[Symbol.dispose]() {
|
|
themeModule.stopThemeWatcher();
|
|
Object.defineProperty(process, "platform", {
|
|
value: originalPlatform,
|
|
configurable: true,
|
|
writable: true,
|
|
});
|
|
if (originalColorfgbg === undefined) delete Bun.env.COLORFGBG;
|
|
else Bun.env.COLORFGBG = originalColorfgbg;
|
|
if (originalZellij === undefined) delete Bun.env.ZELLIJ;
|
|
else Bun.env.ZELLIJ = originalZellij;
|
|
vi.restoreAllMocks();
|
|
},
|
|
};
|
|
};
|
|
|
|
describe("theme auto-detection", () => {
|
|
beforeEach(async () => {
|
|
themeModule.stopThemeWatcher();
|
|
const darkTheme = await themeModule.getThemeByName("dark");
|
|
if (!darkTheme) {
|
|
throw new Error("Failed to load dark theme for tests");
|
|
}
|
|
themeModule.setThemeInstance(darkTheme);
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
afterEach(() => {
|
|
themeModule.stopThemeWatcher();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("prefers COLORFGBG before macOS fallback inside Zellij", async () => {
|
|
using _globals = withThemeTestGlobals({ zellij: "1", colorfgbg: "15;0" });
|
|
const detectSpy = vi.spyOn(nativesModule, "detectMacOSAppearance").mockReturnValue(MacOSAppearance.Light);
|
|
|
|
await themeModule.initTheme(false, undefined, undefined, "dark", "light");
|
|
|
|
expect(themeModule.getCurrentThemeName()).toBe("dark");
|
|
expect(detectSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("keeps honoring terminal-reported appearance outside fallback mode", async () => {
|
|
using _globals = withThemeTestGlobals();
|
|
const detectSpy = vi.spyOn(nativesModule, "detectMacOSAppearance").mockReturnValue(MacOSAppearance.Light);
|
|
const observerSpy = vi.spyOn(nativesModule.MacAppearanceObserver, "start");
|
|
|
|
themeModule.onTerminalAppearanceChange("dark");
|
|
await themeModule.initTheme(true, undefined, undefined, "dark", "light");
|
|
|
|
expect(themeModule.getCurrentThemeName()).toBe("dark");
|
|
expect(detectSpy).not.toHaveBeenCalled();
|
|
expect(observerSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("updates auto theme from the native fallback observer in Zellij", async () => {
|
|
using _globals = withThemeTestGlobals({ zellij: "1" });
|
|
const stop = vi.fn();
|
|
let onAppearanceChange: ((appearance: "dark" | "light") => void) | undefined;
|
|
vi.spyOn(nativesModule, "detectMacOSAppearance").mockReturnValue(MacOSAppearance.Light);
|
|
const observerSpy = vi.spyOn(nativesModule.MacAppearanceObserver, "start").mockImplementation(((
|
|
callback: (err: null | Error, appearance: "dark" | "light") => void,
|
|
) => {
|
|
onAppearanceChange = (appearance: "dark" | "light") => callback(null, appearance);
|
|
return { stop };
|
|
}) as any);
|
|
|
|
await themeModule.initTheme(true, undefined, undefined, "dark", "light");
|
|
|
|
expect(observerSpy).toHaveBeenCalledTimes(1);
|
|
expect(themeModule.getCurrentThemeName()).toBe("light");
|
|
expect(onAppearanceChange).toBeDefined();
|
|
|
|
onAppearanceChange!("dark");
|
|
await Bun.sleep(0);
|
|
|
|
expect(themeModule.getCurrentThemeName()).toBe("dark");
|
|
themeModule.stopThemeWatcher();
|
|
expect(stop).toHaveBeenCalledTimes(1);
|
|
});
|
|
it("Zellij fallback stays macOS-only (Linux + Zellij = honor terminal)", async () => {
|
|
using _globals = withThemeTestGlobals({ platform: "linux", zellij: "1" });
|
|
const detectSpy = vi.spyOn(nativesModule, "detectMacOSAppearance").mockReturnValue(MacOSAppearance.Light);
|
|
|
|
themeModule.onTerminalAppearanceChange("dark");
|
|
await themeModule.initTheme(false, undefined, undefined, "dark", "light");
|
|
|
|
expect(themeModule.getCurrentThemeName()).toBe("dark");
|
|
expect(detectSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("terminal-reported appearance wins over conflicting COLORFGBG", async () => {
|
|
using _globals = withThemeTestGlobals({ colorfgbg: "15;0" });
|
|
const detectSpy = vi.spyOn(nativesModule, "detectMacOSAppearance").mockReturnValue(MacOSAppearance.Light);
|
|
|
|
themeModule.onTerminalAppearanceChange("light");
|
|
await themeModule.initTheme(false, undefined, undefined, "dark", "light");
|
|
|
|
expect(themeModule.getCurrentThemeName()).toBe("light");
|
|
expect(detectSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|