Files
OpenHands/test-utils.tsx
T
7d954a9f0f Remove obsolete V1 terminology (#71)
* 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>
2026-05-09 00:37:03 +07:00

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: [],
});