180 lines · 6.1 KB · TypeScript
RawDownload
import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, describe, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Ref from "effect/Ref"; import type * as Electron from "electron";import { vi } from "vitest"; import * as DesktopAssets from "../app/DesktopAssets.ts";import * as DesktopConfig from "../app/DesktopConfig.ts";import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";import * as DesktopState from "../app/DesktopState.ts";import * as ElectronMenu from "../electron/ElectronMenu.ts";import * as ElectronShell from "../electron/ElectronShell.ts";import * as ElectronTheme from "../electron/ElectronTheme.ts";import * as ElectronWindow from "../electron/ElectronWindow.ts";import * as DesktopServerExposure from "../backend/DesktopServerExposure.ts";import * as DesktopWindow from "./DesktopWindow.ts"; const environmentInput = {  dirname: "/repo/apps/desktop/dist-electron",  homeDirectory: "/Users/alice",  platform: "darwin",  processArch: "arm64",  appVersion: "1.2.3",  appPath: "/repo",  isPackaged: false,  resourcesPath: "/repo/resources",  runningUnderArm64Translation: false,} satisfies DesktopEnvironment.MakeDesktopEnvironmentInput; function makeFakeBrowserWindow() {  const webContents = {    copyImageAt: vi.fn(),    isLoadingMainFrame: vi.fn(() => false),    on: vi.fn(),    once: vi.fn(),    openDevTools: vi.fn(),    replaceMisspelling: vi.fn(),    send: vi.fn(),    setWindowOpenHandler: vi.fn(),  };   const window = {    focus: vi.fn(),    isDestroyed: vi.fn(() => false),    isMinimized: vi.fn(() => false),    isVisible: vi.fn(() => true),    loadURL: vi.fn(() => Promise.resolve()),    on: vi.fn(),    once: vi.fn(),    restore: vi.fn(),    setBackgroundColor: vi.fn(),    setTitle: vi.fn(),    setTitleBarOverlay: vi.fn(),    show: vi.fn(),    webContents,  };   return {    window: window as unknown as Electron.BrowserWindow,    loadURL: window.loadURL,    openDevTools: webContents.openDevTools,  };} const desktopAssetsLayer = Layer.succeed(DesktopAssets.DesktopAssets, {  iconPaths: Effect.succeed({    ico: Option.none<string>(),    icns: Option.none<string>(),    png: Option.none<string>(),  }),  resolveResourcePath: () => Effect.succeed(Option.none<string>()),} satisfies DesktopAssets.DesktopAssetsShape); const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, {  getState: Effect.die("unexpected getState"),  backendConfig: Effect.succeed({    port: 3773,    bindHost: "127.0.0.1",    httpBaseUrl: new URL("http://127.0.0.1:3773"),    tailscaleServeEnabled: false,    tailscaleServePort: 443,  }),  configureFromSettings: () => Effect.die("unexpected configureFromSettings"),  setMode: () => Effect.die("unexpected setMode"),  setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"),  getAdvertisedEndpoints: Effect.die("unexpected getAdvertisedEndpoints"),} satisfies DesktopServerExposure.DesktopServerExposureShape); const electronMenuLayer = Layer.succeed(ElectronMenu.ElectronMenu, {  setApplicationMenu: () => Effect.void,  popupTemplate: () => Effect.void,  showContextMenu: () => Effect.succeed(Option.none()),} satisfies ElectronMenu.ElectronMenuShape); const electronShellLayer = Layer.succeed(ElectronShell.ElectronShell, {  openExternal: () => Effect.succeed(true),  copyText: () => Effect.void,} satisfies ElectronShell.ElectronShellShape); const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, {  shouldUseDarkColors: Effect.succeed(false),  setSource: () => Effect.void,  onUpdated: () => Effect.void,} satisfies ElectronTheme.ElectronThemeShape); const desktopEnvironmentLayer = DesktopEnvironment.layer(environmentInput).pipe(  Layer.provide(    Layer.mergeAll(      NodeServices.layer,      DesktopConfig.layerTest({        T3CODE_PORT: "3773",        VITE_DEV_SERVER_URL: "http://127.0.0.1:5733",      }),    ),  ),); function makeTestLayer(input: {  readonly window: Electron.BrowserWindow;  readonly createCount: Ref.Ref<number>;  readonly mainWindow: Ref.Ref<Option.Option<Electron.BrowserWindow>>;}) {  const electronWindowLayer = Layer.succeed(ElectronWindow.ElectronWindow, {    create: () => Ref.update(input.createCount, (count) => count + 1).pipe(Effect.as(input.window)),    main: Ref.get(input.mainWindow),    currentMainOrFirst: Ref.get(input.mainWindow),    focusedMainOrFirst: Ref.get(input.mainWindow),    setMain: (window) => Ref.set(input.mainWindow, Option.some(window)),    clearMain: () => Ref.set(input.mainWindow, Option.none()),    reveal: () => Effect.void,    sendAll: () => Effect.void,    destroyAll: Effect.void,    syncAllAppearance: (sync) => sync(input.window),  } satisfies ElectronWindow.ElectronWindowShape);   return DesktopWindow.layer.pipe(    Layer.provide(      Layer.mergeAll(        desktopAssetsLayer,        desktopEnvironmentLayer,        desktopServerExposureLayer,        DesktopState.layer,        electronMenuLayer,        electronShellLayer,        electronThemeLayer,        electronWindowLayer,      ),    ),  );} describe("DesktopWindow", () => {  it.effect("does not open a development window until the backend is ready", () =>    Effect.gen(function* () {      const fakeWindow = makeFakeBrowserWindow();      const createCount = yield* Ref.make(0);      const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none());      const layer = makeTestLayer({        window: fakeWindow.window,        createCount,        mainWindow,      });       yield* Effect.gen(function* () {        const desktopWindow = yield* DesktopWindow.DesktopWindow;        yield* desktopWindow.activate;        assert.equal(yield* Ref.get(createCount), 0);         yield* desktopWindow.handleBackendReady;        assert.equal(yield* Ref.get(createCount), 1);        assert.deepEqual(fakeWindow.loadURL.mock.calls[0], ["http://127.0.0.1:5733/"]);        assert.equal(fakeWindow.openDevTools.mock.calls.length, 1);      }).pipe(Effect.provide(layer));    }),  );});