Ctrl K
ComunidadGuías…

src/main_bootstrap.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import fs from "node:fs";
import path from "node:path";
import ts from "typescript";
import { beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
  exit: vi.fn(),
  logError: vi.fn(),
  logFilePath: "/logs/main.log",
  quit: vi.fn(),
  showErrorBox: vi.fn(),
  runtimeLoaded: vi.fn(),
  runtimeError: { value: undefined as Error | undefined },
  runtimeGate: { value: undefined as Promise<void> | undefined },
  resolveReady: { value: undefined as (() => void) | undefined },
  squirrelStarted: { value: true },
  whenReady: vi.fn(
    () =>
      new Promise<void>((resolve) => {
        mocks.resolveReady.value = resolve;
      }),
  ),
}));

vi.mock("electron", () => ({
  app: {
    exit: mocks.exit,
    quit: mocks.quit,
    whenReady: mocks.whenReady,
  },
  dialog: { showErrorBox: mocks.showErrorBox },
}));

vi.mock("electron-log", () => ({
  default: {
    scope: () => ({ error: mocks.logError }),
    transports: {
      file: {
        getFile: () => ({ path: mocks.logFilePath }),
      },
    },
  },
}));

vi.mock("electron-squirrel-startup", () => ({
  get default() {
    return mocks.squirrelStarted.value;
  },
}));

describe("main process bootstrap", () => {
  beforeEach(() => {
    vi.resetModules();
    mocks.exit.mockClear();
    mocks.logError.mockClear();
    mocks.quit.mockClear();
    mocks.showErrorBox.mockClear();
    mocks.runtimeLoaded.mockClear();
    mocks.runtimeError.value = undefined;
    mocks.runtimeGate.value = undefined;
    mocks.resolveReady.value = undefined;
    mocks.squirrelStarted.value = true;
    mocks.whenReady.mockClear();
    vi.doMock("./main", async () => {
      await mocks.runtimeGate.value;
      if (mocks.runtimeError.value) {
        throw mocks.runtimeError.value;
      }
      mocks.runtimeLoaded();
      return {};
    });
  });

  it("quits without loading the application during a Squirrel event", async () => {
    await import("./main_bootstrap");
    await new Promise<void>((resolve) => setImmediate(resolve));

    expect(mocks.quit).toHaveBeenCalledOnce();
    expect(mocks.runtimeLoaded).not.toHaveBeenCalled();
    expect(mocks.whenReady).not.toHaveBeenCalled();
  });

  it("loads the application outside a Squirrel event", async () => {
    mocks.squirrelStarted.value = false;

    await import("./main_bootstrap");

    await vi.waitFor(() => expect(mocks.runtimeLoaded).toHaveBeenCalledOnce());
    expect(mocks.quit).not.toHaveBeenCalled();
  });

  it("fails startup if Electron becomes ready before the runtime loads", async () => {
    let releaseRuntime: (() => void) | undefined;
    mocks.squirrelStarted.value = false;
    mocks.runtimeGate.value = new Promise<void>((resolve) => {
      releaseRuntime = resolve;
    });

    await import("./main_bootstrap");
    mocks.resolveReady.value?.();

    await vi.waitFor(() => expect(mocks.exit).toHaveBeenCalledWith(1));
    expect(mocks.logError).toHaveBeenCalledWith(
      "Failed to load the Dyad application runtime:",
      expect.objectContaining({
        message:
          "Electron became ready before the application runtime completed its pre-ready registrations",
      }),
    );
    expect(mocks.runtimeLoaded).not.toHaveBeenCalled();
    releaseRuntime?.();
  });

  it("reports a failure and exits when the application cannot load", async () => {
    const runtimeError = new Error("runtime failed");
    mocks.squirrelStarted.value = false;
    mocks.runtimeError.value = runtimeError;

    await import("./main_bootstrap");

    await vi.waitFor(() => expect(mocks.exit).toHaveBeenCalledWith(1));
    expect(mocks.logError).toHaveBeenCalledWith(
      "Failed to load the Dyad application runtime:",
      expect.any(Error),
    );
    expect(mocks.showErrorBox).toHaveBeenCalledWith(
      "Dyad failed to start",
      expect.stringMatching(
        new RegExp(
          `^The application runtime could not be loaded\\.\\n\\nError: .+runtime failed\\n\\nDetails were written to:\\n${mocks.logFilePath}\\n\\nPlease share this error and log file when contacting support\\.$`,
          "s",
        ),
      ),
    );
    expect(mocks.runtimeLoaded).not.toHaveBeenCalled();
  });

  it("has no static imports outside the minimal bootstrap dependencies", () => {
    const bootstrapPath = path.resolve(__dirname, "main_bootstrap.ts");
    const sourceFile = ts.createSourceFile(
      bootstrapPath,
      fs.readFileSync(bootstrapPath, "utf8"),
      ts.ScriptTarget.Latest,
      true,
      ts.ScriptKind.TS,
    );
    const staticImports = sourceFile.statements.flatMap((statement) => {
      if (
        !ts.isImportDeclaration(statement) ||
        !ts.isStringLiteralLike(statement.moduleSpecifier)
      ) {
        return [];
      }
      return [statement.moduleSpecifier.text];
    });

    expect(staticImports).toEqual([
      "electron",
      "electron-log",
      "electron-squirrel-startup",
    ]);
  });
});