cruise 0.1.74

YAML-driven coding agent workflow orchestrator
Documentation
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { PublishIssueDialog } from "../components/PublishIssueDialog";

describe("PublishIssueDialog", () => {
  const baseProps = {
    sessionId: "session-1",
    onConfirm: vi.fn().mockResolvedValue(undefined),
    onCancel: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  // --- Rendering ---------------------------------------------------------------

  it("renders title, session id, checkbox, and action buttons", () => {
    // Given / When
    render(<PublishIssueDialog {...baseProps} />);

    // Then
    expect(screen.getByRole("dialog")).toBeInTheDocument();
    expect(screen.getByText("Publish as GitHub Issue")).toBeInTheDocument();
    expect(screen.getByText(/session-1/)).toBeInTheDocument();
    expect(screen.getByRole("checkbox")).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Publish" })).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Cancel" })).toBeInTheDocument();
  });

  it("mentions that the issue body is plan.md, unchanged", () => {
    // Given / When
    render(<PublishIssueDialog {...baseProps} />);

    // Then: the copy makes explicit that plan.md is not modified before publishing
    expect(screen.getByText(/plan\.md/)).toBeInTheDocument();
  });

  it("labels the checkbox as posting a separate `@cruise run` comment", () => {
    // Given / When
    render(<PublishIssueDialog {...baseProps} />);

    // Then: the checkbox describes triggering the Action via a follow-up comment,
    // not mentioning @cruise inside the issue body itself
    expect(screen.getByText(/@cruise run/)).toBeInTheDocument();
  });

  // --- defaultTriggerCruise: checkbox initial state -----------------------------

  describe("defaultTriggerCruise", () => {
    it("checkbox is unchecked by default when defaultTriggerCruise is omitted", () => {
      // Given: the Awaiting Approval use case, which does not pass defaultTriggerCruise
      render(<PublishIssueDialog {...baseProps} />);

      // Then
      expect(screen.getByRole("checkbox")).not.toBeChecked();
    });

    it("checkbox is unchecked by default when defaultTriggerCruise is false", () => {
      // Given
      render(<PublishIssueDialog {...baseProps} defaultTriggerCruise={false} />);

      // Then
      expect(screen.getByRole("checkbox")).not.toBeChecked();
    });

    it("checkbox is checked by default when defaultTriggerCruise is true", () => {
      // Given: the Planned use case, where publishing replaces running locally
      render(<PublishIssueDialog {...baseProps} defaultTriggerCruise={true} />);

      // Then
      expect(screen.getByRole("checkbox")).toBeChecked();
    });
  });

  // --- onConfirm argument -------------------------------------------------------

  describe("onConfirm argument", () => {
    it("calls onConfirm with false when Publish is clicked without touching the checkbox", async () => {
      // Given
      const onConfirm = vi.fn().mockResolvedValue(undefined);
      render(<PublishIssueDialog {...baseProps} onConfirm={onConfirm} />);

      // When
      await userEvent.click(screen.getByRole("button", { name: "Publish" }));

      // Then
      expect(onConfirm).toHaveBeenCalledWith(false);
    });

    it("calls onConfirm with true after checking the box and clicking Publish", async () => {
      // Given
      const onConfirm = vi.fn().mockResolvedValue(undefined);
      render(<PublishIssueDialog {...baseProps} onConfirm={onConfirm} />);

      // When
      await userEvent.click(screen.getByRole("checkbox"));
      await userEvent.click(screen.getByRole("button", { name: "Publish" }));

      // Then
      expect(onConfirm).toHaveBeenCalledWith(true);
    });

    it("calls onConfirm with true by default when defaultTriggerCruise is true and Publish is clicked without touching the checkbox", async () => {
      // Given: Planned session flow -- default ON, user just clicks Publish
      const onConfirm = vi.fn().mockResolvedValue(undefined);
      render(
        <PublishIssueDialog {...baseProps} onConfirm={onConfirm} defaultTriggerCruise={true} />
      );

      // When
      await userEvent.click(screen.getByRole("button", { name: "Publish" }));

      // Then
      expect(onConfirm).toHaveBeenCalledWith(true);
    });

    it("calls onConfirm with false after unchecking a defaulted-on checkbox", async () => {
      // Given: Planned session flow, but the user opts out of the @cruise run comment
      const onConfirm = vi.fn().mockResolvedValue(undefined);
      render(
        <PublishIssueDialog {...baseProps} onConfirm={onConfirm} defaultTriggerCruise={true} />
      );

      // When
      await userEvent.click(screen.getByRole("checkbox"));
      await userEvent.click(screen.getByRole("button", { name: "Publish" }));

      // Then
      expect(onConfirm).toHaveBeenCalledWith(false);
    });
  });

  // --- Cancel --------------------------------------------------------------------

  describe("Cancel", () => {
    it("calls onCancel when the Cancel button is clicked", async () => {
      // Given
      const onCancel = vi.fn();
      render(<PublishIssueDialog {...baseProps} onCancel={onCancel} />);

      // When
      await userEvent.click(screen.getByRole("button", { name: "Cancel" }));

      // Then
      expect(onCancel).toHaveBeenCalledOnce();
    });

    it("calls onCancel when Escape is pressed", async () => {
      // Given
      const onCancel = vi.fn();
      render(<PublishIssueDialog {...baseProps} onCancel={onCancel} />);

      // When
      await userEvent.keyboard("{Escape}");

      // Then
      expect(onCancel).toHaveBeenCalledOnce();
    });

    it("does not call onConfirm when Cancel is clicked", async () => {
      // Given
      const onConfirm = vi.fn().mockResolvedValue(undefined);
      render(<PublishIssueDialog {...baseProps} onConfirm={onConfirm} />);

      // When
      await userEvent.click(screen.getByRole("button", { name: "Cancel" }));

      // Then
      expect(onConfirm).not.toHaveBeenCalled();
    });
  });

  // --- Error handling --------------------------------------------------------------

  describe("when onConfirm rejects", () => {
    it("shows the error message and re-enables the Publish button", async () => {
      // Given
      const onConfirm = vi.fn().mockRejectedValue(new Error("gh issue create failed"));
      render(<PublishIssueDialog {...baseProps} onConfirm={onConfirm} />);

      // When
      await userEvent.click(screen.getByRole("button", { name: "Publish" }));

      // Then
      expect(await screen.findByText(/gh issue create failed/)).toBeInTheDocument();
      expect(screen.getByRole("button", { name: "Publish" })).not.toBeDisabled();
    });
  });
});