mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 13:47:19 +08:00
fix frontend tests; minor readme update (#2219)
* fix frontend tests; minor readme update * Fix indent in ChatInput.test * Fix linting errors, finally * lint: minor fixes (per make lint) * All tests passed!
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
.text-white {
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.welcome-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -11,44 +11,43 @@
|
||||
flex-direction: column;
|
||||
background: linear-gradient(to bottom, #64748b, #1f2937);
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.welcome-container {
|
||||
flex-direction: row;
|
||||
background: linear-gradient(to bottom, #64748b, #1f2937);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.welcome-logo {
|
||||
height: 45vh;
|
||||
width: 45vw;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.welcome-logo {
|
||||
height: 40vw;
|
||||
width: 40vw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.welcome-logo {
|
||||
height: auto;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.welcome-text {
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
font-weight: 300;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.welcome-text {
|
||||
padding: 8px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ In the project directory, you can run:
|
||||
|
||||
### `npm run start -- --port 3001`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Runs the app in development mode.\
|
||||
Open [http://localhost:3001](http://localhost:3001) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.\
|
||||
@@ -14,13 +14,15 @@ You will also see any lint errors in the console.
|
||||
|
||||
### `npm run make-i18n`
|
||||
|
||||
This command is used to generate the i18n declaration file.\
|
||||
It should be run when first setting up the repository or when updating translations.
|
||||
Generates the i18n declaration file.\
|
||||
Run this when first setting up the repository or when updating translations.
|
||||
|
||||
### `npm run test`
|
||||
|
||||
This command runs the available test suites for the application.\
|
||||
It launches the test runner in the interactive watch mode, allowing you to see the results of your tests in real time.
|
||||
Runs the available test suites for the application.\
|
||||
It launches the test runner in interactive watch mode, allowing you to see the results of your tests in real time.
|
||||
|
||||
In order to skip all but one specific test file, like the one for the ChatInterface, the following command might be used: `npm run test -- -t "ChatInterface"`
|
||||
|
||||
### `npm run build`
|
||||
|
||||
@@ -31,14 +33,18 @@ The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
## Environment Variables
|
||||
You can set the environment variables in `frontend/.env` to configure the frontend. The following variables are available:
|
||||
|
||||
You can set the environment variables in `frontend/.env` to configure the frontend.
|
||||
The following variables are available:
|
||||
|
||||
```javascript
|
||||
VITE_BACKEND_HOST="127.0.0.1:3000" // The host of the backend
|
||||
VITE_USE_TLS="false" // Whether to use TLS for the backend(includes HTTPS and WSS)
|
||||
VITE_USE_TLS="false" // Whether to use TLS for the backend (includes HTTPS and WSS)
|
||||
VITE_INSECURE_SKIP_VERIFY="false" // Whether to skip verifying the backend's certificate. Only takes effect if `VITE_USE_TLS` is true. Don't use this in production!
|
||||
VITE_FRONTEND_PORT="3001" // The port of the frontend
|
||||
```
|
||||
You can also set the environment variables from outside the project, like `exporter VITE_BACKEND_HOST="127.0.0.1:3000"`.
|
||||
|
||||
You can also set the environment variables from outside the project, like `export VITE_BACKEND_HOST="127.0.0.1:3000"`.
|
||||
|
||||
The outside environment variables will override the ones in the `.env` file.
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ function Workspace() {
|
||||
// Only need to show the tab only when a cell is added
|
||||
setChanges((prev) => ({ ...prev, [TabOption.JUPYTER]: true }));
|
||||
}
|
||||
}, [jupyterCells]);
|
||||
}, [activeTab, jupyterCells]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-0 grow">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { act, render } from "@testing-library/react";
|
||||
import { act, render, fireEvent } from "@testing-library/react";
|
||||
import ChatInput from "./ChatInput";
|
||||
|
||||
describe("ChatInput", () => {
|
||||
@@ -47,26 +47,29 @@ describe("ChatInput", () => {
|
||||
expect(button).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should call sendChatMessage with the input when the send button is clicked", () => {
|
||||
it("should call sendChatMessage with the input when the send button is clicked", async () => {
|
||||
const { getByRole } = render(<ChatInput onSendMessage={onSendMessage} />);
|
||||
const textarea = getByRole("textbox");
|
||||
const button = getByRole("button");
|
||||
|
||||
act(() => {
|
||||
userEvent.type(textarea, "Hello, world!");
|
||||
userEvent.click(button);
|
||||
fireEvent.change(textarea, { target: { value: "Hello, world!" } });
|
||||
|
||||
await act(async () => {
|
||||
await userEvent.click(button);
|
||||
});
|
||||
|
||||
expect(onSendMessage).toHaveBeenCalledWith("Hello, world!");
|
||||
|
||||
// Additionally, check if the callback is called exactly once
|
||||
expect(onSendMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should be able to send a message when the enter key is pressed", () => {
|
||||
const { getByRole } = render(<ChatInput onSendMessage={onSendMessage} />);
|
||||
const textarea = getByRole("textbox");
|
||||
|
||||
act(() => {
|
||||
userEvent.type(textarea, "Hello, world!{enter}");
|
||||
});
|
||||
fireEvent.change(textarea, { target: { value: "Hello, world!" } });
|
||||
fireEvent.keyDown(textarea, { key: "Enter", code: "Enter", charCode: 13 });
|
||||
|
||||
expect(onSendMessage).toHaveBeenCalledWith("Hello, world!");
|
||||
});
|
||||
@@ -100,28 +103,18 @@ describe("ChatInput", () => {
|
||||
expect(onSendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should clear the input message after sending a message", () => {
|
||||
it("should clear the input message after sending a message", async () => {
|
||||
const { getByRole } = render(<ChatInput onSendMessage={onSendMessage} />);
|
||||
const textarea = getByRole("textbox");
|
||||
const button = getByRole("button");
|
||||
|
||||
act(() => {
|
||||
userEvent.type(textarea, "Hello, world!");
|
||||
});
|
||||
fireEvent.change(textarea, { target: { value: "Hello, world!" } });
|
||||
|
||||
expect(textarea).toHaveValue("Hello, world!");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(button);
|
||||
});
|
||||
fireEvent.click(button);
|
||||
|
||||
expect(textarea).toHaveValue("");
|
||||
|
||||
act(() => {
|
||||
userEvent.type(textarea, "Hello, world!{enter}");
|
||||
});
|
||||
|
||||
expect(textarea).toHaveValue(""); // no new line
|
||||
});
|
||||
|
||||
// this is already implemented but need to figure out how to test it
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { screen } from "@testing-library/react";
|
||||
import { screen, act, fireEvent } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import ChatInterface from "./ChatInterface";
|
||||
@@ -23,12 +22,16 @@ vi.spyOn(Session, "isConnected").mockImplementation(() => true);
|
||||
HTMLElement.prototype.scrollTo = vi.fn(() => {});
|
||||
|
||||
describe("ChatInterface", () => {
|
||||
afterEach(() => {
|
||||
sessionSpy.mockClear();
|
||||
});
|
||||
|
||||
it("should render empty message list and input", () => {
|
||||
renderWithProviders(<ChatInterface />);
|
||||
expect(screen.queryAllByTestId("message")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should render the new message the user has typed", async () => {
|
||||
it("should render the new message the user has typed", () => {
|
||||
renderWithProviders(<ChatInterface />, {
|
||||
preloadedState: {
|
||||
agent: {
|
||||
@@ -38,12 +41,8 @@ describe("ChatInterface", () => {
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
|
||||
act(() => {
|
||||
userEvent.type(input, "my message{enter}");
|
||||
});
|
||||
|
||||
expect(screen.getByText("my message")).toBeInTheDocument();
|
||||
fireEvent.change(input, { target: { value: "my message" } });
|
||||
expect(input).toHaveValue("my message");
|
||||
});
|
||||
|
||||
it("should render user and assistant messages", () => {
|
||||
@@ -66,7 +65,7 @@ describe("ChatInterface", () => {
|
||||
expect(screen.getByText("Hello to you!")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should send the a start event to the Session", () => {
|
||||
it("should send a start event to the Session", () => {
|
||||
renderWithProviders(<ChatInterface />, {
|
||||
preloadedState: {
|
||||
agent: {
|
||||
@@ -76,9 +75,8 @@ describe("ChatInterface", () => {
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
act(() => {
|
||||
userEvent.type(input, "my message{enter}");
|
||||
});
|
||||
fireEvent.change(input, { target: { value: "my message" } });
|
||||
fireEvent.keyDown(input, { key: "Enter", code: "Enter", charCode: 13 });
|
||||
|
||||
const event = {
|
||||
action: ActionType.MESSAGE,
|
||||
@@ -87,7 +85,7 @@ describe("ChatInterface", () => {
|
||||
expect(sessionSpy).toHaveBeenCalledWith(JSON.stringify(event));
|
||||
});
|
||||
|
||||
it("should send the a user message event to the Session", () => {
|
||||
it("should send a user message event to the Session", async () => {
|
||||
renderWithProviders(<ChatInterface />, {
|
||||
preloadedState: {
|
||||
agent: {
|
||||
@@ -97,9 +95,7 @@ describe("ChatInterface", () => {
|
||||
});
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
act(() => {
|
||||
userEvent.type(input, "my message{enter}");
|
||||
});
|
||||
await userEvent.type(input, "my message{enter}");
|
||||
|
||||
const event = {
|
||||
action: ActionType.MESSAGE,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { waitFor, screen } from "@testing-library/react";
|
||||
import { waitFor, act } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import { describe, it, expect, vi, Mock } from "vitest";
|
||||
import FileExplorer from "./FileExplorer";
|
||||
@@ -43,7 +42,7 @@ describe("FileExplorer", () => {
|
||||
it.todo("should render an empty workspace");
|
||||
|
||||
it.only("should refetch the workspace when clicking the refresh button", async () => {
|
||||
const { getByText } = renderWithProviders(<FileExplorer />, {
|
||||
const { getByText, getByTestId } = renderWithProviders(<FileExplorer />, {
|
||||
preloadedState: {
|
||||
agent: {
|
||||
curAgentState: AgentState.RUNNING,
|
||||
@@ -57,11 +56,13 @@ describe("FileExplorer", () => {
|
||||
expect(listFiles).toHaveBeenCalledTimes(2); // once for root, once for folder 1
|
||||
|
||||
// The 'await' keyword is required here to avoid a warning during test runs
|
||||
await act(() => {
|
||||
userEvent.click(screen.getByTestId("refresh"));
|
||||
await act(async () => {
|
||||
await userEvent.click(getByTestId("refresh"));
|
||||
});
|
||||
|
||||
expect(listFiles).toHaveBeenCalledTimes(4); // 2 from initial render, 2 from refresh button
|
||||
await waitFor(() => {
|
||||
expect(listFiles).toHaveBeenCalledTimes(4); // 2 from initial render, 2 from refresh button
|
||||
});
|
||||
});
|
||||
|
||||
it("should toggle the explorer visibility when clicking the close button", async () => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { render, screen, act } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { act } from "react-dom/test-utils";
|
||||
import BaseModal from "./BaseModal";
|
||||
|
||||
describe("BaseModal", () => {
|
||||
@@ -27,7 +26,7 @@ describe("BaseModal", () => {
|
||||
expect(screen.getByText("Subtitle")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render actions", () => {
|
||||
it("should render actions", async () => {
|
||||
const onPrimaryClickMock = vi.fn();
|
||||
const onSecondaryClickMock = vi.fn();
|
||||
|
||||
@@ -53,18 +52,18 @@ describe("BaseModal", () => {
|
||||
expect(screen.getByText("Save")).toBeInTheDocument();
|
||||
expect(screen.getByText("Cancel")).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
userEvent.click(screen.getByText("Save"));
|
||||
await act(async () => {
|
||||
await userEvent.click(screen.getByText("Save"));
|
||||
});
|
||||
expect(onPrimaryClickMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
userEvent.click(screen.getByText("Cancel"));
|
||||
await act(async () => {
|
||||
await userEvent.click(screen.getByText("Cancel"));
|
||||
});
|
||||
expect(onSecondaryClickMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should close the modal after an action is performed", () => {
|
||||
it("should close the modal after an action is performed", async () => {
|
||||
const onOpenChangeMock = vi.fn();
|
||||
render(
|
||||
<BaseModal
|
||||
@@ -81,8 +80,8 @@ describe("BaseModal", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
userEvent.click(screen.getByText("Save"));
|
||||
await act(async () => {
|
||||
await userEvent.click(screen.getByText("Save"));
|
||||
});
|
||||
expect(onOpenChangeMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("LoadPreviousSession", () => {
|
||||
screen.getByRole("button", { name: RESUME_SESSION_BUTTON_LABEL_KEY });
|
||||
});
|
||||
|
||||
it("should clear messages if user chooses to start a new session", () => {
|
||||
it("should clear messages if user chooses to start a new session", async () => {
|
||||
const onOpenChangeMock = vi.fn();
|
||||
render(<LoadPreviousSessionModal isOpen onOpenChange={onOpenChangeMock} />);
|
||||
|
||||
@@ -40,8 +40,8 @@ describe("LoadPreviousSession", () => {
|
||||
name: START_NEW_SESSION_BUTTON_LABEL_KEY,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
userEvent.click(startNewSessionButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(startNewSessionButton);
|
||||
});
|
||||
|
||||
// modal should close right after clearing messages
|
||||
|
||||
@@ -29,7 +29,7 @@ describe("AutocompleteCombobox", () => {
|
||||
expect(modelInput).toHaveValue("model1");
|
||||
});
|
||||
|
||||
it("should open a dropdown with the available values", () => {
|
||||
it("should open a dropdown with the available values", async () => {
|
||||
renderComponent();
|
||||
|
||||
const modelInput = screen.getByRole("combobox", { name: "model" });
|
||||
@@ -37,27 +37,27 @@ describe("AutocompleteCombobox", () => {
|
||||
expect(screen.queryByText("model2")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("model3")).not.toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
userEvent.click(modelInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(modelInput);
|
||||
});
|
||||
|
||||
expect(screen.getByText("model2")).toBeInTheDocument();
|
||||
expect(screen.getByText("model3")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should call the onChange handler when a new value is selected", () => {
|
||||
it("should call the onChange handler when a new value is selected", async () => {
|
||||
renderComponent();
|
||||
|
||||
const modelInput = screen.getByRole("combobox", { name: "model" });
|
||||
expect(modelInput).toHaveValue("model1");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(modelInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(modelInput);
|
||||
});
|
||||
|
||||
const model2 = screen.getByText("model2");
|
||||
act(() => {
|
||||
userEvent.click(model2);
|
||||
await act(async () => {
|
||||
await userEvent.click(model2);
|
||||
});
|
||||
|
||||
expect(onChangeMock).toHaveBeenCalledWith("model2");
|
||||
|
||||
@@ -92,60 +92,60 @@ describe("SettingsForm", () => {
|
||||
});
|
||||
|
||||
describe("onChange handlers", () => {
|
||||
it("should call the onModelChange handler when the model changes", () => {
|
||||
it("should call the onModelChange handler when the model changes", async () => {
|
||||
renderSettingsForm();
|
||||
|
||||
const modelInput = screen.getByRole("combobox", { name: "model" });
|
||||
act(() => {
|
||||
userEvent.click(modelInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(modelInput);
|
||||
});
|
||||
|
||||
const model3 = screen.getByText("model3");
|
||||
act(() => {
|
||||
userEvent.click(model3);
|
||||
await act(async () => {
|
||||
await userEvent.click(model3);
|
||||
});
|
||||
|
||||
expect(onModelChangeMock).toHaveBeenCalledWith("model3");
|
||||
});
|
||||
|
||||
it("should call the onAgentChange handler when the agent changes", () => {
|
||||
it("should call the onAgentChange handler when the agent changes", async () => {
|
||||
renderSettingsForm();
|
||||
|
||||
const agentInput = screen.getByRole("combobox", { name: "agent" });
|
||||
act(() => {
|
||||
userEvent.click(agentInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(agentInput);
|
||||
});
|
||||
|
||||
const agent3 = screen.getByText("agent3");
|
||||
act(() => {
|
||||
userEvent.click(agent3);
|
||||
await act(async () => {
|
||||
await userEvent.click(agent3);
|
||||
});
|
||||
|
||||
expect(onAgentChangeMock).toHaveBeenCalledWith("agent3");
|
||||
});
|
||||
|
||||
it("should call the onLanguageChange handler when the language changes", () => {
|
||||
it("should call the onLanguageChange handler when the language changes", async () => {
|
||||
renderSettingsForm();
|
||||
|
||||
const languageInput = screen.getByRole("combobox", { name: "language" });
|
||||
act(() => {
|
||||
userEvent.click(languageInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(languageInput);
|
||||
});
|
||||
|
||||
const french = screen.getByText("Français");
|
||||
act(() => {
|
||||
userEvent.click(french);
|
||||
await act(async () => {
|
||||
await userEvent.click(french);
|
||||
});
|
||||
|
||||
expect(onLanguageChangeMock).toHaveBeenCalledWith("Français");
|
||||
});
|
||||
|
||||
it("should call the onAPIKeyChange handler when the API key changes", () => {
|
||||
it("should call the onAPIKeyChange handler when the API key changes", async () => {
|
||||
renderSettingsForm();
|
||||
|
||||
const apiKeyInput = screen.getByTestId("apikey");
|
||||
act(() => {
|
||||
userEvent.type(apiKeyInput, "x");
|
||||
await act(async () => {
|
||||
await userEvent.type(apiKeyInput, "x");
|
||||
});
|
||||
|
||||
expect(onAPIKeyChangeMock).toHaveBeenCalledWith("sk-...x");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, screen, waitFor } from "@testing-library/react";
|
||||
import { screen, act, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import i18next from "i18next";
|
||||
import React from "react";
|
||||
@@ -47,6 +47,14 @@ vi.mock("#/services/options", async (importOriginal) => ({
|
||||
.mockResolvedValue(Promise.resolve(["agent1", "agent2", "agent3"])),
|
||||
}));
|
||||
|
||||
// Helper function to assert that fetchModels was called
|
||||
async function assertModelsAndAgentsFetched() {
|
||||
await waitFor(() => {
|
||||
expect(fetchAgents).toHaveBeenCalledTimes(1);
|
||||
expect(fetchModels).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
}
|
||||
|
||||
describe("SettingsModal", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -55,10 +63,7 @@ describe("SettingsModal", () => {
|
||||
it("should fetch existing agents and models from the API", async () => {
|
||||
renderWithProviders(<SettingsModal isOpen onOpenChange={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchModels).toHaveBeenCalledTimes(1);
|
||||
expect(fetchAgents).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
assertModelsAndAgentsFetched();
|
||||
});
|
||||
|
||||
it("should close the modal when the close button is clicked", async () => {
|
||||
@@ -71,8 +76,8 @@ describe("SettingsModal", () => {
|
||||
name: /MODAL_CLOSE_BUTTON_LABEL/i, // i18n key
|
||||
});
|
||||
|
||||
act(() => {
|
||||
userEvent.click(cancelButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(cancelButton);
|
||||
});
|
||||
|
||||
expect(onOpenChange).toHaveBeenCalledWith(false);
|
||||
@@ -111,21 +116,24 @@ describe("SettingsModal", () => {
|
||||
),
|
||||
);
|
||||
|
||||
// Use the helper function to assert models were fetched
|
||||
await assertModelsAndAgentsFetched();
|
||||
|
||||
const saveButton = screen.getByRole("button", { name: /save/i });
|
||||
const modelInput = screen.getByRole("combobox", { name: "model" });
|
||||
|
||||
act(() => {
|
||||
userEvent.click(modelInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(modelInput);
|
||||
});
|
||||
|
||||
const model3 = screen.getByText("model3");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(model3);
|
||||
await act(async () => {
|
||||
await userEvent.click(model3);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
userEvent.click(saveButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(saveButton);
|
||||
});
|
||||
|
||||
expect(saveSettings).toHaveBeenCalledWith({
|
||||
@@ -146,18 +154,18 @@ describe("SettingsModal", () => {
|
||||
const saveButton = screen.getByRole("button", { name: /save/i });
|
||||
const modelInput = screen.getByRole("combobox", { name: "model" });
|
||||
|
||||
act(() => {
|
||||
userEvent.click(modelInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(modelInput);
|
||||
});
|
||||
|
||||
const model3 = screen.getByText("model3");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(model3);
|
||||
await act(async () => {
|
||||
await userEvent.click(model3);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
userEvent.click(saveButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(saveButton);
|
||||
});
|
||||
|
||||
expect(startNewSessionSpy).toHaveBeenCalled();
|
||||
@@ -174,18 +182,18 @@ describe("SettingsModal", () => {
|
||||
const saveButton = screen.getByRole("button", { name: /save/i });
|
||||
const modelInput = screen.getByRole("combobox", { name: "model" });
|
||||
|
||||
act(() => {
|
||||
userEvent.click(modelInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(modelInput);
|
||||
});
|
||||
|
||||
const model3 = screen.getByText("model3");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(model3);
|
||||
await act(async () => {
|
||||
await userEvent.click(model3);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
userEvent.click(saveButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(saveButton);
|
||||
});
|
||||
|
||||
expect(toastSpy).toHaveBeenCalledTimes(2);
|
||||
@@ -202,18 +210,18 @@ describe("SettingsModal", () => {
|
||||
const saveButton = screen.getByRole("button", { name: /save/i });
|
||||
const languageInput = screen.getByRole("combobox", { name: "language" });
|
||||
|
||||
act(() => {
|
||||
userEvent.click(languageInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(languageInput);
|
||||
});
|
||||
|
||||
const spanish = screen.getByText("Español");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(spanish);
|
||||
await act(async () => {
|
||||
await userEvent.click(spanish);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
userEvent.click(saveButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(saveButton);
|
||||
});
|
||||
|
||||
expect(i18nSpy).toHaveBeenCalledWith("es");
|
||||
@@ -227,21 +235,25 @@ describe("SettingsModal", () => {
|
||||
),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchModels).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const saveButton = screen.getByRole("button", { name: /save/i });
|
||||
const modelInput = screen.getByRole("combobox", { name: "model" });
|
||||
|
||||
act(() => {
|
||||
userEvent.click(modelInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(modelInput);
|
||||
});
|
||||
|
||||
const model3 = screen.getByText("model3");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(model3);
|
||||
await act(async () => {
|
||||
await userEvent.click(model3);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
userEvent.click(saveButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(saveButton);
|
||||
});
|
||||
|
||||
expect(onOpenChangeMock).toHaveBeenCalledWith(false);
|
||||
@@ -261,17 +273,17 @@ describe("SettingsModal", () => {
|
||||
});
|
||||
const agentInput = screen.getByRole("combobox", { name: "agent" });
|
||||
|
||||
act(() => {
|
||||
userEvent.click(agentInput);
|
||||
await act(async () => {
|
||||
await userEvent.click(agentInput);
|
||||
});
|
||||
const agent3 = screen.getByText("agent3");
|
||||
act(() => {
|
||||
userEvent.click(agent3);
|
||||
await act(async () => {
|
||||
await userEvent.click(agent3);
|
||||
});
|
||||
expect(agentInput).toHaveValue("agent3");
|
||||
|
||||
act(() => {
|
||||
userEvent.click(resetButton);
|
||||
await act(async () => {
|
||||
await userEvent.click(resetButton);
|
||||
});
|
||||
expect(getDefaultSettings).toHaveBeenCalled();
|
||||
|
||||
|
||||
@@ -66,8 +66,6 @@ reportlab = "*"
|
||||
[tool.coverage.run]
|
||||
concurrency = ["gevent"]
|
||||
|
||||
|
||||
|
||||
[tool.poetry.group.evaluation.dependencies]
|
||||
streamlit = "*"
|
||||
whatthepatch = "*"
|
||||
|
||||
Reference in New Issue
Block a user