diff --git a/frontend/__tests__/components/chat/chat-interface.test.tsx b/frontend/__tests__/components/chat/chat-interface.test.tsx
index 792a4c4fa7..c018e010e9 100644
--- a/frontend/__tests__/components/chat/chat-interface.test.tsx
+++ b/frontend/__tests__/components/chat/chat-interface.test.tsx
@@ -61,38 +61,6 @@ vi.mock("#/hooks/use-conversation-name-context-menu", () => ({
}),
}));
-vi.mock("react-redux", async () => {
- const actual = await vi.importActual("react-redux");
- return {
- ...actual,
- useSelector: vi.fn((selector) => {
- // Create a mock state object
- const mockState = {
- agent: {
- curAgentState: "AWAITING_USER_INPUT",
- },
- initialQuery: {
- selectedRepository: null,
- replayJson: null,
- },
- conversation: {
- messageToSend: null,
- files: [],
- images: [],
- loadingFiles: [],
- loadingImages: [],
- },
- status: {
- curStatusMessage: null,
- },
- };
-
- // Execute the selector function with our mock state
- return selector(mockState);
- }),
- useDispatch: vi.fn(() => vi.fn()),
- };
-});
// Helper function to render with Router context
const renderChatInterfaceWithRouter = () =>
diff --git a/frontend/__tests__/components/features/conversation-panel/conversation-panel.test.tsx b/frontend/__tests__/components/features/conversation-panel/conversation-panel.test.tsx
index 7df58fab97..8250647169 100644
--- a/frontend/__tests__/components/features/conversation-panel/conversation-panel.test.tsx
+++ b/frontend/__tests__/components/features/conversation-panel/conversation-panel.test.tsx
@@ -3,7 +3,7 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import { createRoutesStub } from "react-router";
import React from "react";
-import { renderWithQueryAndI18n } from "test-utils";
+import { renderWithProviders } from "test-utils";
import { ConversationPanel } from "#/components/features/conversation-panel/conversation-panel";
import ConversationService from "#/api/conversation-service/conversation-service.api";
import { Conversation } from "#/api/open-hands.types";
@@ -17,7 +17,7 @@ describe("ConversationPanel", () => {
},
]);
- const renderConversationPanel = () => renderWithQueryAndI18n();
+ const renderConversationPanel = () => renderWithProviders();
beforeAll(() => {
vi.mock("react-router", async (importOriginal) => ({
@@ -287,7 +287,7 @@ describe("ConversationPanel", () => {
},
]);
- renderWithQueryAndI18n();
+ renderWithProviders();
const toggleButton = screen.getByText("Toggle");
diff --git a/frontend/__tests__/components/features/conversation/server-status.test.tsx b/frontend/__tests__/components/features/conversation/server-status.test.tsx
index e5f5a23127..b8fc40369e 100644
--- a/frontend/__tests__/components/features/conversation/server-status.test.tsx
+++ b/frontend/__tests__/components/features/conversation/server-status.test.tsx
@@ -1,7 +1,7 @@
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
-import { renderWithQueryAndI18n } from "test-utils";
+import { renderWithProviders } from "test-utils";
import { ServerStatus } from "#/components/features/controls/server-status";
import { ServerStatusContextMenu } from "#/components/features/controls/server-status-context-menu";
import { ConversationStatus } from "#/types/conversation-status";
@@ -84,7 +84,7 @@ describe("ServerStatus", () => {
mockAgentStore(AgentState.RUNNING);
// Test RUNNING status
- const { rerender } = renderWithQueryAndI18n(
+ const { rerender } = renderWithProviders(
,
);
expect(screen.getByText("Running")).toBeInTheDocument();
@@ -108,7 +108,7 @@ describe("ServerStatus", () => {
// Mock agent store to return RUNNING state
mockAgentStore(AgentState.RUNNING);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusContainer = screen.getByText("Running").closest("div");
expect(statusContainer).toBeInTheDocument();
@@ -128,7 +128,7 @@ describe("ServerStatus", () => {
// Mock agent store to return STOPPED state
mockAgentStore(AgentState.STOPPED);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusContainer = screen.getByText("Server Stopped").closest("div");
expect(statusContainer).toBeInTheDocument();
@@ -148,7 +148,7 @@ describe("ServerStatus", () => {
// Mock agent store to return RUNNING state
mockAgentStore(AgentState.RUNNING);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusContainer = screen.getByText("Running").closest("div");
expect(statusContainer).toBeInTheDocument();
@@ -170,7 +170,7 @@ describe("ServerStatus", () => {
// Mock agent store to return RUNNING state
mockAgentStore(AgentState.RUNNING);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusContainer = screen.getByText("Running").closest("div");
await user.click(statusContainer!);
@@ -192,7 +192,7 @@ describe("ServerStatus", () => {
// Mock agent store to return STOPPED state
mockAgentStore(AgentState.STOPPED);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusContainer = screen.getByText("Server Stopped").closest("div");
await user.click(statusContainer!);
@@ -212,7 +212,7 @@ describe("ServerStatus", () => {
// Mock agent store to return RUNNING state
mockAgentStore(AgentState.RUNNING);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusContainer = screen.getByText("Running").closest("div");
await user.click(statusContainer!);
@@ -232,7 +232,7 @@ describe("ServerStatus", () => {
// Mock agent store to return STOPPED state
mockAgentStore(AgentState.STOPPED);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusContainer = screen.getByText("Server Stopped").closest("div");
await user.click(statusContainer!);
@@ -250,7 +250,7 @@ describe("ServerStatus", () => {
// Mock agent store to return RUNNING state
mockAgentStore(AgentState.RUNNING);
- renderWithQueryAndI18n();
+ renderWithProviders();
const statusText = screen.getByText("Running");
expect(statusText).toBeInTheDocument();
@@ -268,7 +268,7 @@ describe("ServerStatusContextMenu", () => {
});
it("should render stop server button when status is RUNNING", () => {
- renderWithQueryAndI18n(
+ renderWithProviders(
{
});
it("should render start server button when status is STOPPED", () => {
- renderWithQueryAndI18n(
+ renderWithProviders(
{
});
it("should not render stop server button when onStopServer is not provided", () => {
- renderWithQueryAndI18n(
+ renderWithProviders(
{
});
it("should not render start server button when onStartServer is not provided", () => {
- renderWithQueryAndI18n(
+ renderWithProviders(
{
const user = userEvent.setup();
const onStopServer = vi.fn();
- renderWithQueryAndI18n(
+ renderWithProviders(
{
const user = userEvent.setup();
const onStartServer = vi.fn();
- renderWithQueryAndI18n(
+ renderWithProviders(
{
});
it("should render correct text content for stop server button", () => {
- renderWithQueryAndI18n(
+ renderWithProviders(
{
});
it("should render correct text content for start server button", () => {
- renderWithQueryAndI18n(
+ renderWithProviders(
{
it("should call onClose when context menu is closed", () => {
const onClose = vi.fn();
- renderWithQueryAndI18n(
+ renderWithProviders(
{
});
it("should not render any buttons for other conversation statuses", () => {
- renderWithQueryAndI18n(
+ renderWithProviders(
{
const renderHomeHeader = () => {
return render(, {
wrapper: ({ children }) => (
-
-
- {children}
-
-
+
+ {children}
+
),
});
};
diff --git a/frontend/__tests__/components/features/home/new-conversation.test.tsx b/frontend/__tests__/components/features/home/new-conversation.test.tsx
index eb0c7167c0..d947f2a017 100644
--- a/frontend/__tests__/components/features/home/new-conversation.test.tsx
+++ b/frontend/__tests__/components/features/home/new-conversation.test.tsx
@@ -1,8 +1,6 @@
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { render, screen } from "@testing-library/react";
-import { Provider } from "react-redux";
import { createRoutesStub } from "react-router";
-import { setupStore } from "test-utils";
import { describe, expect, it, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import ConversationService from "#/api/conversation-service/conversation-service.api";
@@ -43,11 +41,9 @@ const renderNewConversation = () => {
return render(, {
wrapper: ({ children }) => (
-
-
- {children}
-
-
+
+ {children}
+
),
});
};
diff --git a/frontend/__tests__/components/features/home/repo-connector.test.tsx b/frontend/__tests__/components/features/home/repo-connector.test.tsx
index d04be1bb5a..7948c6d112 100644
--- a/frontend/__tests__/components/features/home/repo-connector.test.tsx
+++ b/frontend/__tests__/components/features/home/repo-connector.test.tsx
@@ -2,8 +2,6 @@ import { render, screen, waitFor, within } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
-import { setupStore } from "test-utils";
-import { Provider } from "react-redux";
import { createRoutesStub, Outlet } from "react-router";
import SettingsService from "#/settings-service/settings-service.api";
import ConversationService from "#/api/conversation-service/conversation-service.api";
@@ -42,11 +40,9 @@ const renderRepoConnector = () => {
return render(, {
wrapper: ({ children }) => (
-
-
- {children}
-
-
+
+ {children}
+
),
});
};
diff --git a/frontend/__tests__/components/features/home/task-card.test.tsx b/frontend/__tests__/components/features/home/task-card.test.tsx
index 742a31784f..6d8fb0ee63 100644
--- a/frontend/__tests__/components/features/home/task-card.test.tsx
+++ b/frontend/__tests__/components/features/home/task-card.test.tsx
@@ -2,9 +2,7 @@ import { render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import userEvent from "@testing-library/user-event";
-import { Provider } from "react-redux";
import { createRoutesStub } from "react-router";
-import { setupStore } from "test-utils";
import ConversationService from "#/api/conversation-service/conversation-service.api";
import UserService from "#/api/user-service/user-service.api";
import GitService from "#/api/git-service/git-service.api";
@@ -41,11 +39,9 @@ const renderTaskCard = (task = MOCK_TASK_1) => {
return render(, {
wrapper: ({ children }) => (
-
-
- {children}
-
-
+
+ {children}
+
),
});
};
diff --git a/frontend/__tests__/components/features/home/task-suggestions.test.tsx b/frontend/__tests__/components/features/home/task-suggestions.test.tsx
index c1ebb42b9d..d1fcb84db9 100644
--- a/frontend/__tests__/components/features/home/task-suggestions.test.tsx
+++ b/frontend/__tests__/components/features/home/task-suggestions.test.tsx
@@ -1,9 +1,7 @@
import { render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { Provider } from "react-redux";
import { createRoutesStub } from "react-router";
-import { setupStore } from "test-utils";
import { TaskSuggestions } from "#/components/features/home/tasks/task-suggestions";
import { SuggestionsService } from "#/api/suggestions-service/suggestions-service.api";
import { MOCK_TASKS } from "#/mocks/task-suggestions-handlers";
@@ -62,11 +60,9 @@ const renderTaskSuggestions = () => {
return render(, {
wrapper: ({ children }) => (
-
-
- {children}
-
-
+
+ {children}
+
),
});
};
diff --git a/frontend/__tests__/components/modals/microagents/microagent-modal.test.tsx b/frontend/__tests__/components/modals/microagents/microagent-modal.test.tsx
index f7ae95c765..1dc6961345 100644
--- a/frontend/__tests__/components/modals/microagents/microagent-modal.test.tsx
+++ b/frontend/__tests__/components/modals/microagents/microagent-modal.test.tsx
@@ -1,7 +1,7 @@
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
-import { renderWithQueryAndI18n } from "test-utils";
+import { renderWithProviders } from "test-utils";
import { MicroagentsModal } from "#/components/features/conversation-panel/microagents-modal";
import ConversationService from "#/api/conversation-service/conversation-service.api";
import { AgentState } from "#/types/agent-state";
@@ -64,7 +64,7 @@ describe("MicroagentsModal - Refresh Button", () => {
describe("Refresh Button Rendering", () => {
it("should render the refresh button with correct text and test ID", async () => {
- renderWithQueryAndI18n();
+ renderWithProviders();
// Wait for the component to load and render the refresh button
const refreshButton = await screen.findByTestId("refresh-microagents");
@@ -77,7 +77,7 @@ describe("MicroagentsModal - Refresh Button", () => {
it("should call refetch when refresh button is clicked", async () => {
const user = userEvent.setup();
- renderWithQueryAndI18n();
+ renderWithProviders();
const refreshSpy = vi.spyOn(ConversationService, "getMicroagents");
diff --git a/frontend/__tests__/hooks/use-terminal.test.tsx b/frontend/__tests__/hooks/use-terminal.test.tsx
index cdaa19404c..898eed5334 100644
--- a/frontend/__tests__/hooks/use-terminal.test.tsx
+++ b/frontend/__tests__/hooks/use-terminal.test.tsx
@@ -3,7 +3,7 @@ import { afterEach } from "node:test";
import { useTerminal } from "#/hooks/use-terminal";
import { Command, useCommandStore } from "#/state/command-store";
import { AgentState } from "#/types/agent-state";
-import { renderWithQueryAndI18n } from "../../test-utils";
+import { renderWithProviders } from "../../test-utils";
import { useAgentStore } from "#/stores/agent-store";
// Mock the WsClient context
@@ -60,7 +60,7 @@ describe("useTerminal", () => {
});
it("should render", () => {
- renderWithQueryAndI18n();
+ renderWithProviders();
});
it("should render the commands in the terminal", () => {
@@ -69,7 +69,7 @@ describe("useTerminal", () => {
{ content: "hello", type: "output" },
];
- renderWithQueryAndI18n();
+ renderWithProviders();
expect(mockTerminal.writeln).toHaveBeenNthCalledWith(1, "echo hello");
expect(mockTerminal.writeln).toHaveBeenNthCalledWith(2, "hello");
@@ -87,7 +87,7 @@ describe("useTerminal", () => {
{ content: secret, type: "output" },
];
- renderWithQueryAndI18n();
+ renderWithProviders();
// This test is no longer relevant as secrets filtering has been removed
});
diff --git a/frontend/__tests__/routes/home-screen.test.tsx b/frontend/__tests__/routes/home-screen.test.tsx
index 924af1401d..a515f670be 100644
--- a/frontend/__tests__/routes/home-screen.test.tsx
+++ b/frontend/__tests__/routes/home-screen.test.tsx
@@ -3,8 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import userEvent from "@testing-library/user-event";
import { createRoutesStub } from "react-router";
-import { Provider } from "react-redux";
-import { createAxiosNotFoundErrorObject, setupStore } from "test-utils";
+import { createAxiosNotFoundErrorObject } from "test-utils";
import HomeScreen from "#/routes/home";
import { GitRepository } from "#/types/git";
import SettingsService from "#/settings-service/settings-service.api";
@@ -66,11 +65,9 @@ const selectRepository = async (repoName: string) => {
const renderHomeScreen = () =>
render(, {
wrapper: ({ children }) => (
-
-
- {children}
-
-
+
+ {children}
+
),
});
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 2cfe481a17..72b7dbede6 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -15,7 +15,6 @@
"@react-router/node": "^7.9.1",
"@react-router/serve": "^7.9.1",
"@react-types/shared": "^3.32.0",
- "@reduxjs/toolkit": "^2.9.0",
"@stripe/react-stripe-js": "^4.0.2",
"@stripe/stripe-js": "^7.9.0",
"@tailwindcss/postcss": "^4.1.13",
@@ -47,7 +46,6 @@
"react-i18next": "^15.7.2",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
- "react-redux": "^9.2.0",
"react-router": "^7.9.1",
"react-syntax-highlighter": "^15.6.6",
"remark-breaks": "^4.0.0",
@@ -4977,32 +4975,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@reduxjs/toolkit": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.9.0.tgz",
- "integrity": "sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==",
- "license": "MIT",
- "dependencies": {
- "@standard-schema/spec": "^1.0.0",
- "@standard-schema/utils": "^0.3.0",
- "immer": "^10.0.3",
- "redux": "^5.0.1",
- "redux-thunk": "^3.1.0",
- "reselect": "^5.1.0"
- },
- "peerDependencies": {
- "react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
- "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
- },
- "peerDependenciesMeta": {
- "react": {
- "optional": true
- },
- "react-redux": {
- "optional": true
- }
- }
- },
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.35",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.35.tgz",
@@ -5337,18 +5309,6 @@
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"license": "MIT"
},
- "node_modules/@standard-schema/spec": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
- "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
- "license": "MIT"
- },
- "node_modules/@standard-schema/utils": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
- "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
- "license": "MIT"
- },
"node_modules/@stripe/react-stripe-js": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-4.0.2.tgz",
@@ -6350,12 +6310,6 @@
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT"
},
- "node_modules/@types/use-sync-external-store": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
- "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
- "license": "MIT"
- },
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
@@ -10830,6 +10784,8 @@
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.3.tgz",
"integrity": "sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==",
"license": "MIT",
+ "optional": true,
+ "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -14754,29 +14710,6 @@
"react": ">=18"
}
},
- "node_modules/react-redux": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
- "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
- "license": "MIT",
- "dependencies": {
- "@types/use-sync-external-store": "^0.0.6",
- "use-sync-external-store": "^1.4.0"
- },
- "peerDependencies": {
- "@types/react": "^18.2.25 || ^19",
- "react": "^18.0 || ^19",
- "redux": "^5.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "redux": {
- "optional": true
- }
- }
- },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -14879,21 +14812,6 @@
"node": ">=8"
}
},
- "node_modules/redux": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
- "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
- "license": "MIT"
- },
- "node_modules/redux-thunk": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
- "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
- "license": "MIT",
- "peerDependencies": {
- "redux": "^5.0.0"
- }
- },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -15164,12 +15082,6 @@
"node": ">=0.10.5"
}
},
- "node_modules/reselect": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
- "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
- "license": "MIT"
- },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 8fa353c8c0..d41617c79e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,7 +14,6 @@
"@react-router/node": "^7.9.1",
"@react-router/serve": "^7.9.1",
"@react-types/shared": "^3.32.0",
- "@reduxjs/toolkit": "^2.9.0",
"@stripe/react-stripe-js": "^4.0.2",
"@stripe/stripe-js": "^7.9.0",
"@tailwindcss/postcss": "^4.1.13",
@@ -46,7 +45,6 @@
"react-i18next": "^15.7.2",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
- "react-redux": "^9.2.0",
"react-router": "^7.9.1",
"react-syntax-highlighter": "^15.6.6",
"remark-breaks": "^4.0.0",
diff --git a/frontend/src/components/features/chat/expandable-message.tsx b/frontend/src/components/features/chat/expandable-message.tsx
index 6acc93dfc5..918eafd6b8 100644
--- a/frontend/src/components/features/chat/expandable-message.tsx
+++ b/frontend/src/components/features/chat/expandable-message.tsx
@@ -1,4 +1,3 @@
-import { PayloadAction } from "@reduxjs/toolkit";
import { useEffect, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import Markdown from "react-markdown";
@@ -30,8 +29,8 @@ interface ExpandableMessageProps {
message: string;
type: string;
success?: boolean;
- observation?: PayloadAction;
- action?: PayloadAction;
+ observation?: { payload: OpenHandsObservation };
+ action?: { payload: OpenHandsAction };
}
export function ExpandableMessage({
diff --git a/frontend/src/entry.client.tsx b/frontend/src/entry.client.tsx
index e7d0aa56ad..b4de43821b 100644
--- a/frontend/src/entry.client.tsx
+++ b/frontend/src/entry.client.tsx
@@ -8,11 +8,9 @@
import { HydratedRouter } from "react-router/dom";
import React, { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
-import { Provider } from "react-redux";
import posthog from "posthog-js";
import "./i18n";
import { QueryClientProvider } from "@tanstack/react-query";
-import store from "./store";
import OptionService from "./api/option-service/option-service.api";
import { displayErrorToast } from "./utils/custom-toast-handlers";
import { queryClient } from "./query-client-config";
@@ -63,12 +61,10 @@ prepareApp().then(() =>
hydrateRoot(
document,
-
-
-
-
-
-
+
+
+
+
,
);
diff --git a/frontend/src/message.d.ts b/frontend/src/message.d.ts
index 080e6350d9..68baf339b5 100644
--- a/frontend/src/message.d.ts
+++ b/frontend/src/message.d.ts
@@ -1,4 +1,3 @@
-import { PayloadAction } from "@reduxjs/toolkit";
import { OpenHandsObservation } from "./types/core/observations";
import { OpenHandsAction } from "./types/core/actions";
@@ -12,6 +11,6 @@ export type Message = {
pending?: boolean;
translationID?: string;
eventID?: number;
- observation?: PayloadAction;
- action?: PayloadAction;
+ observation?: { payload: OpenHandsObservation };
+ action?: { payload: OpenHandsAction };
};
diff --git a/frontend/src/services/__tests__/actions.test.ts b/frontend/src/services/__tests__/actions.test.ts
index cee9338867..a0df1915a8 100644
--- a/frontend/src/services/__tests__/actions.test.ts
+++ b/frontend/src/services/__tests__/actions.test.ts
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { handleStatusMessage } from "../actions";
import { StatusMessage } from "#/types/message";
import { queryClient } from "#/query-client-config";
-import store from "#/store";
import { useStatusStore } from "#/state/status-store";
import { trackError } from "#/utils/error-handler";
@@ -13,12 +12,6 @@ vi.mock("#/query-client-config", () => ({
},
}));
-vi.mock("#/store", () => ({
- default: {
- dispatch: vi.fn(),
- },
-}));
-
vi.mock("#/state/status-store", () => ({
useStatusStore: {
getState: vi.fn(() => ({
@@ -56,9 +49,6 @@ describe("handleStatusMessage", () => {
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
queryKey: ["user", "conversation", "conversation-123"],
});
-
- // Verify that store.dispatch was not called
- expect(store.dispatch).not.toHaveBeenCalled();
});
it("should call setCurStatusMessage for info messages without conversation_title", () => {
@@ -109,9 +99,6 @@ describe("handleStatusMessage", () => {
metadata: { msgId: "ERROR_ID" },
});
- // Verify that store.dispatch was not called
- expect(store.dispatch).not.toHaveBeenCalled();
-
// Verify that queryClient.invalidateQueries was not called
expect(queryClient.invalidateQueries).not.toHaveBeenCalled();
});
diff --git a/frontend/src/store.ts b/frontend/src/store.ts
deleted file mode 100644
index 5700a78300..0000000000
--- a/frontend/src/store.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { combineReducers, configureStore } from "@reduxjs/toolkit";
-
-export const rootReducer = combineReducers({});
-
-const store = configureStore({
- reducer: rootReducer,
-});
-
-export type RootState = ReturnType;
-export type AppStore = typeof store;
-export type AppDispatch = typeof store.dispatch;
-
-export default store;
diff --git a/frontend/test-utils.tsx b/frontend/test-utils.tsx
index c882a054d8..87c5ce691b 100644
--- a/frontend/test-utils.tsx
+++ b/frontend/test-utils.tsx
@@ -1,15 +1,12 @@
-// See https://redux.js.org/usage/writing-tests#setting-up-a-reusable-test-render-function for more information
+// Test utilities for React components
import React, { PropsWithChildren } from "react";
-import { Provider } from "react-redux";
-import { configureStore } from "@reduxjs/toolkit";
import { RenderOptions, render } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { I18nextProvider, initReactI18next } from "react-i18next";
import i18n from "i18next";
import { vi } from "vitest";
import { AxiosError } from "axios";
-import { AppStore, RootState, rootReducer } from "./src/store";
// Mock useParams before importing components
vi.mock("react-router", async () => {
@@ -37,53 +34,14 @@ i18n.use(initReactI18next).init({
},
});
-export const setupStore = (preloadedState?: Partial): AppStore =>
- configureStore({
- reducer: rootReducer,
- preloadedState,
- });
+// This type interface extends the default options for render from RTL
+interface ExtendedRenderOptions extends Omit {}
-// This type interface extends the default options for render from RTL, as well
-// as allows the user to specify other things such as initialState, store.
-interface ExtendedRenderOptions extends Omit {
- preloadedState?: Partial;
- store?: AppStore;
-}
-
-// Export our own customized renderWithProviders function that creates a new Redux store and renders a
-// Note that this creates a separate Redux store instance for every test, rather than reusing the same store instance and resetting its state
+// Export our own customized renderWithProviders function that renders with QueryClient and i18next providers
+// Since we're using Zustand stores, we don't need a Redux Provider wrapper
export function renderWithProviders(
ui: React.ReactElement,
- {
- preloadedState = {},
- // Automatically create a store instance if no store was passed in
- store = setupStore(preloadedState),
- ...renderOptions
- }: ExtendedRenderOptions = {},
-) {
- function Wrapper({ children }: PropsWithChildren) {
- return (
-
-
- {children}
-
-
- );
- }
- return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
-}
-
-// Export a render function for components that only need QueryClient and i18next providers
-// (without Redux store)
-export function renderWithQueryAndI18n(
- ui: React.ReactElement,
- renderOptions: Omit = {},
+ renderOptions: ExtendedRenderOptions = {},
) {
function Wrapper({ children }: PropsWithChildren) {
return (
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index c802fb957b..e75428e6d6 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -35,11 +35,9 @@ export default defineConfig(({ mode }) => {
include: [
// Pre-bundle ALL dependencies to prevent runtime optimization and page reloads
// These are discovered during initial app load:
- "react-redux",
"posthog-js",
"@tanstack/react-query",
"react-hot-toast",
- "@reduxjs/toolkit",
"i18next",
"i18next-http-backend",
"i18next-browser-languagedetector",
@@ -62,7 +60,7 @@ export default defineConfig(({ mode }) => {
"react-icons/lu",
"react-icons/di",
"react-icons/io5",
- "react-icons/io", // Added to prevent runtime optimization
+ "react-icons/io", // Added to prevent runtime optimization
"@monaco-editor/react",
"react-textarea-autosize",
"react-markdown",