Compare commits

...

1 Commits

Author SHA1 Message Date
openhands 9672f105e4 Fix issue #6098: [Bug]: "this action has not been executed" message is duplicated 2025-01-06 23:04:07 +00:00
2 changed files with 126 additions and 39 deletions
@@ -0,0 +1,90 @@
import { test, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { ExpandableMessage } from "#/components/features/chat/expandable-message";
import { I18nextProvider } from "react-i18next";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
// Initialize i18n for testing
i18n.use(initReactI18next).init({
lng: "en",
resources: {
en: {
translation: {
"ACTION_MESSAGE$RUN": "this action has not been executed",
},
},
},
});
test("should show only the headline for unexecuted actions", () => {
render(
<I18nextProvider i18n={i18n}>
<ExpandableMessage
type="action"
id="ACTION_MESSAGE$RUN"
message="Command:\n`ls -l`"
success={undefined}
/>
</I18nextProvider>
);
// The headline should be visible
const headline = screen.getByText("this action has not been executed");
expect(headline).toBeInTheDocument();
expect(headline).toHaveClass("font-bold");
// The command details should not be visible
const details = screen.queryByText(/Command:/, { exact: false });
expect(details).not.toBeInTheDocument();
});
test("should show only the details for completed successful actions", () => {
render(
<I18nextProvider i18n={i18n}>
<ExpandableMessage
type="action"
id="ACTION_MESSAGE$RUN"
message="Command executed successfully"
success={true}
/>
</I18nextProvider>
);
// The command details should be visible
const details = screen.getByText("Command executed successfully");
expect(details).toBeInTheDocument();
// The success icon should be visible
const statusIcon = screen.getByTestId("status-icon");
expect(statusIcon).toHaveClass("fill-success");
// The headline should not be visible
const headline = screen.queryByText("this action has not been executed");
expect(headline).not.toBeInTheDocument();
});
test("should show only the details for completed failed actions", () => {
render(
<I18nextProvider i18n={i18n}>
<ExpandableMessage
type="action"
id="ACTION_MESSAGE$RUN"
message="Command failed"
success={false}
/>
</I18nextProvider>
);
// The command details should be visible
const details = screen.getByText("Command failed");
expect(details).toBeInTheDocument();
// The error icon should be visible
const statusIcon = screen.getByTestId("status-icon");
expect(statusIcon).toHaveClass("fill-danger");
// The headline should not be visible
const headline = screen.queryByText("this action has not been executed");
expect(headline).not.toBeInTheDocument();
});
@@ -30,11 +30,24 @@ export function ExpandableMessage({
useEffect(() => {
if (id && i18n.exists(id)) {
setHeadline(t(id));
// Only show the headline if the action hasn't been executed yet (success is undefined)
if (success === undefined) {
setHeadline(t(id));
setDetails("");
setShowDetails(false);
} else {
// Only show the details if the action has been executed
setHeadline("");
setDetails(message);
setShowDetails(true);
}
} else {
// If no translation ID is provided, just show the message
setHeadline("");
setDetails(message);
setShowDetails(false);
setShowDetails(true);
}
}, [id, message, i18n.language]);
}, [id, message, success, i18n.language, t]);
const statusIconClasses = "h-4 w-4 ml-2 inline";
@@ -46,7 +59,8 @@ export function ExpandableMessage({
)}
>
<div className="text-sm w-full">
{headline && (
{headline ? (
// Show headline for unexecuted actions
<div className="flex flex-row justify-between items-center w-full">
<span
className={cn(
@@ -55,30 +69,26 @@ export function ExpandableMessage({
)}
>
{headline}
<button
type="button"
onClick={() => setShowDetails(!showDetails)}
className="cursor-pointer text-left"
>
{showDetails ? (
<ArrowUp
className={cn(
"h-4 w-4 ml-2 inline",
type === "error" ? "fill-danger" : "fill-neutral-300",
)}
/>
) : (
<ArrowDown
className={cn(
"h-4 w-4 ml-2 inline",
type === "error" ? "fill-danger" : "fill-neutral-300",
)}
/>
)}
</button>
</span>
</div>
) : (
// Show details for executed actions
<div className="flex flex-row justify-between items-center w-full">
<div className="flex-grow">
<Markdown
className="text-sm overflow-auto"
components={{
code,
ul,
ol,
}}
remarkPlugins={[remarkGfm]}
>
{details}
</Markdown>
</div>
{type === "action" && success !== undefined && (
<span className="flex-shrink-0">
<span className="flex-shrink-0 ml-2">
{success ? (
<CheckCircle
data-testid="status-icon"
@@ -94,19 +104,6 @@ export function ExpandableMessage({
)}
</div>
)}
{showDetails && (
<Markdown
className="text-sm overflow-auto"
components={{
code,
ul,
ol,
}}
remarkPlugins={[remarkGfm]}
>
{details}
</Markdown>
)}
</div>
</div>
);