144 lines · 4.8 KB · TypeScript
RawDownload
import { assert, describe, it } from "@effect/vitest";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import { TestClock } from "effect/testing";import type * as Electron from "electron"; import * as ElectronWindow from "../electron/ElectronWindow.ts";import * as IpcChannels from "../ipc/channels.ts";import * as DesktopSshPasswordPrompts from "./DesktopSshPasswordPrompts.ts"; interface SentMessage {  readonly channel: string;  readonly args: readonly unknown[];} function makeTestWindow() {  const listeners = new Map<string, Set<() => void>>();  const sentMessages: SentMessage[] = [];  let destroyed = false;  let minimized = true;  let restored = false;  let focused = false;   const window = {    isDestroyed: () => destroyed,    isMinimized: () => minimized,    restore: () => {      restored = true;      minimized = false;    },    focus: () => {      focused = true;    },    once: (eventName: string, listener: () => void) => {      const eventListeners = listeners.get(eventName) ?? new Set<() => void>();      eventListeners.add(listener);      listeners.set(eventName, eventListeners);    },    removeListener: (eventName: string, listener: () => void) => {      listeners.get(eventName)?.delete(listener);    },    webContents: {      send: (channel: string, ...args: readonly unknown[]) => {        sentMessages.push({ channel, args });      },    },  };   return {    window,    sentMessages,    isRestored: () => restored,    isFocused: () => focused,    close: () => {      destroyed = true;      const closedListeners = [...(listeners.get("closed") ?? [])];      listeners.delete("closed");      for (const listener of closedListeners) {        listener();      }    },  };} function makeElectronWindowLayer(window: ReturnType<typeof makeTestWindow>["window"]) {  return Layer.succeed(    ElectronWindow.ElectronWindow,    ElectronWindow.ElectronWindow.of({      create: () => Effect.die("unexpected BrowserWindow creation"),      main: Effect.succeed(Option.some(window as Electron.BrowserWindow)),      currentMainOrFirst: Effect.succeed(Option.some(window as Electron.BrowserWindow)),      focusedMainOrFirst: Effect.succeed(Option.some(window as Electron.BrowserWindow)),      setMain: () => Effect.void,      clearMain: () => Effect.void,      reveal: () => Effect.void,      sendAll: () => Effect.void,      destroyAll: Effect.void,      syncAllAppearance: () => Effect.void,    }),  );} function makeLayer(window: ReturnType<typeof makeTestWindow>["window"]) {  return DesktopSshPasswordPrompts.layer({ passwordPromptTimeoutMs: 1_000 }).pipe(    Layer.provide(makeElectronWindowLayer(window)),    Layer.provideMerge(TestClock.layer()),  );} describe("DesktopSshPasswordPrompts", () => {  it.effect("sends renderer prompts and resolves them by request id", () => {    const testWindow = makeTestWindow();     return Effect.gen(function* () {      const prompts = yield* DesktopSshPasswordPrompts.DesktopSshPasswordPrompts;      const fiber = yield* prompts        .request({          destination: "devbox",          username: "julius",          prompt: "Enter the SSH password.",          attempt: 1,        })        .pipe(Effect.forkScoped);       yield* Effect.yieldNow;      assert.equal(testWindow.sentMessages.length, 1);      const sent = testWindow.sentMessages[0];      assert.ok(sent);      assert.equal(sent.channel, IpcChannels.SSH_PASSWORD_PROMPT_CHANNEL);      const request = sent.args[0] as { readonly requestId: string; readonly destination: string };      assert.equal(request.destination, "devbox");      assert.equal(testWindow.isRestored(), true);      assert.equal(testWindow.isFocused(), true);       yield* prompts.resolve({ requestId: request.requestId, password: "secret" });      assert.equal(yield* Fiber.join(fiber), "secret");    }).pipe(Effect.provide(makeLayer(testWindow.window)), Effect.scoped);  });   it.effect("times out pending renderer prompts with a typed error", () => {    const testWindow = makeTestWindow();     return Effect.gen(function* () {      const prompts = yield* DesktopSshPasswordPrompts.DesktopSshPasswordPrompts;      const fiber = yield* prompts        .request({          destination: "devbox",          username: null,          prompt: "Enter the SSH password.",          attempt: 1,        })        .pipe(Effect.forkScoped);       yield* Effect.yieldNow;      yield* TestClock.adjust(Duration.millis(1_000));      const error = yield* Fiber.join(fiber).pipe(Effect.flip);      assert.instanceOf(error, DesktopSshPasswordPrompts.DesktopSshPromptTimedOutError);      assert.equal(error.destination, "devbox");    }).pipe(Effect.provide(makeLayer(testWindow.window)), Effect.scoped);  });});