Skip to content

Commit

Permalink
Merge branch 'main' into ray/all-959-user-story-1-connection-rejections
Browse files Browse the repository at this point in the history
  • Loading branch information
raymyers authored Jan 7, 2025
2 parents 464471c + cf0f6e5 commit ec900cb
Show file tree
Hide file tree
Showing 60 changed files with 817 additions and 363 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { afterEach, describe, expect, it, test, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import { formatTimeDelta } from "#/utils/format-time-delta";
import { ConversationCard } from "#/components/features/conversation-panel/conversation-card";
import { clickOnEditButton } from "./utils";

describe("ConversationCard", () => {
const onClick = vi.fn();
Expand Down Expand Up @@ -144,7 +145,9 @@ describe("ConversationCard", () => {
/>,
);

const selectedRepository = screen.getByTestId("conversation-card-selected-repository");
const selectedRepository = screen.getByTestId(
"conversation-card-selected-repository",
);
await user.click(selectedRepository);

expect(onClick).not.toHaveBeenCalled();
Expand All @@ -164,13 +167,22 @@ describe("ConversationCard", () => {
);

const title = screen.getByTestId("conversation-card-title");
expect(title).toBeDisabled();

await clickOnEditButton(user);

expect(title).toBeEnabled();
expect(screen.queryByTestId("context-menu")).not.toBeInTheDocument();
// expect to be focused
expect(document.activeElement).toBe(title);

await user.clear(title);
await user.type(title, "New Conversation Name ");
await user.tab();

expect(onChangeTitle).toHaveBeenCalledWith("New Conversation Name");
expect(title).toHaveValue("New Conversation Name");
expect(title).toBeDisabled();
});

it("should reset title and not call onChangeTitle when the title is empty", async () => {
Expand All @@ -186,6 +198,8 @@ describe("ConversationCard", () => {
/>,
);

await clickOnEditButton(user);

const title = screen.getByTestId("conversation-card-title");

await user.clear(title);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import userEvent from "@testing-library/user-event";
import { ConversationPanel } from "#/components/features/conversation-panel/conversation-panel";
import OpenHands from "#/api/open-hands";
import { AuthProvider } from "#/context/auth-context";
import { clickOnEditButton } from "./utils";

describe("ConversationPanel", () => {
const onCloseMock = vi.fn();
Expand Down Expand Up @@ -52,6 +53,8 @@ describe("ConversationPanel", () => {
renderConversationPanel();
const cards = await screen.findAllByTestId("conversation-card");

// NOTE that we filter out conversations that don't have a created_at property
// (mock data has 4 conversations, but only 3 have a created_at property)
expect(cards).toHaveLength(3);
});

Expand Down Expand Up @@ -169,6 +172,8 @@ describe("ConversationPanel", () => {
const cards = await screen.findAllByTestId("conversation-card");
const title = within(cards[0]).getByTestId("conversation-card-title");

await clickOnEditButton(user);

await user.clear(title);
await user.type(title, "Conversation 1 Renamed");
await user.tab();
Expand Down Expand Up @@ -196,6 +201,8 @@ describe("ConversationPanel", () => {
// Ensure the conversation is not renamed
expect(updateUserConversationSpy).not.toHaveBeenCalled();

await clickOnEditButton(user);

await user.type(title, "Conversation 1");
await user.click(title);
await user.tab();
Expand All @@ -217,51 +224,4 @@ describe("ConversationPanel", () => {

expect(onCloseMock).toHaveBeenCalledOnce();
});

describe("New Conversation Button", () => {
it("should display a confirmation modal when clicking", async () => {
const user = userEvent.setup();
renderConversationPanel();

expect(
screen.queryByTestId("confirm-new-conversation-modal"),
).not.toBeInTheDocument();

const newProjectButton = screen.getByTestId("new-conversation-button");
await user.click(newProjectButton);

const modal = screen.getByTestId("confirm-new-conversation-modal");
expect(modal).toBeInTheDocument();
});

it("should call endSession and close panel after confirming", async () => {
const user = userEvent.setup();
renderConversationPanel();

const newProjectButton = screen.getByTestId("new-conversation-button");
await user.click(newProjectButton);

const confirmButton = screen.getByText("Confirm");
await user.click(confirmButton);

expect(endSessionMock).toHaveBeenCalledOnce();
expect(onCloseMock).toHaveBeenCalledOnce();
});

it("should close the modal when cancelling", async () => {
const user = userEvent.setup();
renderConversationPanel();

const newProjectButton = screen.getByTestId("new-conversation-button");
await user.click(newProjectButton);

const cancelButton = screen.getByText("Cancel");
await user.click(cancelButton);

expect(endSessionMock).not.toHaveBeenCalled();
expect(
screen.queryByTestId("confirm-new-conversation-modal"),
).not.toBeInTheDocument();
});
});
});
12 changes: 12 additions & 0 deletions frontend/__tests__/components/features/conversation-panel/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { screen, within } from "@testing-library/react";
import { UserEvent } from "@testing-library/user-event";

export const clickOnEditButton = async (user: UserEvent) => {
const ellipsisButton = screen.getByTestId("ellipsis-button");
await user.click(ellipsisButton);

const menu = screen.getByTestId("context-menu");
const editButton = within(menu).getByTestId("edit-button");

await user.click(editButton);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { renderWithProviders } from "test-utils";
import { RuntimeSizeSelector } from "#/components/shared/modals/settings/runtime-size-selector";

const renderRuntimeSizeSelector = () =>
renderWithProviders(<RuntimeSizeSelector isDisabled={false} />);

describe("RuntimeSizeSelector", () => {
it("should show both runtime size options", () => {
renderRuntimeSizeSelector();
// The options are in the hidden select element
const select = screen.getByRole("combobox", { hidden: true });
expect(select).toHaveValue("1");
expect(select).toHaveDisplayValue("1x (2 core, 8G)");
expect(select.children).toHaveLength(3); // Empty option + 2 size options
});

it("should show the full description text for disabled options", async () => {
renderRuntimeSizeSelector();

// Click the button to open the dropdown
const button = screen.getByRole("button", {
name: "1x (2 core, 8G) SETTINGS_FORM$RUNTIME_SIZE_LABEL",
});
button.click();

// Wait for the dropdown to open and find the description text
const description = await screen.findByText(
"Runtime sizes over 1 are disabled by default, please contact [email protected] to get access to larger runtimes.",
);
expect(description).toBeInTheDocument();
expect(description).toHaveClass("whitespace-normal", "break-words");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { screen, fireEvent } from "@testing-library/react";
import { describe, it, expect, vi } from "vitest";
import { renderWithProviders } from "test-utils";
import { createRoutesStub } from "react-router";
import { DEFAULT_SETTINGS } from "#/services/settings";
import { SettingsForm } from "#/components/shared/modals/settings/settings-form";
import OpenHands from "#/api/open-hands";

describe("SettingsForm", () => {
const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
getConfigSpy.mockResolvedValue({
APP_MODE: "saas",
GITHUB_CLIENT_ID: "123",
POSTHOG_CLIENT_KEY: "123",
});

const RouterStub = createRoutesStub([
{
Component: () => (
<SettingsForm
settings={DEFAULT_SETTINGS}
models={[]}
agents={[]}
securityAnalyzers={[]}
onClose={() => {}}
/>
),
path: "/",
},
]);

it("should not show runtime size selector by default", () => {
renderWithProviders(<RouterStub />);
expect(screen.queryByText("Runtime Size")).not.toBeInTheDocument();
});

it("should show runtime size selector when advanced options are enabled", async () => {
renderWithProviders(<RouterStub />);
const advancedSwitch = screen.getByRole("switch", {
name: "SETTINGS_FORM$ADVANCED_OPTIONS_LABEL",
});
fireEvent.click(advancedSwitch);
await screen.findByText("SETTINGS_FORM$RUNTIME_SIZE_LABEL");
});
});
1 change: 1 addition & 0 deletions frontend/__tests__/routes/_oh.app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe("App", () => {
getConversationSpy.mockResolvedValue({
conversation_id: "9999",
last_updated_at: "",
created_at: "",
title: "",
selected_repository: "",
status: "STOPPED",
Expand Down
8 changes: 4 additions & 4 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"@playwright/test": "^1.49.1",
"@react-router/dev": "^7.1.1",
"@tailwindcss/typography": "^0.5.15",
"@tanstack/eslint-plugin-query": "^5.62.15",
"@tanstack/eslint-plugin-query": "^5.62.16",
"@testing-library/jest-dom": "^6.6.1",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/api/open-hands.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export interface Conversation {
title: string;
selected_repository: string | null;
last_updated_at: string;
created_at: string;
status: ProjectStatus;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ export function ConfirmDeleteModal({
<div className="flex flex-col gap-2 w-full">
<ModalButton
onClick={onConfirm}
className="bg-[#4465DB]"
className="bg-danger font-bold"
text="Confirm"
/>
<ModalButton onClick={onCancel} className="bg-danger" text="Cancel" />
<ModalButton
onClick={onCancel}
className="bg-neutral-500 font-bold"
text="Cancel"
/>
</div>
</ModalBody>
</ModalBackdrop>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useClickOutsideElement } from "#/hooks/use-click-outside-element";
import { ContextMenu } from "../context-menu/context-menu";
import { ContextMenuListItem } from "../context-menu/context-menu-list-item";

interface ConversationCardContextMenuProps {
onClose: () => void;
onDelete: (event: React.MouseEvent<HTMLButtonElement>) => void;
onEdit: (event: React.MouseEvent<HTMLButtonElement>) => void;
}

export function ConversationCardContextMenu({
onClose,
onDelete,
onEdit,
}: ConversationCardContextMenuProps) {
const ref = useClickOutsideElement<HTMLUListElement>(onClose);

return (
<ContextMenu
ref={ref}
testId="context-menu"
className="left-full float-right"
>
<ContextMenuListItem testId="delete-button" onClick={onDelete}>
Delete
</ContextMenuListItem>
<ContextMenuListItem testId="edit-button" onClick={onEdit}>
Edit Title
</ContextMenuListItem>
</ContextMenu>
);
}
Loading

0 comments on commit ec900cb

Please sign in to comment.