feat: allow flow name to be edited from the main page or by clicking header (#5525)
* Added flowData to allow changing flow name without being on the flow * Added being able to edit the flow name just by clicking the name * Added handleEdit and edit details option on flow dropdown * Added flow settings modal to grid and list * Added flowData type and select option * Update src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com> * Update src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com> * Update src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com> * Fixed useCallback import * Implemented details boolean to not show endpoint, changed title of flow settings modal * Changed Flow Settings to Edit Details * Changed Flow Settings modal description * Fixed empty name not appearing, fixed naming conditions not considered * 📝 (editFlowSettingsComponent/index.tsx): add data-testid attribute to input element for flow name for testing purposes ✨ (edit-flow-name.spec.ts): create test to ensure user can edit flow name by clicking on the header or main page, with various random names generated for testing purposes * Fixed tests that used Flow Settings * Fixed tests that rely on flow name to open edit details * Fixed tests --------- Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com> Co-authored-by: anovazzi1 <otavio2204@gmail.com>
This commit is contained in:
parent
a87bd8b57b
commit
3e482dbc29
14 changed files with 456 additions and 162 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||||
import useAddFlow from "@/hooks/flows/use-add-flow";
|
import useAddFlow from "@/hooks/flows/use-add-flow";
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { UPLOAD_ERROR_ALERT } from "@/constants/alerts_constants";
|
import { UPLOAD_ERROR_ALERT } from "@/constants/alerts_constants";
|
||||||
import { SAVED_HOVER } from "@/constants/constants";
|
import { SAVED_HOVER } from "@/constants/constants";
|
||||||
import { useGetRefreshFlowsQuery } from "@/controllers/API/queries/flows/use-get-refresh-flows-query";
|
import { useGetRefreshFlowsQuery } from "@/controllers/API/queries/flows/use-get-refresh-flows-query";
|
||||||
|
|
@ -54,8 +55,26 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
const onFlowPage = useFlowStore((state) => state.onFlowPage);
|
const onFlowPage = useFlowStore((state) => state.onFlowPage);
|
||||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||||
const stopBuilding = useFlowStore((state) => state.stopBuilding);
|
const stopBuilding = useFlowStore((state) => state.stopBuilding);
|
||||||
|
const [editingName, setEditingName] = useState(false);
|
||||||
|
const [flowName, setFlowName] = useState(currentFlow?.name ?? "");
|
||||||
|
const [isInvalidName, setIsInvalidName] = useState(false);
|
||||||
|
const nameInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [inputWidth, setInputWidth] = useState<number>(0);
|
||||||
|
const measureRef = useRef<HTMLSpanElement>(null);
|
||||||
|
|
||||||
const { data: folders, isFetched: isFoldersFetched } = useGetFoldersQuery();
|
const { data: folders, isFetched: isFoldersFetched } = useGetFoldersQuery();
|
||||||
|
const flows = useFlowsManagerStore((state) => state.flows);
|
||||||
|
const [nameLists, setNameList] = useState<string[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (flows) {
|
||||||
|
const tempNameList: string[] = [];
|
||||||
|
flows.forEach((flow) => {
|
||||||
|
tempNameList.push(flow.name);
|
||||||
|
});
|
||||||
|
setNameList(tempNameList.filter((name) => name !== currentFlow?.name));
|
||||||
|
}
|
||||||
|
}, [flows, currentFlow?.name]);
|
||||||
|
|
||||||
useGetRefreshFlowsQuery(
|
useGetRefreshFlowsQuery(
|
||||||
{
|
{
|
||||||
|
|
@ -73,6 +92,12 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
const changesNotSaved =
|
const changesNotSaved =
|
||||||
customStringify(currentFlow) !== customStringify(currentSavedFlow);
|
customStringify(currentFlow) !== customStringify(currentSavedFlow);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (measureRef.current) {
|
||||||
|
setInputWidth(measureRef.current.offsetWidth);
|
||||||
|
}
|
||||||
|
}, [flowName]);
|
||||||
|
|
||||||
function handleAddFlow() {
|
function handleAddFlow() {
|
||||||
try {
|
try {
|
||||||
addFlow().then((id) => {
|
addFlow().then((id) => {
|
||||||
|
|
@ -113,6 +138,80 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
const changes = useShortcutsStore((state) => state.changesSave);
|
const changes = useShortcutsStore((state) => state.changesSave);
|
||||||
useHotkeys(changes, handleSave, { preventDefault: true });
|
useHotkeys(changes, handleSave, { preventDefault: true });
|
||||||
|
|
||||||
|
const handleEditName = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { value } = e.target;
|
||||||
|
let invalid = false;
|
||||||
|
for (let i = 0; i < nameLists.length; i++) {
|
||||||
|
if (value === nameLists[i]) {
|
||||||
|
invalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsInvalidName(invalid);
|
||||||
|
setFlowName(value);
|
||||||
|
},
|
||||||
|
[nameLists],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleKeyDown = useCallback(
|
||||||
|
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
setEditingName(false);
|
||||||
|
setFlowName(currentFlow?.name ?? "");
|
||||||
|
setIsInvalidName(false);
|
||||||
|
}
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
nameInputRef.current?.blur();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentFlow?.name],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleNameSubmit = useCallback(() => {
|
||||||
|
if (
|
||||||
|
flowName.trim() !== "" &&
|
||||||
|
flowName !== currentFlow?.name &&
|
||||||
|
!isInvalidName
|
||||||
|
) {
|
||||||
|
const newFlow = {
|
||||||
|
...currentFlow!,
|
||||||
|
name: flowName,
|
||||||
|
id: currentFlow!.id,
|
||||||
|
};
|
||||||
|
setCurrentFlow(newFlow);
|
||||||
|
saveFlow(newFlow)
|
||||||
|
.then(() => {
|
||||||
|
setSuccessData({ title: "Flow name updated successfully" });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setErrorData({
|
||||||
|
title: "Error updating flow name",
|
||||||
|
list: [(error as Error).message],
|
||||||
|
});
|
||||||
|
setFlowName(currentFlow?.name ?? "");
|
||||||
|
});
|
||||||
|
} else if (isInvalidName) {
|
||||||
|
setErrorData({
|
||||||
|
title: "Invalid flow name",
|
||||||
|
list: ["Name already exists"],
|
||||||
|
});
|
||||||
|
setFlowName(currentFlow?.name ?? "");
|
||||||
|
} else {
|
||||||
|
setFlowName(currentFlow?.name ?? "");
|
||||||
|
}
|
||||||
|
setEditingName(false);
|
||||||
|
setIsInvalidName(false);
|
||||||
|
}, [
|
||||||
|
flowName,
|
||||||
|
currentFlow,
|
||||||
|
setCurrentFlow,
|
||||||
|
saveFlow,
|
||||||
|
setSuccessData,
|
||||||
|
setErrorData,
|
||||||
|
isInvalidName,
|
||||||
|
]);
|
||||||
|
|
||||||
return currentFlow && onFlowPage ? (
|
return currentFlow && onFlowPage ? (
|
||||||
<div className="flex items-center justify-center gap-2 truncate">
|
<div className="flex items-center justify-center gap-2 truncate">
|
||||||
<div className="header-menu-bar hidden w-20 max-w-fit grow justify-end truncate md:flex">
|
<div className="header-menu-bar hidden w-20 max-w-fit grow justify-end truncate md:flex">
|
||||||
|
|
@ -138,148 +237,191 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="overflow-hidden truncate text-sm sm:whitespace-normal">
|
<div className="overflow-hidden truncate text-sm sm:whitespace-normal">
|
||||||
<DropdownMenu>
|
<div className="header-menu-bar-display-2 truncate">
|
||||||
<DropdownMenuTrigger asChild>
|
<div
|
||||||
<div className="header-menu-bar-display-2 group truncate">
|
className="header-menu-flow-name-2 truncate"
|
||||||
|
data-testid="flow-configuration-button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
ref={measureRef}
|
||||||
|
className="invisible absolute font-semibold"
|
||||||
|
style={{ whiteSpace: "pre" }}
|
||||||
|
>
|
||||||
|
{flowName}
|
||||||
|
</span>
|
||||||
|
{editingName ? (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
className={cn(
|
||||||
|
"h-6 px-0 font-semibold focus:border-0",
|
||||||
|
isInvalidName &&
|
||||||
|
"border-status-red focus-visible:ring-status-red",
|
||||||
|
)}
|
||||||
|
style={{ width: `${inputWidth + 1}px` }}
|
||||||
|
onChange={handleEditName}
|
||||||
|
maxLength={38}
|
||||||
|
ref={nameInputRef}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
autoFocus={true}
|
||||||
|
onBlur={handleNameSubmit}
|
||||||
|
value={flowName}
|
||||||
|
id="input-flow-name"
|
||||||
|
data-testid="input-flow-name"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
<div
|
<div
|
||||||
className="header-menu-flow-name-2 truncate"
|
className="truncate font-semibold text-primary"
|
||||||
data-testid="flow-configuration-button"
|
data-testid="flow_name"
|
||||||
|
onClick={() => {
|
||||||
|
setEditingName(true);
|
||||||
|
setFlowName(currentFlow.name);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div
|
{currentFlow.name}
|
||||||
className="truncate font-semibold group-hover:text-primary dark:text-[white]"
|
|
||||||
data-testid="flow_name"
|
|
||||||
>
|
|
||||||
{currentFlow.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger
|
||||||
|
className="group"
|
||||||
|
data-testid="flow_menu_trigger"
|
||||||
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="ChevronDown"
|
name="ChevronDown"
|
||||||
className="flex h-5 w-5 text-muted-foreground group-hover:text-primary"
|
className="flex h-5 w-5 text-muted-foreground hover:text-primary"
|
||||||
/>
|
/>
|
||||||
</div>
|
</DropdownMenuTrigger>
|
||||||
</DropdownMenuTrigger>
|
<DropdownMenuContent className="w-44 bg-white dark:bg-background">
|
||||||
<DropdownMenuContent className="w-44 bg-white dark:bg-background">
|
<DropdownMenuLabel>Options</DropdownMenuLabel>
|
||||||
<DropdownMenuLabel>Options</DropdownMenuLabel>
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
onClick={() => {
|
||||||
onClick={() => {
|
handleAddFlow();
|
||||||
handleAddFlow();
|
}}
|
||||||
}}
|
className="cursor-pointer"
|
||||||
className="cursor-pointer"
|
>
|
||||||
>
|
<IconComponent name="Plus" className="header-menu-options" />
|
||||||
<IconComponent name="Plus" className="header-menu-options" />
|
New
|
||||||
New
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenSettings(true);
|
setOpenSettings(true);
|
||||||
}}
|
}}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
<IconComponent name="Settings2" className="header-menu-options" />
|
<IconComponent
|
||||||
Flow Settings
|
name="SquarePen"
|
||||||
</DropdownMenuItem>
|
className="header-menu-options"
|
||||||
{!autoSaving && (
|
/>
|
||||||
<DropdownMenuItem onClick={handleSave} className="cursor-pointer">
|
Edit Details
|
||||||
|
</DropdownMenuItem>
|
||||||
|
{!autoSaving && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={handleSave}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
<ToolbarSelectItem
|
||||||
|
value="Save"
|
||||||
|
icon="Save"
|
||||||
|
dataTestId=""
|
||||||
|
shortcut={
|
||||||
|
shortcuts.find(
|
||||||
|
(s) => s.name.toLowerCase() === "changes save",
|
||||||
|
)?.shortcut!
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setOpenLogs(true);
|
||||||
|
}}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="ScrollText"
|
||||||
|
className="header-menu-options"
|
||||||
|
/>
|
||||||
|
Logs
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
uploadFlow({ position: { x: 300, y: 100 } })
|
||||||
|
.then(() => {
|
||||||
|
setSuccessData({
|
||||||
|
title: "Uploaded successfully",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setErrorData({
|
||||||
|
title: UPLOAD_ERROR_ALERT,
|
||||||
|
list: [(error as Error).message],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconComponent name="FileUp" className="header-menu-options" />
|
||||||
|
Import
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<ExportModal>
|
||||||
|
<div className="header-menubar-item">
|
||||||
|
<IconComponent
|
||||||
|
name="FileDown"
|
||||||
|
className="header-menu-options"
|
||||||
|
/>
|
||||||
|
Export
|
||||||
|
</div>
|
||||||
|
</ExportModal>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
undo();
|
||||||
|
}}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
<ToolbarSelectItem
|
<ToolbarSelectItem
|
||||||
value="Save"
|
value="Undo"
|
||||||
icon="Save"
|
icon="Undo"
|
||||||
dataTestId=""
|
dataTestId=""
|
||||||
shortcut={
|
shortcut={
|
||||||
shortcuts.find(
|
shortcuts.find((s) => s.name.toLowerCase() === "undo")
|
||||||
(s) => s.name.toLowerCase() === "changes save",
|
?.shortcut!
|
||||||
)?.shortcut!
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
onClick={() => {
|
||||||
onClick={() => {
|
redo();
|
||||||
setOpenLogs(true);
|
}}
|
||||||
}}
|
className="cursor-pointer"
|
||||||
className="cursor-pointer"
|
>
|
||||||
>
|
<ToolbarSelectItem
|
||||||
<IconComponent
|
value="Redo"
|
||||||
name="ScrollText"
|
icon="Redo"
|
||||||
className="header-menu-options"
|
dataTestId=""
|
||||||
/>
|
shortcut={
|
||||||
Logs
|
shortcuts.find((s) => s.name.toLowerCase() === "redo")
|
||||||
</DropdownMenuItem>
|
?.shortcut!
|
||||||
<DropdownMenuItem
|
}
|
||||||
className="cursor-pointer"
|
/>
|
||||||
onClick={() => {
|
</DropdownMenuItem>
|
||||||
uploadFlow({ position: { x: 300, y: 100 } })
|
<DropdownMenuItem
|
||||||
.then(() => {
|
onClick={() => {
|
||||||
setSuccessData({
|
handleReloadComponents();
|
||||||
title: "Uploaded successfully",
|
}}
|
||||||
});
|
className="cursor-pointer"
|
||||||
})
|
>
|
||||||
.catch((error) => {
|
|
||||||
setErrorData({
|
|
||||||
title: UPLOAD_ERROR_ALERT,
|
|
||||||
list: [(error as Error).message],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconComponent name="FileUp" className="header-menu-options" />
|
|
||||||
Import
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<ExportModal>
|
|
||||||
<div className="header-menubar-item">
|
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="FileDown"
|
name="RefreshCcw"
|
||||||
className="header-menu-options"
|
className="header-menu-options"
|
||||||
/>
|
/>
|
||||||
Export
|
Refresh All
|
||||||
</div>
|
</DropdownMenuItem>
|
||||||
</ExportModal>
|
</DropdownMenuContent>
|
||||||
<DropdownMenuItem
|
</DropdownMenu>
|
||||||
onClick={() => {
|
</div>
|
||||||
undo();
|
|
||||||
}}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
<ToolbarSelectItem
|
|
||||||
value="Undo"
|
|
||||||
icon="Undo"
|
|
||||||
dataTestId=""
|
|
||||||
shortcut={
|
|
||||||
shortcuts.find((s) => s.name.toLowerCase() === "undo")
|
|
||||||
?.shortcut!
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => {
|
|
||||||
redo();
|
|
||||||
}}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
<ToolbarSelectItem
|
|
||||||
value="Redo"
|
|
||||||
icon="Redo"
|
|
||||||
dataTestId=""
|
|
||||||
shortcut={
|
|
||||||
shortcuts.find((s) => s.name.toLowerCase() === "redo")
|
|
||||||
?.shortcut!
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => {
|
|
||||||
handleReloadComponents();
|
|
||||||
}}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
name="RefreshCcw"
|
|
||||||
className="header-menu-options"
|
|
||||||
/>
|
|
||||||
Refresh All
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
<FlowSettingsModal
|
<FlowSettingsModal
|
||||||
open={openSettings}
|
open={openSettings}
|
||||||
setOpen={setOpenSettings}
|
setOpen={setOpenSettings}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
||||||
onDoubleClickCapture={(event) => {
|
onDoubleClickCapture={(event) => {
|
||||||
handleFocus(event);
|
handleFocus(event);
|
||||||
}}
|
}}
|
||||||
|
data-testid="input-flow-name"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="font-normal text-muted-foreground word-break-break-word">
|
<span className="font-normal text-muted-foreground word-break-break-word">
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ export const EXPORT_DIALOG_SUBTITLE = "Export flow as JSON file.";
|
||||||
* @constant
|
* @constant
|
||||||
*/
|
*/
|
||||||
export const SETTINGS_DIALOG_SUBTITLE =
|
export const SETTINGS_DIALOG_SUBTITLE =
|
||||||
"Customize workspace settings and preferences.";
|
"Customize your flow details and settings.";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base text for subtitle of Flow Logs (Menubar)
|
* The base text for subtitle of Flow Logs (Menubar)
|
||||||
|
|
|
||||||
|
|
@ -15,29 +15,30 @@ import BaseModal from "../baseModal";
|
||||||
export default function FlowSettingsModal({
|
export default function FlowSettingsModal({
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
|
flowData,
|
||||||
|
details,
|
||||||
}: FlowSettingsPropsType): JSX.Element {
|
}: FlowSettingsPropsType): JSX.Element {
|
||||||
const saveFlow = useSaveFlow();
|
const saveFlow = useSaveFlow();
|
||||||
const currentFlow = useFlowStore((state) => state.currentFlow);
|
const currentFlow = useFlowStore((state) => state.currentFlow);
|
||||||
const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow);
|
const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow);
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const flows = useFlowsManagerStore((state) => state.flows);
|
const flows = useFlowsManagerStore((state) => state.flows);
|
||||||
|
const flow = flowData ?? currentFlow;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setName(currentFlow!.name);
|
setName(flow!.name);
|
||||||
setDescription(currentFlow!.description);
|
setDescription(flow!.description);
|
||||||
}, [currentFlow?.name, currentFlow?.description, open]);
|
}, [flow?.name, flow?.description, open]);
|
||||||
|
|
||||||
const [name, setName] = useState(currentFlow!.name);
|
const [name, setName] = useState(flow!.name);
|
||||||
const [description, setDescription] = useState(currentFlow!.description);
|
const [description, setDescription] = useState(flow!.description);
|
||||||
const [endpoint_name, setEndpointName] = useState(
|
const [endpoint_name, setEndpointName] = useState(flow!.endpoint_name ?? "");
|
||||||
currentFlow!.endpoint_name ?? "",
|
|
||||||
);
|
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [disableSave, setDisableSave] = useState(true);
|
const [disableSave, setDisableSave] = useState(true);
|
||||||
const autoSaving = useFlowsManagerStore((state) => state.autoSaving);
|
const autoSaving = useFlowsManagerStore((state) => state.autoSaving);
|
||||||
function handleClick(): void {
|
function handleClick(): void {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
if (!currentFlow) return;
|
if (!flow) return;
|
||||||
const newFlow = cloneDeep(currentFlow);
|
const newFlow = cloneDeep(flow);
|
||||||
newFlow.name = name;
|
newFlow.name = name;
|
||||||
newFlow.description = description;
|
newFlow.description = description;
|
||||||
newFlow.endpoint_name =
|
newFlow.endpoint_name =
|
||||||
|
|
@ -67,22 +68,22 @@ export default function FlowSettingsModal({
|
||||||
flows.forEach((flow: FlowType) => {
|
flows.forEach((flow: FlowType) => {
|
||||||
tempNameList.push(flow.name);
|
tempNameList.push(flow.name);
|
||||||
});
|
});
|
||||||
setNameList(tempNameList.filter((name) => name !== currentFlow!.name));
|
setNameList(tempNameList.filter((name) => name !== flow!.name));
|
||||||
}
|
}
|
||||||
}, [flows]);
|
}, [flows]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
(!nameLists.includes(name) && currentFlow?.name !== name) ||
|
(!nameLists.includes(name) && flow?.name !== name) ||
|
||||||
currentFlow?.description !== description ||
|
flow?.description !== description ||
|
||||||
((currentFlow?.endpoint_name ?? "") !== endpoint_name &&
|
((flow?.endpoint_name ?? "") !== endpoint_name &&
|
||||||
isEndpointNameValid(endpoint_name ?? "", 50))
|
isEndpointNameValid(endpoint_name ?? "", 50))
|
||||||
) {
|
) {
|
||||||
setDisableSave(false);
|
setDisableSave(false);
|
||||||
} else {
|
} else {
|
||||||
setDisableSave(true);
|
setDisableSave(true);
|
||||||
}
|
}
|
||||||
}, [nameLists, currentFlow, description, endpoint_name, name]);
|
}, [nameLists, flow, description, endpoint_name, name]);
|
||||||
return (
|
return (
|
||||||
<BaseModal
|
<BaseModal
|
||||||
open={open}
|
open={open}
|
||||||
|
|
@ -91,8 +92,8 @@ export default function FlowSettingsModal({
|
||||||
onSubmit={handleClick}
|
onSubmit={handleClick}
|
||||||
>
|
>
|
||||||
<BaseModal.Header description={SETTINGS_DIALOG_SUBTITLE}>
|
<BaseModal.Header description={SETTINGS_DIALOG_SUBTITLE}>
|
||||||
<span className="pr-2">Settings</span>
|
<span className="pr-2">Details</span>
|
||||||
<IconComponent name="Settings2" className="mr-2 h-4 w-4" />
|
<IconComponent name="SquarePen" className="mr-2 h-4 w-4" />
|
||||||
</BaseModal.Header>
|
</BaseModal.Header>
|
||||||
<BaseModal.Content>
|
<BaseModal.Content>
|
||||||
<EditFlowSettings
|
<EditFlowSettings
|
||||||
|
|
@ -102,7 +103,7 @@ export default function FlowSettingsModal({
|
||||||
endpointName={endpoint_name}
|
endpointName={endpoint_name}
|
||||||
setName={setName}
|
setName={setName}
|
||||||
setDescription={setDescription}
|
setDescription={setDescription}
|
||||||
setEndpointName={setEndpointName}
|
setEndpointName={details ? undefined : setEndpointName}
|
||||||
/>
|
/>
|
||||||
</BaseModal.Content>
|
</BaseModal.Content>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@ type DropdownComponentProps = {
|
||||||
flowData: FlowType;
|
flowData: FlowType;
|
||||||
setOpenDelete: (open: boolean) => void;
|
setOpenDelete: (open: boolean) => void;
|
||||||
handlePlaygroundClick?: () => void;
|
handlePlaygroundClick?: () => void;
|
||||||
|
handleEdit: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DropdownComponent = ({
|
const DropdownComponent = ({
|
||||||
flowData,
|
flowData,
|
||||||
setOpenDelete,
|
setOpenDelete,
|
||||||
|
handleEdit,
|
||||||
}: DropdownComponentProps) => {
|
}: DropdownComponentProps) => {
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
|
|
@ -29,17 +31,32 @@ const DropdownComponent = ({
|
||||||
downloadFlow(flowData, flowData.name, flowData.description);
|
downloadFlow(flowData, flowData.name, flowData.description);
|
||||||
setSuccessData({ title: `${flowData.name} exported successfully` });
|
setSuccessData({ title: `${flowData.name} exported successfully` });
|
||||||
};
|
};
|
||||||
|
|
||||||
const { handleSelectOptionsChange } = useSelectOptionsChange(
|
const { handleSelectOptionsChange } = useSelectOptionsChange(
|
||||||
[flowData.id],
|
[flowData.id],
|
||||||
setErrorData,
|
setErrorData,
|
||||||
setOpenDelete,
|
setOpenDelete,
|
||||||
handleDuplicate,
|
handleDuplicate,
|
||||||
handleExport,
|
handleExport,
|
||||||
|
handleEdit,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleSelectOptionsChange("edit");
|
||||||
|
}}
|
||||||
|
className="cursor-pointer"
|
||||||
|
data-testid="btn-edit-flow"
|
||||||
|
>
|
||||||
|
<ForwardedIconComponent
|
||||||
|
name="SquarePen"
|
||||||
|
aria-hidden="true"
|
||||||
|
className="mr-2 h-4 w-4"
|
||||||
|
/>
|
||||||
|
Edit details
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import {
|
||||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||||
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
||||||
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
||||||
|
import FlowSettingsModal from "@/modals/flowSettingsModal";
|
||||||
import useAlertStore from "@/stores/alertStore";
|
import useAlertStore from "@/stores/alertStore";
|
||||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||||
import { FlowType } from "@/types/flow";
|
import { FlowType } from "@/types/flow";
|
||||||
import { getInputsAndOutputs } from "@/utils/storeUtils";
|
|
||||||
import { swatchColors } from "@/utils/styleUtils";
|
import { swatchColors } from "@/utils/styleUtils";
|
||||||
import { cn, getNumberFromString } from "@/utils/utils";
|
import { cn, getNumberFromString } from "@/utils/utils";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
@ -27,6 +27,7 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||||
const navigate = useCustomNavigate();
|
const navigate = useCustomNavigate();
|
||||||
|
|
||||||
const [openDelete, setOpenDelete] = useState(false);
|
const [openDelete, setOpenDelete] = useState(false);
|
||||||
|
const [openSettings, setOpenSettings] = useState(false);
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const { deleteFlow } = useDeleteFlow();
|
const { deleteFlow } = useDeleteFlow();
|
||||||
|
|
||||||
|
|
@ -124,6 +125,9 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
flowData={flowData}
|
flowData={flowData}
|
||||||
setOpenDelete={setOpenDelete}
|
setOpenDelete={setOpenDelete}
|
||||||
|
handleEdit={() => {
|
||||||
|
setOpenSettings(true);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
@ -145,6 +149,12 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||||
<></>
|
<></>
|
||||||
</DeleteConfirmationModal>
|
</DeleteConfirmationModal>
|
||||||
)}
|
)}
|
||||||
|
<FlowSettingsModal
|
||||||
|
open={openSettings}
|
||||||
|
setOpen={setOpenSettings}
|
||||||
|
flowData={flowData}
|
||||||
|
details
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||||
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
import useDeleteFlow from "@/hooks/flows/use-delete-flow";
|
||||||
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
import DeleteConfirmationModal from "@/modals/deleteConfirmationModal";
|
||||||
|
import FlowSettingsModal from "@/modals/flowSettingsModal";
|
||||||
import useAlertStore from "@/stores/alertStore";
|
import useAlertStore from "@/stores/alertStore";
|
||||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||||
import { FlowType } from "@/types/flow";
|
import { FlowType } from "@/types/flow";
|
||||||
|
|
@ -30,6 +31,7 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||||
const { deleteFlow } = useDeleteFlow();
|
const { deleteFlow } = useDeleteFlow();
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const { folderId } = useParams();
|
const { folderId } = useParams();
|
||||||
|
const [openSettings, setOpenSettings] = useState(false);
|
||||||
const isComponent = flowData.is_component ?? false;
|
const isComponent = flowData.is_component ?? false;
|
||||||
const setFlowToCanvas = useFlowsManagerStore(
|
const setFlowToCanvas = useFlowsManagerStore(
|
||||||
(state) => state.setFlowToCanvas,
|
(state) => state.setFlowToCanvas,
|
||||||
|
|
@ -144,6 +146,9 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
flowData={flowData}
|
flowData={flowData}
|
||||||
setOpenDelete={setOpenDelete}
|
setOpenDelete={setOpenDelete}
|
||||||
|
handleEdit={() => {
|
||||||
|
setOpenSettings(true);
|
||||||
|
}}
|
||||||
handlePlaygroundClick={() => {
|
handlePlaygroundClick={() => {
|
||||||
// handlePlaygroundClick();
|
// handlePlaygroundClick();
|
||||||
}}
|
}}
|
||||||
|
|
@ -163,6 +168,12 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => {
|
||||||
<></>
|
<></>
|
||||||
</DeleteConfirmationModal>
|
</DeleteConfirmationModal>
|
||||||
)}
|
)}
|
||||||
|
<FlowSettingsModal
|
||||||
|
open={openSettings}
|
||||||
|
setOpen={setOpenSettings}
|
||||||
|
flowData={flowData}
|
||||||
|
details
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const useSelectOptionsChange = (
|
||||||
setOpenDelete: (value: boolean) => void,
|
setOpenDelete: (value: boolean) => void,
|
||||||
handleDuplicate: () => void,
|
handleDuplicate: () => void,
|
||||||
handleExport: () => void,
|
handleExport: () => void,
|
||||||
|
handleEdit: () => void,
|
||||||
) => {
|
) => {
|
||||||
const handleSelectOptionsChange = useCallback(
|
const handleSelectOptionsChange = useCallback(
|
||||||
(action) => {
|
(action) => {
|
||||||
|
|
@ -23,6 +24,8 @@ const useSelectOptionsChange = (
|
||||||
handleDuplicate();
|
handleDuplicate();
|
||||||
} else if (action === "export") {
|
} else if (action === "export") {
|
||||||
handleExport();
|
handleExport();
|
||||||
|
} else if (action === "edit") {
|
||||||
|
handleEdit();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
|
@ -31,6 +34,7 @@ const useSelectOptionsChange = (
|
||||||
setOpenDelete,
|
setOpenDelete,
|
||||||
handleDuplicate,
|
handleDuplicate,
|
||||||
handleExport,
|
handleExport,
|
||||||
|
handleEdit,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -672,6 +672,8 @@ export type buttonBoxPropsType = {
|
||||||
export type FlowSettingsPropsType = {
|
export type FlowSettingsPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: (open: boolean) => void;
|
setOpen: (open: boolean) => void;
|
||||||
|
details?: boolean;
|
||||||
|
flowData?: FlowType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type groupDataType = {
|
export type groupDataType = {
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,8 @@ test(
|
||||||
await page.getByTestId("fit_view").click();
|
await page.getByTestId("fit_view").click();
|
||||||
await page.getByTestId("zoom_out").click();
|
await page.getByTestId("zoom_out").click();
|
||||||
|
|
||||||
await page.getByTestId("flow-configuration-button").click();
|
await page.getByTestId("flow_menu_trigger").click();
|
||||||
await page.getByText("Flow Settings", { exact: true }).last().click();
|
await page.getByText("Edit Details", { exact: true }).last().click();
|
||||||
|
|
||||||
await page.getByPlaceholder("Flow Name").fill(randomFlowName);
|
await page.getByPlaceholder("Flow Name").fill(randomFlowName);
|
||||||
|
|
||||||
|
|
@ -201,8 +201,8 @@ test(
|
||||||
await page.getByTestId("fit_view").click();
|
await page.getByTestId("fit_view").click();
|
||||||
await page.getByTestId("zoom_out").click();
|
await page.getByTestId("zoom_out").click();
|
||||||
|
|
||||||
await page.getByTestId("flow-configuration-button").click();
|
await page.getByTestId("flow_menu_trigger").click();
|
||||||
await page.getByText("Flow Settings", { exact: true }).last().click();
|
await page.getByText("Edit Details", { exact: true }).last().click();
|
||||||
|
|
||||||
await page.getByPlaceholder("Flow Name").fill(secondRandomFlowName);
|
await page.getByPlaceholder("Flow Name").fill(secondRandomFlowName);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,8 @@ test("should share component with share button", async ({ page }) => {
|
||||||
await page.getByRole("heading", { name: "Basic Prompting" }).click();
|
await page.getByRole("heading", { name: "Basic Prompting" }).click();
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
const flowName = await page.getByTestId("flow_name").innerText();
|
const flowName = await page.getByTestId("flow_name").innerText();
|
||||||
await page.getByTestId("flow_name").click();
|
await page.getByTestId("flow_menu_trigger").click();
|
||||||
await page.getByText("Flow Settings").click();
|
await page.getByText("Edit Details").click();
|
||||||
const flowDescription = await page
|
const flowDescription = await page
|
||||||
.getByPlaceholder("Flow description")
|
.getByPlaceholder("Flow description")
|
||||||
.inputValue();
|
.inputValue();
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ test(
|
||||||
|
|
||||||
await page.getByTestId("fit_view").click();
|
await page.getByTestId("fit_view").click();
|
||||||
|
|
||||||
await page.getByTestId("flow-configuration-button").click();
|
await page.getByTestId("flow_menu_trigger").click();
|
||||||
await page.getByText("Flow Settings").click();
|
await page.getByText("Edit Details").click();
|
||||||
await page.getByPlaceholder("Flow name").fill(randomName);
|
await page.getByPlaceholder("Flow name").fill(randomName);
|
||||||
await page.getByText("Save").last().click();
|
await page.getByText("Save").last().click();
|
||||||
await page.getByTestId("icon-ChevronLeft").last().click();
|
await page.getByTestId("icon-ChevronLeft").last().click();
|
||||||
|
|
@ -72,8 +72,8 @@ test(
|
||||||
|
|
||||||
await page.getByTestId(`card-${randomName}`).first().click();
|
await page.getByTestId(`card-${randomName}`).first().click();
|
||||||
|
|
||||||
await page.getByTestId("flow-configuration-button").click();
|
await page.getByTestId("flow_menu_trigger").click();
|
||||||
await page.getByText("Flow Settings").click();
|
await page.getByText("Edit Details").click();
|
||||||
await page.getByPlaceholder("Flow name").fill(secondRandomName);
|
await page.getByPlaceholder("Flow name").fill(secondRandomName);
|
||||||
await page.getByText("Save").last().click();
|
await page.getByText("Save").last().click();
|
||||||
await page.getByTestId("icon-ChevronLeft").last().click();
|
await page.getByTestId("icon-ChevronLeft").last().click();
|
||||||
|
|
|
||||||
106
src/frontend/tests/extended/features/edit-flow-name.spec.ts
Normal file
106
src/frontend/tests/extended/features/edit-flow-name.spec.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
|
import { simulateDragAndDrop } from "../../utils/simulate-drag-and-drop";
|
||||||
|
test(
|
||||||
|
"user should be able to edit flow name by clicking on the header or on the main page",
|
||||||
|
{ tag: ["@release"] },
|
||||||
|
async ({ page }) => {
|
||||||
|
const randomName = Math.random().toString(36).substring(2, 15);
|
||||||
|
const randomName2 = Math.random().toString(36).substring(2, 15);
|
||||||
|
const randomName3 = Math.random().toString(36).substring(2, 15);
|
||||||
|
const randomName4 = Math.random().toString(36).substring(2, 15);
|
||||||
|
|
||||||
|
await awaitBootstrapTest(page);
|
||||||
|
|
||||||
|
await page.getByRole("heading", { name: "Basic Prompting" }).click();
|
||||||
|
|
||||||
|
await page.getByTestId("flow_name").click();
|
||||||
|
|
||||||
|
await page.getByTestId("input-flow-name").fill(randomName);
|
||||||
|
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
let flowName = await page.getByTestId("flow_name").textContent();
|
||||||
|
|
||||||
|
expect(flowName).toBe(randomName);
|
||||||
|
|
||||||
|
await page.getByTestId("icon-ChevronLeft").first().click();
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="home-dropdown-menu"]', {
|
||||||
|
timeout: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForSelector(`text=${randomName}`, {
|
||||||
|
timeout: 3000,
|
||||||
|
state: "visible",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await page.getByText(randomName).count()).toBe(1);
|
||||||
|
|
||||||
|
await page.getByText(randomName).click();
|
||||||
|
|
||||||
|
await page.getByTestId("flow_name").click();
|
||||||
|
|
||||||
|
await page.getByTestId("input-flow-name").fill(randomName2);
|
||||||
|
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
flowName = await page.getByTestId("flow_name").textContent();
|
||||||
|
|
||||||
|
expect(flowName).toBe(randomName2);
|
||||||
|
|
||||||
|
await page.getByTestId("icon-ChevronLeft").first().click();
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="home-dropdown-menu"]', {
|
||||||
|
timeout: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForSelector(`text=${randomName2}`, {
|
||||||
|
timeout: 3000,
|
||||||
|
state: "visible",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await page.getByText(randomName2).count()).toBe(1);
|
||||||
|
|
||||||
|
await page.getByTestId("home-dropdown-menu").first().click();
|
||||||
|
|
||||||
|
await page.getByTestId("btn-edit-flow").click();
|
||||||
|
|
||||||
|
await page.getByTestId("input-flow-name").fill(randomName3);
|
||||||
|
|
||||||
|
await page.getByTestId("save-flow-settings").click();
|
||||||
|
|
||||||
|
await page.waitForSelector(`text=${randomName3}`, {
|
||||||
|
timeout: 3000,
|
||||||
|
state: "visible",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await page.getByText(randomName3).count()).toBe(1);
|
||||||
|
|
||||||
|
await page.getByText(randomName3).click();
|
||||||
|
|
||||||
|
await page.getByTestId("flow_name").click();
|
||||||
|
|
||||||
|
await page.getByTestId("input-flow-name").fill(randomName4);
|
||||||
|
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
|
||||||
|
flowName = await page.getByTestId("flow_name").textContent();
|
||||||
|
|
||||||
|
expect(flowName).toBe(randomName4);
|
||||||
|
|
||||||
|
await page.getByTestId("icon-ChevronLeft").first().click();
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="home-dropdown-menu"]', {
|
||||||
|
timeout: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForSelector(`text=${randomName4}`, {
|
||||||
|
timeout: 3000,
|
||||||
|
state: "visible",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await page.getByText(randomName4).count()).toBe(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
@ -15,8 +15,8 @@ test(
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByTestId("flow_name").click();
|
await page.getByTestId("flow_menu_trigger").click();
|
||||||
await page.getByText("Flow Settings").first().click();
|
await page.getByText("Edit Details").first().click();
|
||||||
await page
|
await page
|
||||||
.getByPlaceholder("Flow name")
|
.getByPlaceholder("Flow name")
|
||||||
.fill(
|
.fill(
|
||||||
|
|
@ -39,8 +39,8 @@ test(
|
||||||
|
|
||||||
await page.getByText("Changes saved successfully").isVisible();
|
await page.getByText("Changes saved successfully").isVisible();
|
||||||
|
|
||||||
await page.getByTestId("flow_name").click();
|
await page.getByTestId("flow_menu_trigger").click();
|
||||||
await page.getByText("Flow Settings").first().click();
|
await page.getByText("Edit Details").first().click();
|
||||||
|
|
||||||
const flowName = await page.getByPlaceholder("Flow name").inputValue();
|
const flowName = await page.getByPlaceholder("Flow name").inputValue();
|
||||||
const flowDescription = await page
|
const flowDescription = await page
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue