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)), ); });