feature: Add message feedback and update frontend interface (#5022)
* add style for message feedback * add backend suprot to new feature * update frontend interface and add handle function * [autofix.ci] apply automated fixes * Update tooltip content for bot messages * Update evaluation icons styling * Add custom thumb icons for thumbs up and thumbs down * Add custom thumb icons for thumbs up and thumbs down * Update thumb icons based on evaluation value * [autofix.ci] apply automated fixes * Update property name for positive feedback * Update property name for positive feedback * feat: Add data-testid attributes to helpful and not helpful buttons and update test of playground to include new functionality * update test to include new message features
This commit is contained in:
parent
4be6b04d8c
commit
02fbb450db
14 changed files with 258 additions and 61 deletions
|
|
@ -92,14 +92,14 @@ async def update_message(
|
|||
|
||||
try:
|
||||
message_dict = message.model_dump(exclude_unset=True, exclude_none=True)
|
||||
message_dict["edit"] = True
|
||||
if "text" in message_dict and message_dict["text"] != db_message.text:
|
||||
message_dict["edit"] = True
|
||||
db_message.sqlmodel_update(message_dict)
|
||||
session.add(db_message)
|
||||
await session.commit()
|
||||
await session.refresh(db_message)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e)) from e
|
||||
|
||||
return db_message
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class Properties(BaseModel):
|
|||
source: Source = Field(default_factory=Source)
|
||||
icon: str | None = None
|
||||
allow_markdown: bool = False
|
||||
positive_feedback: bool | None = None
|
||||
state: Literal["partial", "complete"] = "complete"
|
||||
targets: list = []
|
||||
|
||||
|
|
|
|||
|
|
@ -160,3 +160,4 @@ class MessageUpdate(SQLModel):
|
|||
files: list[str] | None = None
|
||||
edit: bool | None = None
|
||||
error: bool | None = None
|
||||
properties: Properties | None = None
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ class TestChatInput(ComponentTestBaseWithClient):
|
|||
"background_color": default_kwargs["background_color"],
|
||||
"text_color": default_kwargs["text_color"],
|
||||
"icon": default_kwargs["chat_icon"],
|
||||
"positive_feedback": None,
|
||||
"edited": False,
|
||||
"source": {"id": None, "display_name": None, "source": None},
|
||||
"allow_markdown": False,
|
||||
|
|
|
|||
17
src/frontend/src/icons/thumbs/index.tsx
Normal file
17
src/frontend/src/icons/thumbs/index.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import ThumbDownFilled from "./thumbDown";
|
||||
import ThumbUpFilled from "./thumbUp";
|
||||
|
||||
export const ThumbUpIconCustom = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <ThumbUpFilled ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
export const ThumbDownIconCustom = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <ThumbDownFilled ref={ref} {...props} />;
|
||||
});
|
||||
25
src/frontend/src/icons/thumbs/thumbDown.jsx
Normal file
25
src/frontend/src/icons/thumbs/thumbDown.jsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
const ThumbDownFilled = (props) => (
|
||||
<svg
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M6.74945 13.59L7.49945 10.5H3.12695C2.89409 10.5 2.66442 10.4458 2.45613 10.3416C2.24785 10.2375 2.06667 10.0863 1.92695 9.9C1.78723 9.71371 1.6928 9.49744 1.65115 9.26833C1.60949 9.03922 1.62175 8.80355 1.68695 8.58L3.43445 2.58C3.52533 2.26843 3.71481 1.99473 3.97445 1.8C4.2341 1.60527 4.5499 1.5 4.87445 1.5H14.9995C15.3973 1.5 15.7788 1.65804 16.0601 1.93934C16.3414 2.22064 16.4995 2.60218 16.4995 3V9C16.4995 9.39782 16.3414 9.77936 16.0601 10.0607C15.7788 10.342 15.3973 10.5 14.9995 10.5H12.9295C12.6504 10.5001 12.3769 10.5781 12.1397 10.7252C11.9026 10.8723 11.7111 11.0826 11.587 11.3325L8.99945 16.5C8.64577 16.4956 8.29765 16.4114 7.9811 16.2536C7.66455 16.0957 7.38776 15.8684 7.1714 15.5886C6.95504 15.3088 6.80472 14.9837 6.73165 14.6376C6.65859 14.2915 6.66467 13.9334 6.74945 13.59Z"
|
||||
fill="#FEE2E2"
|
||||
stroke="#DC2626"
|
||||
stroke-width="1.25"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.75 10.5V1.5"
|
||||
stroke="#DC2626"
|
||||
stroke-width="1.25"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default ThumbDownFilled;
|
||||
25
src/frontend/src/icons/thumbs/thumbUp.jsx
Normal file
25
src/frontend/src/icons/thumbs/thumbUp.jsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
const ThumbUpFilled = (props) => (
|
||||
<svg
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M11.25 4.41L10.5 7.5H14.8725C15.1054 7.5 15.335 7.55422 15.5433 7.65836C15.7516 7.7625 15.9328 7.91371 16.0725 8.1C16.2122 8.28629 16.3066 8.50256 16.3483 8.73167C16.39 8.96078 16.3777 9.19645 16.3125 9.42L14.565 15.42C14.4741 15.7316 14.2846 16.0053 14.025 16.2C13.7654 16.3947 13.4496 16.5 13.125 16.5H3C2.60218 16.5 2.22064 16.342 1.93934 16.0607C1.65804 15.7794 1.5 15.3978 1.5 15V9C1.5 8.60218 1.65804 8.22064 1.93934 7.93934C2.22064 7.65804 2.60218 7.5 3 7.5H5.07C5.34906 7.49985 5.62255 7.42186 5.85972 7.27479C6.09688 7.12772 6.28832 6.91741 6.4125 6.6675L9 1.5C9.35368 1.50438 9.7018 1.58863 10.0184 1.74645C10.3349 1.90427 10.6117 2.13158 10.8281 2.4114C11.0444 2.69122 11.1947 3.01632 11.2678 3.3624C11.3409 3.70848 11.3348 4.0666 11.25 4.41Z"
|
||||
fill="#D1FAE5"
|
||||
stroke="#059669"
|
||||
stroke-width="1.25"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.25 7.5V16.5"
|
||||
stroke="#059669"
|
||||
stroke-width="1.25"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default ThumbUpFilled;
|
||||
|
|
@ -1,25 +1,35 @@
|
|||
import IconComponent from "@/components/common/genericIconComponent";
|
||||
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/utils/utils";
|
||||
import { ButtonHTMLAttributes, useState } from "react";
|
||||
|
||||
export function EditMessageButton({
|
||||
onEdit,
|
||||
onCopy,
|
||||
onDelete,
|
||||
onEvaluate,
|
||||
isBotMessage,
|
||||
evaluation,
|
||||
...props
|
||||
}: ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
onEdit: () => void;
|
||||
onCopy: () => void;
|
||||
onDelete: () => void;
|
||||
onEvaluate?: (value: boolean | null) => void;
|
||||
isBotMessage?: boolean;
|
||||
evaluation?: boolean | null;
|
||||
}) {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const handleCopy = () => {
|
||||
onCopy();
|
||||
setIsCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000); // Reset after 2 seconds
|
||||
setTimeout(() => setIsCopied(false), 2000);
|
||||
};
|
||||
|
||||
const handleEvaluate = (value: boolean) => {
|
||||
onEvaluate?.(evaluation === value ? null : value);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -56,6 +66,46 @@ export function EditMessageButton({
|
|||
</Button>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
|
||||
{isBotMessage && (
|
||||
<div className="flex">
|
||||
<ShadTooltip styleClasses="z-50" content="Helpful" side="top">
|
||||
<div className="p-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleEvaluate(true)}
|
||||
className="h-8 w-8"
|
||||
data-testid="helpful-button"
|
||||
>
|
||||
<IconComponent
|
||||
name={evaluation === true ? "ThumbUpIconCustom" : "ThumbsUp"}
|
||||
className={cn("h-4 w-4")}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip styleClasses="z-50" content="Not helpful" side="top">
|
||||
<div className="p-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleEvaluate(false)}
|
||||
className="h-8 w-8"
|
||||
data-testid="not-helpful-button"
|
||||
>
|
||||
<IconComponent
|
||||
name={
|
||||
evaluation === false ? "ThumbDownIconCustom" : "ThumbsDown"
|
||||
}
|
||||
className={cn("h-4 w-4")}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,6 +212,35 @@ export default function ChatMessage({
|
|||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleEvaluateAnswer = (evaluation: boolean | null) => {
|
||||
updateMessageMutation(
|
||||
{
|
||||
message: {
|
||||
...chat,
|
||||
files: convertFiles(chat.files),
|
||||
sender_name: chat.sender_name ?? "AI",
|
||||
text: chat.message.toString(),
|
||||
sender: chat.isSend ? "User" : "Machine",
|
||||
flow_id,
|
||||
session_id: chat.session ?? "",
|
||||
properties: {
|
||||
...chat.properties,
|
||||
positive_feedback: evaluation,
|
||||
},
|
||||
},
|
||||
refetch: true,
|
||||
},
|
||||
{
|
||||
onError: () => {
|
||||
setErrorData({
|
||||
title: "Error updating messages.",
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const editedFlag = chat.edit ? (
|
||||
<div className="text-sm text-muted-foreground">(Edited)</div>
|
||||
) : null;
|
||||
|
|
@ -742,6 +771,9 @@ export default function ChatMessage({
|
|||
onDelete={() => {}}
|
||||
onEdit={() => setEditMessage(true)}
|
||||
className="h-fit group-hover:visible"
|
||||
isBotMessage={!chat.isSend}
|
||||
onEvaluate={handleEvaluateAnswer}
|
||||
evaluation={chat.properties?.positive_feedback}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -177,7 +177,10 @@ export default function ChatView({
|
|||
<h3 className="mt-2 pb-2 text-2xl font-semibold text-primary">
|
||||
New chat
|
||||
</h3>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
<p
|
||||
className="text-lg text-muted-foreground"
|
||||
data-testid="new-chat-text"
|
||||
>
|
||||
<TextEffectPerChar>
|
||||
Test your flow with a chat prompt
|
||||
</TextEffectPerChar>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
useGetMessagesQuery,
|
||||
} from "@/controllers/API/queries/messages";
|
||||
import { useUtilityStore } from "@/stores/utilityStore";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import IconComponent from "../../components/common/genericIconComponent";
|
||||
import ShadTooltip from "../../components/common/shadTooltipComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
|
|
@ -118,7 +118,6 @@ export default function IOModal({
|
|||
const setLockChat = useFlowStore((state) => state.setLockChat);
|
||||
const [chatValue, setChatValue] = useState("");
|
||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
const messages = useMessagesStore((state) => state.messages);
|
||||
const [sessions, setSessions] = useState<string[]>(
|
||||
Array.from(
|
||||
|
|
@ -129,7 +128,6 @@ export default function IOModal({
|
|||
),
|
||||
),
|
||||
);
|
||||
const flowPool = useFlowStore((state) => state.flowPool);
|
||||
const [sessionId, setSessionId] = useState<string>(currentFlowId);
|
||||
const { isFetched: messagesFetched } = useGetMessagesQuery(
|
||||
{
|
||||
|
|
@ -139,33 +137,44 @@ export default function IOModal({
|
|||
{ enabled: open },
|
||||
);
|
||||
|
||||
async function sendMessage({
|
||||
repeat = 1,
|
||||
files,
|
||||
}: {
|
||||
repeat: number;
|
||||
files?: string[];
|
||||
}): Promise<void> {
|
||||
if (isBuilding) return;
|
||||
setIsBuilding(true);
|
||||
setLockChat(true);
|
||||
setChatValue("");
|
||||
for (let i = 0; i < repeat; i++) {
|
||||
await buildFlow({
|
||||
input_value: chatValue,
|
||||
startNodeId: chatInput?.id,
|
||||
files: files,
|
||||
silent: true,
|
||||
session: sessionId,
|
||||
setLockChat,
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setLockChat(false);
|
||||
});
|
||||
}
|
||||
// refetch();
|
||||
setLockChat(false);
|
||||
}
|
||||
const sendMessage = useCallback(
|
||||
async ({
|
||||
repeat = 1,
|
||||
files,
|
||||
}: {
|
||||
repeat: number;
|
||||
files?: string[];
|
||||
}): Promise<void> => {
|
||||
if (isBuilding) return;
|
||||
setIsBuilding(true);
|
||||
setLockChat(true);
|
||||
setChatValue("");
|
||||
for (let i = 0; i < repeat; i++) {
|
||||
await buildFlow({
|
||||
input_value: chatValue,
|
||||
startNodeId: chatInput?.id,
|
||||
files: files,
|
||||
silent: true,
|
||||
session: sessionId,
|
||||
setLockChat,
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
setLockChat(false);
|
||||
});
|
||||
}
|
||||
// refetch();
|
||||
setLockChat(false);
|
||||
},
|
||||
[
|
||||
isBuilding,
|
||||
setIsBuilding,
|
||||
setLockChat,
|
||||
chatValue,
|
||||
chatInput?.id,
|
||||
sessionId,
|
||||
buildFlow,
|
||||
],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedTab(inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export type PropertiesType = {
|
|||
edited?: boolean;
|
||||
allow_markdown?: boolean;
|
||||
state?: string;
|
||||
positive_feedback?: boolean | null;
|
||||
};
|
||||
|
||||
export type ChatOutputType = {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { ZepMemoryIcon } from "@/icons/ZepMemory";
|
|||
import { AthenaIcon } from "@/icons/athena/index";
|
||||
import { freezeAllIcon } from "@/icons/freezeAll";
|
||||
import { GlobeOkIcon } from "@/icons/globe-ok";
|
||||
import { ThumbDownIconCustom, ThumbUpIconCustom } from "@/icons/thumbs";
|
||||
import { TwitterLogoIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
AlertCircle,
|
||||
|
|
@ -205,6 +206,8 @@ import {
|
|||
TextCursorInput,
|
||||
TextSearch,
|
||||
TextSearchIcon,
|
||||
ThumbsDown,
|
||||
ThumbsUp,
|
||||
ToyBrick,
|
||||
Trash2,
|
||||
Type,
|
||||
|
|
@ -927,4 +930,8 @@ export const nodeIconsLucide: iconsType = {
|
|||
Cog,
|
||||
ArrowRightLeft,
|
||||
FolderSync,
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
ThumbDownIconCustom,
|
||||
ThumbUpIconCustom,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ test(
|
|||
|
||||
await page.getByTestId("sidebar-search-input").click();
|
||||
await page.getByTestId("sidebar-search-input").fill("chat output");
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForSelector('[data-testid="outputsChat Output"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
await page
|
||||
.getByTestId("outputsChat Output")
|
||||
|
|
@ -39,7 +41,9 @@ test(
|
|||
|
||||
await page.getByTestId("sidebar-search-input").click();
|
||||
await page.getByTestId("sidebar-search-input").fill("chat input");
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForSelector('[data-testid="inputsChat Input"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
await page
|
||||
.getByTestId("inputsChat Input")
|
||||
|
|
@ -53,7 +57,9 @@ test(
|
|||
|
||||
await page.getByTestId("sidebar-search-input").click();
|
||||
await page.getByTestId("sidebar-search-input").fill("text output");
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForSelector('[data-testid="outputsText Output"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
await page
|
||||
.getByTestId("outputsText Output")
|
||||
|
|
@ -168,7 +174,6 @@ test(
|
|||
.filter({ hasText: /^Usermessage 1$/ })
|
||||
.getByTestId("icon-Pen")
|
||||
.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.getByTestId("textarea").fill("edit_1");
|
||||
await page.getByTestId("save-button").click();
|
||||
|
|
@ -177,8 +182,6 @@ test(
|
|||
// check cancel edit
|
||||
await page.getByTestId("sender_name_user").hover();
|
||||
await page.getByTestId("icon-Pen").first().click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.getByTestId("textarea").fill("cancel_edit");
|
||||
await page.getByTestId("cancel-button").click();
|
||||
await page.getByTestId("chat-message-User-edit_1").click();
|
||||
|
|
@ -190,7 +193,6 @@ test(
|
|||
.click();
|
||||
await page.getByTestId("chat-message-AI-message 1").hover();
|
||||
await page.getByTestId("icon-Pen").last().click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.getByTestId("textarea").fill("edit_bot_1");
|
||||
await page.getByTestId("save-button").click();
|
||||
|
|
@ -198,7 +200,6 @@ test(
|
|||
// check cancel edit bot
|
||||
await page.getByTestId("chat-message-AI-edit_bot_1").hover();
|
||||
await page.getByTestId("icon-Pen").last().click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.getByTestId("textarea").fill("edit_bot_cancel");
|
||||
await page.getByTestId("cancel-button").click();
|
||||
|
|
@ -241,23 +242,47 @@ test(
|
|||
await page.getByTestId("chat-message-User-session_after_delete").click();
|
||||
await expect(page.getByTestId("session-selector")).toBeVisible();
|
||||
|
||||
// check new chat
|
||||
await page.getByTestId("new-chat").click();
|
||||
await page.waitForTimeout(5000);
|
||||
await page.getByText("New chat").click();
|
||||
await page.getByTestId("input-chat-playground").click();
|
||||
await page.getByTestId("input-chat-playground").fill("second session");
|
||||
await page.keyboard.press("Enter");
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
await page.getByTestId("chat-message-User-second session").click();
|
||||
await page
|
||||
.getByTestId("chat-message-AI-second session")
|
||||
.getByText("second session")
|
||||
.click();
|
||||
expect(await page.getByTestId("session-selector").count()).toBe(2);
|
||||
|
||||
const sessionElements = await page.getByTestId("session-selector").all();
|
||||
expect(sessionElements.length).toBe(2);
|
||||
// check helpful button
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await page.getByTestId("helpful-button").click();
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.getByTestId("helpful-button").click();
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
|
||||
timeout: 10000,
|
||||
visible: false,
|
||||
});
|
||||
// check not helpful button
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await page.getByTestId("not-helpful-button").click();
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await expect(page.getByTestId("icon-ThumbDownIconCustom")).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.getByTestId("not-helpful-button").click();
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await expect(page.getByTestId("icon-ThumbDownIconCustom")).toBeVisible({
|
||||
timeout: 10000,
|
||||
visible: false,
|
||||
});
|
||||
// check switch feedback
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await page.getByTestId("helpful-button").click();
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.getByTestId("not-helpful-button").click();
|
||||
await page.getByTestId("chat-message-AI-session_after_delete").hover();
|
||||
await expect(page.getByTestId("icon-ThumbDownIconCustom")).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
|
||||
timeout: 10000,
|
||||
visible: false,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue