File size: 4,293 Bytes
b59aa07
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { describe, expect, it, vi, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ActionSuggestions } from "#/components/features/chat/action-suggestions";
import OpenHands from "#/api/open-hands";
import { MOCK_DEFAULT_USER_SETTINGS } from "#/mocks/handlers";

// Mock dependencies
vi.mock("posthog-js", () => ({
  default: {
    capture: vi.fn(),
  },
}));

const { useSelectorMock } = vi.hoisted(() => ({
  useSelectorMock: vi.fn(),
}));

vi.mock("react-redux", () => ({
  useSelector: useSelectorMock,
}));

vi.mock("#/context/auth-context", () => ({
  useAuth: vi.fn(),
}));

// Mock react-i18next
vi.mock("react-i18next", () => ({
  useTranslation: () => ({
    t: (key: string) => {
      const translations: Record<string, string> = {
        ACTION$PUSH_TO_BRANCH: "Push to Branch",
        ACTION$PUSH_CREATE_PR: "Push & Create PR",
        ACTION$PUSH_CHANGES_TO_PR: "Push Changes to PR",
      };
      return translations[key] || key;
    },
  }),
}));

vi.mock("react-router", () => ({
  useParams: () => ({
    conversationId: "test-conversation-id",
  }),
}));

const renderActionSuggestions = () =>
  render(<ActionSuggestions onSuggestionsClick={() => {}} />, {
    wrapper: ({ children }) => (
      <QueryClientProvider client={new QueryClient()}>
        {children}
      </QueryClientProvider>
    ),
  });

describe("ActionSuggestions", () => {
  // Setup mocks for each test
  beforeEach(() => {
    vi.clearAllMocks();
    const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
    getSettingsSpy.mockResolvedValue({
      ...MOCK_DEFAULT_USER_SETTINGS,
      provider_tokens_set: {
        github: "some-token",
      },
    });

    useSelectorMock.mockReturnValue({
      selectedRepository: "test-repo",
    });
  });

  it("should render both GitHub buttons when GitHub token is set and repository is selected", async () => {
    const getConversationSpy = vi.spyOn(OpenHands, "getConversation");
    // @ts-expect-error - only required for testing
    getConversationSpy.mockResolvedValue({
      selected_repository: "test-repo",
    });
    renderActionSuggestions();

    // Find all buttons with data-testid="suggestion"
    const buttons = await screen.findAllByTestId("suggestion");

    // Check if we have at least 2 buttons
    expect(buttons.length).toBeGreaterThanOrEqual(2);

    // Check if the buttons contain the expected text
    const pushButton = buttons.find((button) =>
      button.textContent?.includes("Push to Branch"),
    );
    const prButton = buttons.find((button) =>
      button.textContent?.includes("Push & Create PR"),
    );

    expect(pushButton).toBeInTheDocument();
    expect(prButton).toBeInTheDocument();
  });

  it("should not render buttons when GitHub token is not set", () => {
    renderActionSuggestions();

    expect(screen.queryByTestId("suggestion")).not.toBeInTheDocument();
  });

  it("should not render buttons when no repository is selected", () => {
    useSelectorMock.mockReturnValue({
      selectedRepository: null,
    });

    renderActionSuggestions();

    expect(screen.queryByTestId("suggestion")).not.toBeInTheDocument();
  });

  it("should have different prompts for 'Push to Branch' and 'Push & Create PR' buttons", () => {
    // This test verifies that the prompts are different in the component
    renderActionSuggestions();

    // Get the component instance to access the internal values
    const pushBranchPrompt =
      "Please push the changes to a remote branch on GitHub, but do NOT create a pull request. Please use the exact SAME branch name as the one you are currently on.";
    const createPRPrompt =
      "Please push the changes to GitHub and open a pull request. Please create a meaningful branch name that describes the changes. If a pull request template exists in the repository, please follow it when creating the PR description.";

    // Verify the prompts are different
    expect(pushBranchPrompt).not.toEqual(createPRPrompt);

    // Verify the PR prompt mentions creating a meaningful branch name
    expect(createPRPrompt).toContain("meaningful branch name");
    expect(createPRPrompt).not.toContain("SAME branch name");
  });
});