1import { assert, describe, it } from "@effect/vitest";
2import * as Effect from "effect/Effect";
3import { beforeEach, vi } from "vite-plus/test";
4
5const { onMock, removeListenerMock, themeState } = vi.hoisted(() => ({
6 onMock: vi.fn(),
7 removeListenerMock: vi.fn(),
8 themeState: {
9 shouldUseDarkColors: true,
10 themeSource: "system",
11 setSourceError: null as unknown,
12 },
13}));
14
15vi.mock("electron", () => ({
16 nativeTheme: {
17 get shouldUseDarkColors() {
18 return themeState.shouldUseDarkColors;
19 },
20 set themeSource(value: string) {
21 if (themeState.setSourceError !== null) {
22 throw themeState.setSourceError;
23 }
24 themeState.themeSource = value;
25 },
26 on: onMock,
27 removeListener: removeListenerMock,
28 },
29}));
30
31import * as ElectronTheme from "./ElectronTheme.ts";
32
33describe("ElectronTheme", () => {
34 beforeEach(() => {
35 onMock.mockClear();
36 removeListenerMock.mockClear();
37 themeState.shouldUseDarkColors = true;
38 themeState.themeSource = "system";
39 themeState.setSourceError = null;
40 });
41
42 it.effect("scopes native theme update listeners", () =>
43 Effect.gen(function* () {
44 const listener = vi.fn();
45
46 yield* Effect.scoped(
47 Effect.gen(function* () {
48 const electronTheme = yield* ElectronTheme.ElectronTheme;
49 yield* electronTheme.onUpdated(listener);
50 }),
51 );
52
53 assert.deepEqual(onMock.mock.calls, [["updated", listener]]);
54 assert.deepEqual(removeListenerMock.mock.calls, [["updated", listener]]);
55 }).pipe(Effect.provide(ElectronTheme.layer)),
56 );
57
58 it.effect("preserves the requested source and cause when setting the theme fails", () =>
59 Effect.gen(function* () {
60 const cause = new Error("theme source failed");
61 themeState.setSourceError = cause;
62 const electronTheme = yield* ElectronTheme.ElectronTheme;
63
64 const error = yield* Effect.flip(electronTheme.setSource("dark"));
65
66 assert.instanceOf(error, ElectronTheme.ElectronThemeSetSourceError);
67 assert.strictEqual(error.source, "dark");
68 assert.strictEqual(error.cause, cause);
69 assert.include(error.message, "dark");
70 assert.notInclude(error.message, cause.message);
71 }).pipe(Effect.provide(ElectronTheme.layer)),
72 );
73});