♻️ (playwright.config.ts): disable fullyParallel and increase workers to 5 to optimize test execution

♻️ (addNewVariableButton.tsx): remove unused Button import and add data-testid to Save Variable button for better testing
♻️ (index.tsx): add preventDefault to button click handlers to prevent form submission
♻️ (sideBarFolderButtons): add data-testid attributes to buttons and inputs for better testing and readability

♻️ (api.tsx): refactor duplicate request check logic into helper function
✨ (api.tsx): add helper function to check and store duplicate requests
♻️ (genericNode): refactor code for better readability and maintainability
💡 (genericNode): add data-testid attribute for testing purposes
🐛 (use-fetch-data-on-mount): fix error handling to prevent undefined errors

🐛 (use-handle-new-value.tsx, use-handle-refresh-buttons.tsx): fix potential undefined errors in error handling
✨ (baseModal/index.tsx, flowSettingsModal/index.tsx): add data-testid attributes for better testability
♻️ (editNodeModal/index.tsx): refactor code for better readability and consistency

♻️ (nodeToolbarComponent): remove trailing commas to improve code style
♻️ (GeneralPage): reorder imports and remove trailing commas for consistency
✅ (chatInputOutput.spec.ts): update path to ChatTest.json for better file organization

✅ (chatInputOutputUser.spec.ts): add additional interactions to chat test
✅ (dragAndDrop.spec.ts): update file path for collection.json
✅ (dropdownComponent.spec.ts): fix formatting and improve locator usage

✅ (filterEdge.spec.ts): fix locator strings and remove redundant code
✅ (floatComponent.spec.ts): remove redundant cache checks and fix locator strings

✅ (flowSettings.spec.ts): update test to use data-testid for save button
✅ (folders.spec.ts): refactor folder creation and editing to use data-testid
✅ (globalVariables.spec.ts): remove redundant save button click in test

✅ (tests): update test locators to use more specific selectors

- Change locators for "Save Changes" button to use `getByText` with exact match
- Update locators for `showchroma_server_http_port` in `inputComponent.spec.ts`
- Add additional test steps in `intComponent.spec.ts` for better coverage

✅ (keyPairListComponent.spec.ts, langflowShortcuts.spec.ts, logs.spec.ts): update locators for better test stability and readability

✅ (tests): update end-to-end tests for improved stability and accuracy

✅ (textInputOutput.spec.ts): update locators and placeholders for accuracy
✅ (toggleComponent.spec.ts): replace locators with getByText for clarity
This commit is contained in:
cristhianzl 2024-06-04 17:49:58 -03:00
commit 0ae32f89d2
36 changed files with 346 additions and 331 deletions

View file

@ -15,13 +15,13 @@ dotenv.config({ path: path.resolve(__dirname, "../../.env") });
export default defineConfig({
testDir: "./tests",
/* Run tests in files in parallel */
fullyParallel: true,
fullyParallel: false,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: 1,
workers: 5,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
timeout: 120 * 1000,
// reporter: [
@ -52,18 +52,18 @@ export default defineConfig({
},
},
{
name: "firefox",
use: {
...devices["Desktop Firefox"],
launchOptions: {
firefoxUserPrefs: {
"dom.events.asyncClipboard.readText": true,
"dom.events.testing.asyncClipboard": true,
},
},
},
},
// {
// name: "firefox",
// use: {
// ...devices["Desktop Firefox"],
// launchOptions: {
// firefoxUserPrefs: {
// "dom.events.asyncClipboard.readText": true,
// "dom.events.testing.asyncClipboard": true,
// },
// },
// },
// },
],
webServer: [
{

View file

@ -7,7 +7,6 @@ import { useTypesStore } from "../../stores/typesStore";
import { ResponseErrorDetailAPI } from "../../types/api";
import ForwardedIconComponent from "../genericIconComponent";
import InputComponent from "../inputComponent";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import { Label } from "../ui/label";
import { Textarea } from "../ui/textarea";
@ -24,19 +23,19 @@ export default function AddNewVariableButton({ children }): JSX.Element {
const setErrorData = useAlertStore((state) => state.setErrorData);
const componentFields = useTypesStore((state) => state.ComponentFields);
const unavaliableFields = new Set(
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)),
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields))
);
const availableFields = () => {
const fields = Array.from(componentFields).filter(
(field) => !unavaliableFields.has(field),
(field) => !unavaliableFields.has(field)
);
return sortByName(fields);
};
const addGlobalVariable = useGlobalVariablesStore(
(state) => state.addGlobalVariable,
(state) => state.addGlobalVariable
);
function handleSaveVariable() {
@ -65,7 +64,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
let responseError = error as ResponseErrorDetailAPI;
setErrorData({
title: "Error creating variable",
list: [responseError.response.data.detail ?? "Unknown error"],
list: [responseError?.response?.data?.detail ?? "Unknown error"],
});
});
}
@ -142,7 +141,9 @@ export default function AddNewVariableButton({ children }): JSX.Element {
></InputComponent>
</div>
</BaseModal.Content>
<BaseModal.Footer submit={{ label: "Save Variable" }} />
<BaseModal.Footer
submit={{ label: "Save Variable", dataTestId: "save-variable-btn" }}
/>
</BaseModal>
);
}

View file

@ -55,10 +55,11 @@ export default function InputListComponent({
/>
{idx === value.length - 1 ? (
<button
onClick={() => {
onClick={(e) => {
let newInputList = _.cloneDeep(value);
newInputList.push("");
onChange(newInputList);
e.preventDefault();
}}
data-testid={
`input-list-plus-btn${
@ -79,10 +80,11 @@ export default function InputListComponent({
editNode ? "-edit" : ""
}_${componentName}-` + idx
}
onClick={() => {
onClick={(e) => {
let newInputList = _.cloneDeep(value);
newInputList.splice(idx, 1);
onChange(newInputList);
e.preventDefault();
}}
disabled={disabled || playgroundDisabled}
>

View file

@ -33,7 +33,7 @@ const SideBarFoldersButtonsComponent = ({
const [foldersNames, setFoldersNames] = useState({});
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const [editFolders, setEditFolderName] = useState(
folders.map((obj) => ({ name: obj.name, edit: false })),
folders.map((obj) => ({ name: obj.name, edit: false }))
);
const uploadFolder = useFolderStore((state) => state.uploadFolder);
const currentFolder = pathname.split("/");
@ -58,7 +58,7 @@ const SideBarFoldersButtonsComponent = ({
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
folderId,
handleFolderChange,
handleFolderChange
);
const handleUploadFlowsToFolder = () => {
@ -73,7 +73,7 @@ const SideBarFoldersButtonsComponent = ({
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
(res) => {
getFoldersApi(true);
},
}
);
}
@ -100,6 +100,7 @@ const SideBarFoldersButtonsComponent = ({
size="icon"
className="px-2"
onClick={addNewFolder}
data-testid="add-folder-button"
>
<ForwardedIconComponent name="FolderPlus" className="w-4" />
</Button>
@ -108,6 +109,7 @@ const SideBarFoldersButtonsComponent = ({
size="icon"
className="px-2"
onClick={handleUploadFlowsToFolder}
data-testid="upload-folder-button"
>
<ForwardedIconComponent name="Upload" className="w-4" />
</Button>
@ -117,7 +119,7 @@ const SideBarFoldersButtonsComponent = ({
<>
{folders.map((item, index) => {
const editFolderName = editFolders?.filter(
(folder) => folder.name === item.name,
(folder) => folder.name === item.name
)[0];
return (
<div
@ -133,7 +135,7 @@ const SideBarFoldersButtonsComponent = ({
? "border border-border bg-muted hover:bg-muted"
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
folderIdDragging === item.id! ? "bg-border" : "",
folderIdDragging === item.id! ? "bg-border" : ""
)}
onClick={() => handleChangeFolder!(item.id!)}
>
@ -203,7 +205,7 @@ const SideBarFoldersButtonsComponent = ({
folders.map((obj) => ({
name: obj.name,
edit: false,
})),
}))
);
}
if (e.key === "Enter") {
@ -236,10 +238,10 @@ const SideBarFoldersButtonsComponent = ({
};
const updatedFolder = await updateFolder(
body,
item.id!,
item.id!
);
const updateFolders = folders.filter(
(f) => f.name !== item.name,
(f) => f.name !== item.name
);
setFolders([...updateFolders, updatedFolder]);
setFoldersNames({});
@ -247,7 +249,7 @@ const SideBarFoldersButtonsComponent = ({
folders.map((obj) => ({
name: obj.name,
edit: false,
})),
}))
);
} else {
setFoldersNames((old) => ({
@ -258,6 +260,7 @@ const SideBarFoldersButtonsComponent = ({
}}
value={foldersNames[item.name]}
id={`input-folder-${item.name}`}
data-testid={`input-folder`}
/>
</div>
) : (

View file

@ -2,11 +2,11 @@ import axios, { AxiosError, AxiosInstance } from "axios";
import { useContext, useEffect } from "react";
import { Cookies } from "react-cookie";
import { renewAccessToken } from ".";
import { AUTHORIZED_DUPLICATE_REQUESTS } from "../../constants/constants";
import { BuildStatus } from "../../constants/enums";
import { AuthContext } from "../../contexts/authContext";
import useAlertStore from "../../stores/alertStore";
import useFlowStore from "../../stores/flowStore";
import { checkDuplicateRequestAndStoreRequest } from "./helpers/check-duplicate-requests";
// Create a new Axios instance
const api: AxiosInstance = axios.create({
@ -48,7 +48,7 @@ function ApiInterceptor() {
}
await clearBuildVerticesState(error);
return Promise.reject(error);
},
}
);
const isAuthorizedURL = (url) => {
@ -65,10 +65,10 @@ function ApiInterceptor() {
const parsedURL = new URL(url);
const isDomainAllowed = authorizedDomains.some(
(domain) => parsedURL.origin === new URL(domain).origin,
(domain) => parsedURL.origin === new URL(domain).origin
);
const isEndpointAllowed = authorizedEndpoints.some((endpoint) =>
parsedURL.pathname.includes(endpoint),
parsedURL.pathname.includes(endpoint)
);
return isDomainAllowed || isEndpointAllowed;
@ -81,28 +81,12 @@ function ApiInterceptor() {
// Request interceptor to add access token to every request
const requestInterceptor = api.interceptors.request.use(
(config) => {
const lastUrl = localStorage.getItem("lastUrlCalled");
const lastMethodCalled = localStorage.getItem("lastMethodCalled");
const checkRequest = checkDuplicateRequestAndStoreRequest(config);
const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) =>
config?.url!.includes(request),
);
if (
config?.url === lastUrl &&
!isContained &&
lastMethodCalled === config.method
) {
return Promise.reject("Duplicate request");
if (!checkRequest) {
return Promise.reject("Duplicate request.");
}
localStorage.setItem("lastUrlCalled", config.url ?? "");
localStorage.setItem("lastMethodCalled", config.method ?? "");
localStorage.setItem(
"lastRequestData",
JSON.stringify(config.data) ?? "",
);
const accessToken = cookies.get("access_token_lf");
if (accessToken && !isAuthorizedURL(config?.url)) {
config.headers["Authorization"] = `Bearer ${accessToken}`;
@ -112,7 +96,7 @@ function ApiInterceptor() {
},
(error) => {
return Promise.reject(error);
},
}
);
return () => {
@ -144,7 +128,7 @@ function ApiInterceptor() {
if (error?.config?.headers) {
delete error.config.headers["Authorization"];
error.config.headers["Authorization"] = `Bearer ${cookies.get(
"access_token_lf",
"access_token_lf"
)}`;
const response = await axios.request(error.config);
return response;

View file

@ -0,0 +1,30 @@
import { AUTHORIZED_DUPLICATE_REQUESTS } from "../../../constants/constants";
export function checkDuplicateRequestAndStoreRequest(config) {
const lastUrl = localStorage.getItem("lastUrlCalled");
const lastMethodCalled = localStorage.getItem("lastMethodCalled");
const lastRequestTime = localStorage.getItem("lastRequestTime");
const currentTime = Date.now();
const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) =>
config?.url!.includes(request),
);
if (
config?.url === lastUrl &&
!isContained &&
lastMethodCalled === config.method &&
lastMethodCalled === "get" && // Assuming you want to check only for GET requests
lastRequestTime &&
currentTime - parseInt(lastRequestTime, 10) < 800
) {
return false;
}
localStorage.setItem("lastUrlCalled", config.url ?? "");
localStorage.setItem("lastMethodCalled", config.method ?? "");
localStorage.setItem("lastRequestTime", currentTime.toString());
return true;
}

View file

@ -55,14 +55,14 @@ export default function GenericNode({
const [nodeName, setNodeName] = useState(data.node!.display_name);
const [inputDescription, setInputDescription] = useState(false);
const [nodeDescription, setNodeDescription] = useState(
data.node?.description!,
data.node?.description!
);
const [isOutdated, setIsOutdated] = useState(false);
const buildStatus = useFlowStore(
(state) => state.flowBuildStatus[data.id]?.status,
(state) => state.flowBuildStatus[data.id]?.status
);
const lastRunTime = useFlowStore(
(state) => state.flowBuildStatus[data.id]?.timestamp,
(state) => state.flowBuildStatus[data.id]?.timestamp
);
const [validationStatus, setValidationStatus] =
useState<validationStatusType | null>(null);
@ -77,10 +77,10 @@ export default function GenericNode({
// first check if data.type in NATIVE_CATEGORIES
// if not return
if (!data.node?.template?.code?.value) return;
const thisNodeTemplate = templates[data.type].template;
const thisNodeTemplate = templates[data.type]?.template;
// if the template does not have a code key
// return
if (!thisNodeTemplate.code) return;
if (!thisNodeTemplate?.code) return;
const currentCode = thisNodeTemplate.code?.value;
const thisNodesCode = data.node!.template?.code?.value;
const componentsToIgnore = ["Custom Component"];
@ -115,7 +115,7 @@ export default function GenericNode({
updateNodeInternals(data.id);
},
[data.id, data.node, setNode, setIsOutdated],
[data.id, data.node, setNode, setIsOutdated]
);
if (!data.node!.template) {
@ -255,7 +255,7 @@ export default function GenericNode({
const isDark = useDarkStore((state) => state.dark);
const renderIconStatus = (
buildStatus: BuildStatus | undefined,
validationStatus: validationStatusType | null,
validationStatus: validationStatusType | null
) => {
if (buildStatus === BuildStatus.BUILDING) {
return <Loading className="text-medium-indigo" />;
@ -296,7 +296,7 @@ export default function GenericNode({
};
const getSpecificClassFromBuildStatus = (
buildStatus: BuildStatus | undefined,
validationStatus: validationStatusType | null,
validationStatus: validationStatusType | null
) => {
let isInvalid = validationStatus && !validationStatus.valid;
@ -320,11 +320,11 @@ export default function GenericNode({
selected: boolean,
showNode: boolean,
buildStatus: BuildStatus | undefined,
validationStatus: validationStatusType | null,
validationStatus: validationStatusType | null
) => {
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
buildStatus,
validationStatus,
validationStatus
);
const baseBorderClass = getBaseBorderClass(selected);
@ -333,7 +333,7 @@ export default function GenericNode({
baseBorderClass,
nodeSizeClass,
"generic-node-div",
specificClassFromBuildStatus,
specificClassFromBuildStatus
);
return names;
};
@ -393,7 +393,7 @@ export default function GenericNode({
selected,
showNode,
buildStatus,
validationStatus,
validationStatus
)}
>
{data.node?.beta && showNode && (
@ -416,6 +416,7 @@ export default function GenericNode({
"generic-node-title-arrangement rounded-full" +
(!showNode && " justify-center ")
}
data-testid="generic-node-title-arrangement"
>
{iconNodeRender()}
{showNode && (
@ -523,7 +524,7 @@ export default function GenericNode({
}
title={getFieldTitle(
data.node?.template!,
templateField,
templateField
)}
info={data.node?.template[templateField].info}
name={templateField}
@ -551,7 +552,7 @@ export default function GenericNode({
proxy={data.node?.template[templateField].proxy}
showNode={showNode}
/>
),
)
)}
<ParameterComponent
key={scapedJSONStringfy({
@ -708,7 +709,7 @@ export default function GenericNode({
!data.node?.description) &&
nameEditable
? "font-light italic"
: "",
: ""
)}
onClick={(e) => {
setInputDescription(true);
@ -770,13 +771,13 @@ export default function GenericNode({
}
title={getFieldTitle(
data.node?.template!,
templateField,
templateField
)}
info={data.node?.template[templateField].info}
name={templateField}
tooltipTitle={
data.node?.template[templateField].input_types?.join(
"\n",
"\n"
) ?? data.node?.template[templateField].type
}
required={data.node!.template[templateField].required}
@ -803,7 +804,7 @@ export default function GenericNode({
<div
className={classNames(
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
"flex-max-width justify-center",
"flex-max-width justify-center"
)}
>
{" "}

View file

@ -9,7 +9,7 @@ const useFetchDataOnMount = (
handleUpdateValues,
setNode,
renderTooltips,
setIsLoading,
setIsLoading
) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
@ -40,7 +40,7 @@ const useFetchDataOnMount = (
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail ?? "Unknown error"],
list: [responseError?.response?.data?.detail ?? "Unknown error"],
});
}
setIsLoading(false);

View file

@ -10,7 +10,7 @@ const useHandleOnNewValue = (
debouncedHandleUpdateValues,
setNode,
renderTooltips,
setIsLoading,
setIsLoading
) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
@ -44,7 +44,9 @@ const useHandleOnNewValue = (
let responseError = error as ResponseErrorTypeAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail.error ?? "Unknown error"],
list: [
responseError?.response?.data?.detail.error ?? "Unknown error",
],
});
}
setIsLoading(false);

View file

@ -26,7 +26,7 @@ const useHandleRefreshButtonPress = (setIsLoading, setNode, renderTooltips) => {
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail ?? "Unknown error"],
list: [responseError?.response?.data?.detail ?? "Unknown error"],
});
}
setIsLoading(false);

View file

@ -71,6 +71,7 @@ const Footer: React.FC<{
icon?: ReactNode;
loading?: boolean;
disabled?: boolean;
dataTestId?: string;
};
}> = ({ children, submit }) => {
return submit ? (
@ -83,6 +84,7 @@ const Footer: React.FC<{
</Button>
</DialogClose>
<Button
data-testid={submit.dataTestId}
type="submit"
disabled={submit.loading || submit.disabled}
className="relative"
@ -90,7 +92,7 @@ const Footer: React.FC<{
<div
className={cn(
submit.loading ? "opacity-100" : "opacity-0",
"absolute self-center",
"absolute self-center"
)}
>
<ForwardedIconComponent
@ -114,7 +116,7 @@ interface BaseModalProps {
React.ReactElement<ContentProps>,
React.ReactElement<HeaderProps>,
React.ReactElement<TriggerProps>?,
React.ReactElement<FooterProps>?,
React.ReactElement<FooterProps>?
];
open?: boolean;
setOpen?: (open: boolean) => void;
@ -148,16 +150,16 @@ function BaseModal({
onSubmit,
}: BaseModalProps) {
const headerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Header,
(child) => (child as React.ReactElement).type === Header
);
const triggerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Trigger,
(child) => (child as React.ReactElement).type === Trigger
);
const ContentChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Content,
(child) => (child as React.ReactElement).type === Content
);
const ContentFooter = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Footer,
(child) => (child as React.ReactElement).type === Footer
);
let minWidth: string;

View file

@ -52,7 +52,7 @@ const EditNodeModal = forwardRef(
setOpen: (open: boolean) => void;
data: NodeDataType;
},
ref,
ref
) => {
const nodes = useFlowStore((state) => state.nodes);
@ -134,7 +134,7 @@ const EditNodeModal = forwardRef(
"edit-node-modal-box",
nodeLength > limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll"
: "",
: ""
)}
>
{nodeLength > 0 && (
@ -157,8 +157,8 @@ const EditNodeModal = forwardRef(
templateParam.charAt(0) !== "_" &&
myData.node?.template[templateParam].show &&
LANGFLOW_SUPPORTED_TYPES.has(
myData.node!.template[templateParam].type,
),
myData.node!.template[templateParam].type
)
)
.map((templateParam, index) => {
let id = {
@ -180,8 +180,8 @@ const EditNodeModal = forwardRef(
myData.node?.template[templateParam]
.proxy,
}
: id,
),
: id
)
) ?? false;
return (
<TableRow
@ -204,11 +204,11 @@ const EditNodeModal = forwardRef(
? myData.node?.template[templateParam]
.proxy?.id
: myData.node?.template[templateParam]
.display_name
? myData.node!.template[templateParam]
.display_name
: myData.node?.template[templateParam]
.name
.display_name
? myData.node!.template[templateParam]
.display_name
: myData.node?.template[templateParam]
.name
}
>
<span>
@ -263,7 +263,7 @@ const EditNodeModal = forwardRef(
onChange={(value: string[]) => {
handleOnNewValue(
value,
templateParam,
templateParam
);
}}
/>
@ -287,11 +287,11 @@ const EditNodeModal = forwardRef(
.value ?? ""
}
onChange={(
value: string | string[],
value: string | string[]
) => {
handleOnNewValue(
value,
templateParam,
templateParam
);
}}
/>
@ -341,7 +341,7 @@ const EditNodeModal = forwardRef(
].value = newValue;
handleOnNewValue(
newValue,
templateParam,
templateParam
);
}}
id="editnode-div-dict-input"
@ -358,7 +358,7 @@ const EditNodeModal = forwardRef(
myData.node!.template[templateParam].value
?.length > 1
? "my-3"
: "",
: ""
)}
>
<KeypairListComponent
@ -374,7 +374,7 @@ const EditNodeModal = forwardRef(
myData.node!.template[
templateParam
].value,
type(templateParam)!,
type(templateParam)!
)
}
duplicateKey={errorDuplicateKey}
@ -385,11 +385,11 @@ const EditNodeModal = forwardRef(
templateParam
].value = valueToNumbers;
setErrorDuplicateKey(
hasDuplicateKeys(valueToNumbers),
hasDuplicateKeys(valueToNumbers)
);
handleOnNewValue(
valueToNumbers,
templateParam,
templateParam
);
}}
isList={
@ -419,7 +419,7 @@ const EditNodeModal = forwardRef(
setEnabled={(isEnabled) => {
handleOnNewValue(
isEnabled,
templateParam,
templateParam
);
}}
size="small"
@ -643,7 +643,7 @@ const EditNodeModal = forwardRef(
<BaseModal.Footer submit={{ label: "Save Changes" }} />
</BaseModal>
);
},
}
);
export default EditNodeModal;

View file

@ -1,7 +1,6 @@
import { useEffect, useState } from "react";
import EditFlowSettings from "../../components/editFlowSettingsComponent";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants";
import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -36,7 +35,7 @@ export default function FlowSettingsModal({
.catch((err) => {
useAlertStore.getState().setErrorData({
title: "Error while saving changes",
list: [(err as AxiosError).response?.data.detail ?? ""],
list: [err?.response?.data.detail ?? ""],
});
console.error(err);
});
@ -79,6 +78,7 @@ export default function FlowSettingsModal({
submit={{
label: "Save",
disabled: nameLists.includes(name) && name !== currentFlow!.name,
dataTestId: "save-flow-settings",
}}
/>
</BaseModal>

View file

@ -58,7 +58,7 @@ export default function NodeToolbarComponent({
data.node.template[templateField].type === "Any" ||
data.node.template[templateField].type === "int" ||
data.node.template[templateField].type === "dict" ||
data.node.template[templateField].type === "NestedDict"),
data.node.template[templateField].type === "NestedDict")
).length;
const templates = useTypesStore((state) => state.templates);
const hasStore = useStoreStore((state) => state.hasStore);
@ -85,7 +85,7 @@ export default function NodeToolbarComponent({
const [showconfirmShare, setShowconfirmShare] = useState(false);
const [showOverrideModal, setShowOverrideModal] = useState(false);
const [flowComponent, setFlowComponent] = useState<FlowType>(
createFlowComponent(cloneDeep(data), version),
createFlowComponent(cloneDeep(data), version)
);
const openInNewTab = (url) => {
@ -100,7 +100,7 @@ export default function NodeToolbarComponent({
const updateNodeInternals = useUpdateNodeInternals();
const setLastCopiedSelection = useFlowStore(
(state) => state.setLastCopiedSelection,
(state) => state.setLastCopiedSelection
);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
@ -153,7 +153,7 @@ export default function NodeToolbarComponent({
nodes,
edges,
setNodes,
setEdges,
setEdges
);
break;
case "override":
@ -177,16 +177,16 @@ export default function NodeToolbarComponent({
y: 10,
paneX: nodes.find((node) => node.id === data.id)?.position.x,
paneY: nodes.find((node) => node.id === data.id)?.position.y,
},
}
);
break;
case "update":
takeSnapshot();
// to update we must get the code from the templates in useTypesStore
const thisNodeTemplate = templates[data.type].template;
const thisNodeTemplate = templates[data.type]?.template;
// if the template does not have a code key
// return
if (!thisNodeTemplate.code) return;
if (!thisNodeTemplate?.code) return;
const currentCode = thisNodeTemplate.code.value;
if (data.node) {
@ -215,13 +215,13 @@ export default function NodeToolbarComponent({
};
const isSaved = flows.some((flow) =>
Object.values(flow).includes(data.node?.display_name!),
Object.values(flow).includes(data.node?.display_name!)
);
const setNode = useFlowStore((state) => state.setNode);
const handleOnNewValue = (
newValue: string | string[] | boolean | Object[],
newValue: string | string[] | boolean | Object[]
): void => {
if (data.node!.template[name].value !== newValue) {
takeSnapshot();
@ -408,7 +408,7 @@ export default function NodeToolbarComponent({
data-testid="save-button-modal"
className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
hasCode ? " " : " rounded-l-md ",
hasCode ? " " : " rounded-l-md "
)}
onClick={(event) => {
event.preventDefault();
@ -426,7 +426,7 @@ export default function NodeToolbarComponent({
<button
data-testid="duplicate-button-modal"
className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
)}
onClick={(event) => {
event.preventDefault();
@ -440,7 +440,7 @@ export default function NodeToolbarComponent({
<ShadTooltip content="Freeze" side="top">
<button
className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
)}
onClick={(event) => {
event.preventDefault();
@ -461,7 +461,7 @@ export default function NodeToolbarComponent({
className={cn(
"h-4 w-4 transition-all",
// TODO UPDATE THIS COLOR TO BE A VARIABLE
frozen ? "animate-wiggle text-ice" : "",
frozen ? "animate-wiggle text-ice" : ""
)}
/>
</button>
@ -474,7 +474,7 @@ export default function NodeToolbarComponent({
<div
data-testid="more-options-modal"
className={classNames(
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
)}
>
<IconComponent

View file

@ -1,6 +1,7 @@
import * as Form from "@radix-ui/react-form";
import { cloneDeep } from "lodash";
import { useContext, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import ForwardedIconComponent from "../../../../components/genericIconComponent";
import GradientChooserComponent from "../../../../components/gradientChooserComponent";
import InputComponent from "../../../../components/inputComponent";
@ -35,17 +36,16 @@ import {
} from "../../../../controllers/API";
import useAlertStore from "../../../../stores/alertStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { useStoreStore } from "../../../../stores/storeStore";
import {
inputHandlerEventType,
patchUserInputStateType,
} from "../../../../types/components";
import { gradients } from "../../../../utils/styleUtils";
import { useStoreStore } from "../../../../stores/storeStore";
import { useParams } from "react-router-dom";
export default function GeneralPage() {
const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId,
(state) => state.setCurrentFlowId
);
const { scrollId } = useParams();
@ -59,7 +59,7 @@ export default function GeneralPage() {
}, [scrollId]);
const [inputState, setInputState] = useState<patchUserInputStateType>(
CONTROL_PATCH_USER_STATE,
CONTROL_PATCH_USER_STATE
);
const { autoLogin } = useContext(AuthContext);
@ -143,7 +143,7 @@ export default function GeneralPage() {
setHasApiKey(false);
setValidApiKey(false);
setLoadingApiKey(false);
},
}
);
}
};
@ -290,8 +290,8 @@ export default function GeneralPage() {
{(hasApiKey && !validApiKey
? INVALID_API_KEY
: !hasApiKey
? NO_API_KEY
: "") + INSERT_API_KEY}
? NO_API_KEY
: "") + INSERT_API_KEY}
</CardDescription>
</CardHeader>
<CardContent>
@ -319,7 +319,11 @@ export default function GeneralPage() {
</CardContent>
<CardFooter className="border-t px-6 py-4">
<Form.Submit asChild>
<Button loading={loadingApiKey} type="submit">
<Button
loading={loadingApiKey}
type="submit"
data-testid="api-key-save-button-store"
>
Save
</Button>
</Form.Submit>

View file

@ -23,7 +23,7 @@ test("chat_io_teste", async ({ page }) => {
}
const jsonContent = readFileSync(
"tests/end-to-end/assets/ChatTest.json",
"src/frontend/tests/end-to-end/assets/ChatTest.json",
"utf-8"
);

View file

@ -59,6 +59,9 @@ test("user must interact with chat with Input/Output", async ({ page }) => {
.fill(
"testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!"
);
await page.getByTestId("icon-LucideSend").click();
await page.getByText("Close", { exact: true }).click();
await page
.getByTestId("popover-anchor-input-sender_name")
.nth(1)
@ -68,7 +71,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => {
.nth(0)
.fill("TestSenderNameAI");
await page.getByText("Playground", { exact: true }).click();
await page.getByText("Playground", { exact: true }).last().click();
await page.getByTestId("icon-LucideSend").click();
valueUser = await page

View file

@ -26,8 +26,8 @@ test.describe("drag and drop test", () => {
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
// Read your file into a buffer.
const jsonContent = readFileSync(
"tests/end-to-end/assets/collection.json",
"utf-8",
"src/frontend/tests/end-to-end/assets/collection.json",
"utf-8"
);
// Create the DataTransfer and File
@ -47,7 +47,7 @@ test.describe("drag and drop test", () => {
"drop",
{
dataTransfer,
},
}
);
const genericNoda = page.getByTestId("div-generic-node");

View file

@ -78,32 +78,32 @@ test("dropDownComponent", async ({ page }) => {
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(),
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(),
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showendpoint_url"]').click();
expect(
await page.locator('//*[@id="showendpoint_url"]').isChecked(),
await page.locator('//*[@id="showendpoint_url"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showendpoint_url"]').click();
expect(
await page.locator('//*[@id="showendpoint_url"]').isChecked(),
await page.locator('//*[@id="showendpoint_url"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showregion_name"]').click();
expect(
await page.locator('//*[@id="showregion_name"]').isChecked(),
await page.locator('//*[@id="showregion_name"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showregion_name"]').click();
expect(
await page.locator('//*[@id="showregion_name"]').isChecked(),
await page.locator('//*[@id="showregion_name"]').isChecked()
).toBeTruthy();
// showmodel_id
@ -113,7 +113,7 @@ test("dropDownComponent", async ({ page }) => {
// showmodel_id
await page.locator('//*[@id="showmodel_id"]').click();
expect(
await page.locator('//*[@id="showmodel_id"]').isChecked(),
await page.locator('//*[@id="showmodel_id"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showcache"]').click();
@ -124,32 +124,32 @@ test("dropDownComponent", async ({ page }) => {
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(),
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showcredentials_profile_name"]').click();
expect(
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(),
await page.locator('//*[@id="showcredentials_profile_name"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showendpoint_url"]').click();
expect(
await page.locator('//*[@id="showendpoint_url"]').isChecked(),
await page.locator('//*[@id="showendpoint_url"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showendpoint_url"]').click();
expect(
await page.locator('//*[@id="showendpoint_url"]').isChecked(),
await page.locator('//*[@id="showendpoint_url"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showregion_name"]').click();
expect(
await page.locator('//*[@id="showregion_name"]').isChecked(),
await page.locator('//*[@id="showregion_name"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showregion_name"]').click();
expect(
await page.locator('//*[@id="showregion_name"]').isChecked(),
await page.locator('//*[@id="showregion_name"]').isChecked()
).toBeTruthy();
// showmodel_id
@ -159,7 +159,7 @@ test("dropDownComponent", async ({ page }) => {
// showmodel_id
await page.locator('//*[@id="showmodel_id"]').click();
expect(
await page.locator('//*[@id="showmodel_id"]').isChecked(),
await page.locator('//*[@id="showmodel_id"]').isChecked()
).toBeTruthy();
await page.getByTestId("dropdown-edit-model_id").click();
@ -170,7 +170,7 @@ test("dropDownComponent", async ({ page }) => {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="saveChangesBtn"]').click();
await page.getByText("Save Changes", { exact: true }).click();
value = await page.getByTestId("dropdown-model_id").innerText();
if (value !== "ai21.j2-mid-v1") {

View file

@ -40,7 +40,7 @@ test("LLMChain - Tooltip", async ({ page }) => {
await page
.locator(
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[3]/div/button/div/div',
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[3]/div/button/div/div'
)
.hover()
.then(async () => {
@ -60,17 +60,17 @@ test("LLMChain - Tooltip", async ({ page }) => {
await page.getByTitle("zoom out").click();
await page
.locator(
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[4]/div/button/div/div',
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[4]/div/button/div/div'
)
.hover()
.then(async () => {
await expect(
page.getByTestId("tooltip-Model Specs").first(),
page.getByTestId("tooltip-Model Specs").first()
).toBeVisible();
await page.waitForTimeout(2000);
await expect(
page.getByTestId("tooltip-Model Specs").first(),
page.getByTestId("tooltip-Model Specs").first()
).toBeVisible();
await page.getByTestId("icon-Search").click();
@ -81,12 +81,12 @@ test("LLMChain - Tooltip", async ({ page }) => {
await page
.locator(
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[5]/div/button/div/div',
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[5]/div/button/div/div'
)
.hover()
.then(async () => {
await expect(
page.getByTestId("empty-tooltip-filter").first(),
page.getByTestId("empty-tooltip-filter").first()
).toBeVisible();
});
});
@ -113,7 +113,7 @@ test("LLMChain - Filter", async ({ page }) => {
await page.waitForTimeout(1000);
await page.getByTestId(
"input-list-plus-btn-edit_metadata_indexing_include-2",
"input-list-plus-btn-edit_metadata_indexing_include-2"
);
await page.getByTestId("blank-flow").click();
@ -136,7 +136,7 @@ test("LLMChain - Filter", async ({ page }) => {
await page
.locator(
'//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[4]/div/button/div/div',
'//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[4]/div/button/div/div'
)
.click();
@ -148,16 +148,15 @@ test("LLMChain - Filter", async ({ page }) => {
await expect(page.getByTestId("model_specsChatOllama")).toBeVisible();
await expect(page.getByTestId("model_specsChatOpenAI")).toBeVisible();
await expect(page.getByTestId("model_specsChatVertexAI")).toBeVisible();
await expect(page.getByTestId("model_specsCohere")).toBeVisible();
await expect(
page.getByTestId("model_specsGoogle Generative AI"),
page.getByTestId("model_specsGoogle Generative AI")
).toBeVisible();
await expect(
page.getByTestId("model_specsHugging Face Inference API"),
page.getByTestId("model_specsHugging Face Inference API")
).toBeVisible();
await expect(page.getByTestId("model_specsOllama")).toBeVisible();
await expect(
page.getByTestId("model_specsQianfanChatEndpoint"),
page.getByTestId("model_specsQianfanChatEndpoint")
).toBeVisible();
await expect(page.getByTestId("model_specsQianfanLLMEndpoint")).toBeVisible();
await expect(page.getByTestId("model_specsVertexAI")).toBeVisible();
@ -169,24 +168,23 @@ test("LLMChain - Filter", async ({ page }) => {
await expect(page.getByTestId("model_specsAmazon Bedrock")).not.toBeVisible();
await expect(page.getByTestId("modelsAzure OpenAI")).not.toBeVisible();
await expect(
page.getByTestId("model_specsAzureChatOpenAI"),
page.getByTestId("model_specsAzureChatOpenAI")
).not.toBeVisible();
await expect(page.getByTestId("model_specsChatAnthropic")).not.toBeVisible();
await expect(page.getByTestId("model_specsChatLiteLLM")).not.toBeVisible();
await expect(page.getByTestId("model_specsChatOllama")).not.toBeVisible();
await expect(page.getByTestId("model_specsChatOpenAI")).not.toBeVisible();
await expect(page.getByTestId("model_specsChatVertexAI")).not.toBeVisible();
await expect(page.getByTestId("model_specsCohere")).not.toBeVisible();
await page
.locator(
'//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div',
'//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div'
)
.click();
await page
.locator(
'//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div',
'//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div'
)
.click();

Some files were not shown because too many files have changed in this diff Show more