Files
OpenHands/frontend/__tests__/utils/handle-event-for-ui.test.ts
2025-10-10 16:29:27 +04:00

142 lines
4.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
ActionEvent,
ObservationEvent,
MessageEvent,
SecurityRisk,
OpenHandsEvent,
} from "#/types/v1/core";
import { handleEventForUI } from "#/utils/handle-event-for-ui";
describe("handleEventForUI", () => {
const mockObservationEvent: ObservationEvent = {
id: "test-observation-1",
timestamp: Date.now().toString(),
source: "environment",
tool_name: "execute_bash",
tool_call_id: "call_123",
observation: {
kind: "ExecuteBashObservation",
output: "hello\n",
command: "echo hello",
exit_code: 0,
error: false,
timeout: false,
metadata: {
exit_code: 0,
pid: 12345,
username: "user",
hostname: "localhost",
working_dir: "/home/user",
py_interpreter_path: null,
prefix: "",
suffix: "",
},
},
action_id: "test-action-1",
};
const mockActionEvent: ActionEvent = {
id: "test-action-1",
timestamp: Date.now().toString(),
source: "agent",
thought: [{ type: "text", text: "I need to execute a bash command" }],
thinking_blocks: [],
action: {
kind: "ExecuteBashAction",
command: "echo hello",
is_input: false,
timeout: null,
reset: false,
},
tool_name: "execute_bash",
tool_call_id: "call_123",
tool_call: {
id: "call_123",
type: "function",
function: {
name: "execute_bash",
arguments: '{"command": "echo hello"}',
},
},
llm_response_id: "response_123",
security_risk: SecurityRisk.UNKNOWN,
};
const mockMessageEvent: MessageEvent = {
id: "test-event-1",
timestamp: Date.now().toString(),
source: "user",
llm_message: {
role: "user",
content: [{ type: "text", text: "Hello, world!" }],
},
activated_microagents: [],
extended_content: [],
};
it("should add non-observation events to the end of uiEvents", () => {
const initialUiEvents = [mockMessageEvent];
const result = handleEventForUI(mockActionEvent, initialUiEvents);
expect(result).toEqual([mockMessageEvent, mockActionEvent]);
expect(result).not.toBe(initialUiEvents); // Should return a new array
});
it("should replace corresponding action with observation when action exists", () => {
const initialUiEvents = [mockMessageEvent, mockActionEvent];
const result = handleEventForUI(mockObservationEvent, initialUiEvents);
expect(result).toEqual([mockMessageEvent, mockObservationEvent]);
expect(result).not.toBe(initialUiEvents); // Should return a new array
});
it("should add observation to end when corresponding action is not found", () => {
const initialUiEvents = [mockMessageEvent];
const result = handleEventForUI(mockObservationEvent, initialUiEvents);
expect(result).toEqual([mockMessageEvent, mockObservationEvent]);
expect(result).not.toBe(initialUiEvents); // Should return a new array
});
it("should handle empty uiEvents array", () => {
const initialUiEvents: OpenHandsEvent[] = [];
const result = handleEventForUI(mockObservationEvent, initialUiEvents);
expect(result).toEqual([mockObservationEvent]);
expect(result).not.toBe(initialUiEvents); // Should return a new array
});
it("should not mutate the original uiEvents array", () => {
const initialUiEvents = [mockMessageEvent, mockActionEvent];
const originalLength = initialUiEvents.length;
const originalFirstEvent = initialUiEvents[0];
handleEventForUI(mockObservationEvent, initialUiEvents);
expect(initialUiEvents).toHaveLength(originalLength);
expect(initialUiEvents[0]).toBe(originalFirstEvent);
expect(initialUiEvents[1]).toBe(mockActionEvent); // Should not be replaced
});
it("should replace the correct action when multiple actions exist", () => {
const anotherActionEvent: ActionEvent = {
...mockActionEvent,
id: "test-action-2",
};
const initialUiEvents = [
mockMessageEvent,
mockActionEvent,
anotherActionEvent,
];
const result = handleEventForUI(mockObservationEvent, initialUiEvents);
expect(result).toEqual([
mockMessageEvent,
mockObservationEvent,
anotherActionEvent,
]);
});
});