mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 13:18:19 +08:00
* Remove obsolete V1 terminology Renames V1-labeled agent-server modules, types, hooks, and component paths to neutral agent-server/conversation terminology. Drops the residual frontend `v1_enabled` setting branching while preserving true external/API-version contracts (e.g. `/api/v1` endpoint paths and provider URL versions). Fixes #70. Co-authored-by: openhands <openhands@all-hands.dev> * Remove legacy V0 frontend code Drops the V0 chat renderers, event-content helpers, V0 type aliases, socket.io subscription/mocking infrastructure, V0-only hooks/tests, and the residual V0/V1 commentary that the previous "Remove obsolete V1 terminology" commit left behind. Resolved on top of the Phase-1 OSS cleanup that landed on main: kept the agent-server-aligned settings/secrets services, mocks, and WebClientFeatureFlags shape. Co-authored-by: openhands <openhands@all-hands.dev> * Merge remote-tracking branch 'origin/main' into openhands/remove-v1-terminology Resolved conflicts: - src/api/agent-server-adapter.ts: kept ExecutionStatus rename + main's getEffectiveLocalBackend usage. - src/api/conversation-service/agent-server-conversation-service.api.ts and .types.ts: kept ConversationWorkspace rename + sandbox_id field from main, merged main's cloud branching with our renamed types. - src/api/event-service/event-service.api.ts: kept agent-server import path + main's added cloud-proxy imports. - src/api/git-service/agent-server-git-service.api.ts: kept renamed mapAnyGitStatusToClientStatus + class AgentServerGitService, renamed V1GitChange to AgentServerGitChange. - src/components/features/chat/components/chat-input-actions.tsx: kept renamed pause/resume hooks + main's useActiveBackend import. - src/hooks/mutation/conversation-mutation-utils.ts: merged renamed pauseConversation with main's cloud sandbox branch. - src/hooks/mutation/use-new-conversation-command.ts: kept renamed AgentServerConversationService usage with main's added args. - src/hooks/query/use-conversation-history.ts: kept renamed searchEvents with main's added forwarding params. - src/hooks/query/use-paginated-conversations.ts: kept renamed types + main's useActiveBackend. - src/hooks/query/use-unified-vscode-url.ts: kept renamed AgentServerConversationService. Also propagated V1 -> agent-server / Conversation renames into files that came from main: - Cloud service file imports updated to agent-server-conversation-service paths and renamed types (V1AppConversation* -> AppConversation*, V1ExecutionStatus -> ExecutionStatus, V1RuntimeConversationInfo -> RuntimeConversationInfo, V1ConversationWorkspace -> ConversationWorkspace, V1Messages -> ConversationMessages). - pauseV1Conversation -> pauseConversation, fetchV1ConversationData -> fetchConversationData, searchEventsV1 -> searchEvents, V1ConversationService -> AgentServerConversationService. - Dropped v1_task_id / is_v1 fields on CreateConversationResponse; use the existing task_id field. Verified locally: npm run typecheck, npm test (1479 passed, 12 skipped), and npm run build all pass. Co-authored-by: openhands <openhands@all-hands.dev> * Merge origin/main into openhands/remove-v1-terminology Resolved merge conflicts from main, including: - Incoming cloud backend support (#145), automations, ACP tool events - Renamed V1 references in merged cloud service files - Fixed isV1Event → isAgentServerEvent rename and removed dead V0 type guard - Fixed V1ConfirmationButtons → ConversationConfirmationButtons reference - Fixed broken relative import path in generic-event-message-wrapper - Renamed pauseV1Conversation → pauseConversation in cloud test - Added { ref: "HEAD" } to agent-server-git-service calls to match test expectations Verified: - npm run typecheck ✅ - npm test ✅ (1572 passed) - npm run build ✅ Co-authored-by: openhands <openhands@all-hands.dev> --------- Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: Xingyao Wang <xingyao@all-hands.dev> Co-authored-by: hieptl <hieptl.developer@gmail.com>
233 lines
5.9 KiB
TypeScript
233 lines
5.9 KiB
TypeScript
import React, { PropsWithChildren } from "react";
|
|
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 {
|
|
ActionEvent,
|
|
MessageEvent,
|
|
ObservationEvent,
|
|
PlanningFileEditorObservation,
|
|
} from "#/types/agent-server/core";
|
|
import {
|
|
NavigationProvider,
|
|
type NavigationContextValue,
|
|
} from "#/context/navigation-context";
|
|
import { SecurityRisk } from "#/types/agent-server/core";
|
|
|
|
export const useParamsMock = vi.fn(() => ({
|
|
conversationId: "test-conversation-id",
|
|
}));
|
|
|
|
// Mock useParams before importing components
|
|
vi.mock("react-router", async () => {
|
|
const actual =
|
|
await vi.importActual<typeof import("react-router")>("react-router");
|
|
return {
|
|
...actual,
|
|
useParams: useParamsMock,
|
|
useRevalidator: () => ({
|
|
revalidate: vi.fn(),
|
|
}),
|
|
};
|
|
});
|
|
|
|
// Initialize i18n for tests - return key when translation is missing
|
|
i18n.use(initReactI18next).init({
|
|
lng: "en",
|
|
fallbackLng: "en",
|
|
ns: ["translation", "openhands"],
|
|
defaultNS: "translation",
|
|
resources: {
|
|
en: {
|
|
translation: {},
|
|
openhands: {},
|
|
},
|
|
},
|
|
interpolation: {
|
|
escapeValue: false,
|
|
},
|
|
returnEmptyString: false,
|
|
missingKeyHandler: false,
|
|
saveMissing: false,
|
|
});
|
|
|
|
const createNavigationValue = (
|
|
overrides: Partial<NavigationContextValue> = {},
|
|
): NavigationContextValue => ({
|
|
currentPath: "/",
|
|
conversationId: "test-conversation-id",
|
|
isNavigating: false,
|
|
navigate: vi.fn(),
|
|
...overrides,
|
|
});
|
|
|
|
// This type interface extends the default options for render from RTL
|
|
interface ExtendedRenderOptions extends Omit<RenderOptions, "queries"> {
|
|
navigation?: Partial<NavigationContextValue>;
|
|
}
|
|
|
|
// 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,
|
|
renderOptions: ExtendedRenderOptions = {},
|
|
) {
|
|
const { navigation, ...rtlRenderOptions } = renderOptions;
|
|
const navigationValue = createNavigationValue(navigation);
|
|
|
|
function Wrapper({ children }: PropsWithChildren) {
|
|
return (
|
|
<QueryClientProvider
|
|
client={
|
|
new QueryClient({
|
|
defaultOptions: { queries: { retry: false } },
|
|
})
|
|
}
|
|
>
|
|
<I18nextProvider i18n={i18n}>
|
|
<NavigationProvider value={navigationValue}>
|
|
{children}
|
|
</NavigationProvider>
|
|
</I18nextProvider>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
return render(ui, { wrapper: Wrapper, ...rtlRenderOptions });
|
|
}
|
|
|
|
export const createAxiosNotFoundErrorObject = () =>
|
|
new AxiosError(
|
|
"Request failed with status code 404",
|
|
"ERR_BAD_REQUEST",
|
|
undefined,
|
|
undefined,
|
|
{
|
|
status: 404,
|
|
statusText: "Not Found",
|
|
data: { message: "Settings not found" },
|
|
headers: {},
|
|
// @ts-expect-error - we only need the response object for this test
|
|
config: {},
|
|
},
|
|
);
|
|
|
|
export const createAxiosError = (
|
|
status: number,
|
|
statusText: string,
|
|
data: unknown,
|
|
) =>
|
|
new AxiosError(
|
|
`Request failed with status code ${status}`,
|
|
"ERR_BAD_REQUEST",
|
|
undefined,
|
|
undefined,
|
|
{
|
|
status,
|
|
statusText,
|
|
data,
|
|
headers: {},
|
|
// @ts-expect-error - we only need the response object for this test
|
|
config: {},
|
|
},
|
|
);
|
|
|
|
// Helper to create a PlanningFileEditorAction event
|
|
export const createPlanningFileEditorActionEvent = (
|
|
id: string,
|
|
): ActionEvent => ({
|
|
id,
|
|
timestamp: new Date().toISOString(),
|
|
source: "agent",
|
|
thought: [{ type: "text", text: "Planning action" }],
|
|
thinking_blocks: [],
|
|
action: {
|
|
kind: "PlanningFileEditorAction",
|
|
command: "create",
|
|
path: "/workspace/PLAN.md",
|
|
file_text: "Plan content",
|
|
old_str: null,
|
|
new_str: null,
|
|
insert_line: null,
|
|
view_range: null,
|
|
},
|
|
tool_name: "planning_file_editor",
|
|
tool_call_id: "call-1",
|
|
tool_call: {
|
|
id: "call-1",
|
|
type: "function",
|
|
function: {
|
|
name: "planning_file_editor",
|
|
arguments: '{"command": "create"}',
|
|
},
|
|
},
|
|
llm_response_id: "response-1",
|
|
security_risk: SecurityRisk.UNKNOWN,
|
|
});
|
|
|
|
// Helper to create a non-planning action event
|
|
export const createOtherActionEvent = (id: string): ActionEvent => ({
|
|
id,
|
|
timestamp: new Date().toISOString(),
|
|
source: "agent",
|
|
thought: [{ type: "text", text: "Other action" }],
|
|
thinking_blocks: [],
|
|
action: {
|
|
kind: "ExecuteBashAction",
|
|
command: "echo test",
|
|
is_input: false,
|
|
timeout: null,
|
|
reset: false,
|
|
},
|
|
tool_name: "execute_bash",
|
|
tool_call_id: "call-1",
|
|
tool_call: {
|
|
id: "call-1",
|
|
type: "function",
|
|
function: {
|
|
name: "execute_bash",
|
|
arguments: '{"command": "echo test"}',
|
|
},
|
|
},
|
|
llm_response_id: "response-1",
|
|
security_risk: SecurityRisk.UNKNOWN,
|
|
});
|
|
|
|
// Helper to create a PlanningFileEditorObservation event
|
|
export const createPlanningObservationEvent = (
|
|
id: string,
|
|
actionId: string = "action-1",
|
|
): ObservationEvent<PlanningFileEditorObservation> => ({
|
|
id,
|
|
timestamp: new Date().toISOString(),
|
|
source: "environment",
|
|
tool_name: "planning_file_editor",
|
|
tool_call_id: "call-1",
|
|
action_id: actionId,
|
|
observation: {
|
|
kind: "PlanningFileEditorObservation",
|
|
content: [{ type: "text", text: "Plan content" }],
|
|
is_error: false,
|
|
command: "create",
|
|
path: "/workspace/PLAN.md",
|
|
prev_exist: false,
|
|
old_content: null,
|
|
new_content: "Plan content",
|
|
},
|
|
});
|
|
|
|
// Helper to create a user message event
|
|
export const createUserMessageEvent = (id: string): MessageEvent => ({
|
|
id,
|
|
timestamp: new Date().toISOString(),
|
|
source: "user",
|
|
llm_message: {
|
|
role: "user",
|
|
content: [{ type: "text", text: "User message" }],
|
|
},
|
|
activated_microagents: [],
|
|
extended_content: [],
|
|
});
|