241 lines · 8.3 KB · TypeScript
RawDownload
import { assert, describe, it } from "@effect/vitest";import { HostProcessPlatform } from "@t3tools/shared/hostProcess";import * as Cause from "effect/Cause";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import type * as Electron from "electron";import { beforeEach, vi } from "vite-plus/test"; const { appFocusMock, browserWindowMock, getAllWindowsMock, getFocusedWindowMock } = vi.hoisted(  () => ({    appFocusMock: vi.fn(),    browserWindowMock: vi.fn(function BrowserWindowMock() {}),    getAllWindowsMock: vi.fn(),    getFocusedWindowMock: vi.fn(),  }),); vi.mock("electron", () => ({  app: {    focus: appFocusMock,  },  BrowserWindow: Object.assign(browserWindowMock, {    getAllWindows: getAllWindowsMock,    getFocusedWindow: getFocusedWindowMock,  }),})); import * as ElectronWindow from "./ElectronWindow.ts"; const TestLayer = ElectronWindow.layer.pipe(  Layer.provide(Layer.succeed(HostProcessPlatform, "linux")),); function makeBrowserWindow(input: { readonly id: number; readonly destroyed: boolean }) {  return {    id: input.id,    isDestroyed: vi.fn(() => input.destroyed),  } as unknown as Electron.BrowserWindow;} describe("ElectronWindow", () => {  beforeEach(() => {    appFocusMock.mockReset();    browserWindowMock.mockReset();    getAllWindowsMock.mockReset();    getFocusedWindowMock.mockReset();  });   it.effect("preserves schema-safe creation context and the Electron cause", () =>    Effect.gen(function* () {      const cause = new Error("native BrowserWindow construction failed");      browserWindowMock.mockImplementationOnce(function BrowserWindowFailure() {        throw cause;      });      const options = {        title: "T3 Code",        width: 1100,        height: 780,        minWidth: 840,        minHeight: 620,        show: false,        modal: false,        frame: true,        transparent: false,        backgroundColor: "#101010",        icon: {} as Electron.NativeImage,        webPreferences: {          preload: "/tmp/preload.js",          partition: "persist:t3code-preview-test",          sandbox: true,          contextIsolation: true,          nodeIntegration: false,          webviewTag: true,          spellcheck: true,        },      } satisfies Electron.BrowserWindowConstructorOptions;      const electronWindow = yield* ElectronWindow.ElectronWindow;       const error = yield* electronWindow.create(options).pipe(Effect.flip);       assert.instanceOf(error, ElectronWindow.ElectronWindowCreateError);      assert.isTrue(ElectronWindow.isElectronWindowCreateError(error));      assert.deepEqual(error.options, {        title: "T3 Code",        width: 1100,        height: 780,        minWidth: 840,        minHeight: 620,        show: false,        modal: false,        frame: true,        transparent: false,        backgroundColor: "#101010",        webPreferences: {          preload: "/tmp/preload.js",          partition: "persist:t3code-preview-test",          backgroundThrottling: null,          sandbox: true,          contextIsolation: true,          nodeIntegration: false,          webviewTag: true,        },      });      assert.isFalse("icon" in error.options);      assert.isFalse("spellcheck" in error.options.webPreferences);      assert.strictEqual(error.cause, cause);      assert.equal(error.message, 'Failed to create Electron BrowserWindow "T3 Code" (1100x780).');      assert.notInclude(error.message, cause.message);      assert.deepEqual(browserWindowMock.mock.calls, [[options]]);    }).pipe(Effect.provide(TestLayer)),  );   it.effect("skips windows destroyed before appearance sync runs", () =>    Effect.gen(function* () {      const liveWindow = makeBrowserWindow({ id: 1, destroyed: false });      const destroyedWindow = makeBrowserWindow({ id: 2, destroyed: true });      getAllWindowsMock.mockReturnValue([destroyedWindow, liveWindow]);       const syncedWindows: Electron.BrowserWindow[] = [];      const electronWindow = yield* ElectronWindow.ElectronWindow;      yield* electronWindow.syncAllAppearance((window) =>        Effect.sync(() => {          syncedWindows.push(window);        }),      );       assert.deepEqual(syncedWindows, [liveWindow]);    }).pipe(Effect.provide(TestLayer)),  );   it.effect("preserves window enumeration failures as structured defects", () =>    Effect.gen(function* () {      const cause = new Error("window enumeration failed");      getAllWindowsMock.mockImplementationOnce(() => {        throw cause;      });       const electronWindow = yield* ElectronWindow.ElectronWindow;      const exit = yield* Effect.exit(electronWindow.currentMainOrFirst);       assert.equal(exit._tag, "Failure");      if (exit._tag === "Failure") {        const error = Cause.squash(exit.cause);        assert.instanceOf(error, ElectronWindow.ElectronWindowOperationError);        assert.equal(error.operation, "list-windows");        assert.equal(error.platform, "linux");        assert.isNull(error.windowId);        assert.isNull(error.channel);        assert.strictEqual(error.cause, cause);        assert.notInclude(error.message, cause.message);      }    }).pipe(Effect.provide(TestLayer)),  );   it.effect("preserves reveal failures with the target window", () =>    Effect.gen(function* () {      const cause = new Error("window restore failed");      const window = {        id: 41,        isDestroyed: vi.fn(() => false),        isMinimized: vi.fn(() => true),        restore: vi.fn(() => {          throw cause;        }),      } as unknown as Electron.BrowserWindow;       const electronWindow = yield* ElectronWindow.ElectronWindow;      const exit = yield* Effect.exit(electronWindow.reveal(window));       assert.equal(exit._tag, "Failure");      if (exit._tag === "Failure") {        const error = Cause.squash(exit.cause);        assert.instanceOf(error, ElectronWindow.ElectronWindowOperationError);        assert.equal(error.operation, "reveal-window");        assert.equal(error.windowId, 41);        assert.isNull(error.channel);        assert.strictEqual(error.cause, cause);      }    }).pipe(Effect.provide(TestLayer)),  );   it.effect("preserves message delivery failures with window and channel context", () =>    Effect.gen(function* () {      const cause = new Error("renderer send failed");      const window = {        id: 42,        isDestroyed: vi.fn(() => false),        webContents: {          send: vi.fn(() => {            throw cause;          }),        },      } as unknown as Electron.BrowserWindow;      getAllWindowsMock.mockReturnValueOnce([window]);       const electronWindow = yield* ElectronWindow.ElectronWindow;      const exit = yield* Effect.exit(electronWindow.sendAll("desktop:update", { ready: true }));       assert.equal(exit._tag, "Failure");      if (exit._tag === "Failure") {        const error = Cause.squash(exit.cause);        assert.instanceOf(error, ElectronWindow.ElectronWindowOperationError);        assert.equal(error.operation, "send-window-message");        assert.equal(error.windowId, 42);        assert.equal(error.channel, "desktop:update");        assert.strictEqual(error.cause, cause);      }    }).pipe(Effect.provide(TestLayer)),  );   it.effect("preserves destroy failures and continues with later windows", () =>    Effect.gen(function* () {      const cause = new Error("window destroy failed");      const window = {        id: 43,        destroy: vi.fn(() => {          throw cause;        }),      } as unknown as Electron.BrowserWindow;      const laterWindow = {        id: 44,        destroy: vi.fn(),      } as unknown as Electron.BrowserWindow;      getAllWindowsMock.mockReturnValueOnce([window, laterWindow]);       const electronWindow = yield* ElectronWindow.ElectronWindow;      const exit = yield* Effect.exit(electronWindow.destroyAll);       assert.equal(exit._tag, "Failure");      if (exit._tag === "Failure") {        const error = Cause.squash(exit.cause);        assert.instanceOf(error, ElectronWindow.ElectronWindowOperationError);        assert.equal(error.operation, "destroy-window");        assert.equal(error.windowId, 43);        assert.isNull(error.channel);        assert.strictEqual(error.cause, cause);      }      assert.equal(vi.mocked(laterWindow.destroy).mock.calls.length, 1);    }).pipe(Effect.provide(TestLayer)),  );});