diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index badf53c43..369a6f764 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -3,10 +3,6 @@ import { Cookies } from "react-cookie"; import { RouterProvider } from "react-router-dom"; import "reactflow/dist/style.css"; import LoadingComponent from "./components/loadingComponent"; -import { - LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS, - LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV, -} from "./constants/constants"; import { AuthContext } from "./contexts/authContext"; import { useAutoLogin, @@ -34,8 +30,6 @@ export default function App() { const { mutate: mutateAutoLogin } = useAutoLogin(); - useGetVersionQuery(); - const { mutate: mutateRefresh } = useRefreshAccessToken(); const isLoginPage = location.pathname.includes("login"); @@ -79,23 +73,7 @@ export default function App() { }); }, []); - useEffect(() => { - const envRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV; - const automaticRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS; - - const accessTokenTimer = isNaN(envRefreshTime) - ? automaticRefreshTime - : envRefreshTime; - - const intervalId = setInterval(() => { - if (isAuthenticated && !isLoginPage) { - mutateRefresh({ refresh_token: refreshToken }); - } - }, accessTokenTimer * 1000); - - return () => clearInterval(intervalId); - }, [isLoginPage]); - + useGetVersionQuery(); useSaveConfig(); return ( diff --git a/src/frontend/src/components/authAdminGuard/index.tsx b/src/frontend/src/components/authAdminGuard/index.tsx index e2966c873..715b9bba2 100644 --- a/src/frontend/src/components/authAdminGuard/index.tsx +++ b/src/frontend/src/components/authAdminGuard/index.tsx @@ -2,17 +2,20 @@ import useAuthStore from "@/stores/authStore"; import { useContext } from "react"; import { Navigate } from "react-router-dom"; import { AuthContext } from "../../contexts/authContext"; +import LoadingComponent from "../loadingComponent"; export const ProtectedAdminRoute = ({ children }) => { const { userData } = useContext(AuthContext); const isAuthenticated = useAuthStore((state) => state.isAuthenticated); const autoLogin = useAuthStore((state) => state.autoLogin); const isAdmin = useAuthStore((state) => state.isAdmin); - const isLoginPage = location.pathname.includes("login"); - const logout = useAuthStore((state) => state.logout); - if (!isAuthenticated && !isLoginPage) { - logout(); + if (!isAuthenticated) { + return ( +
+ +
+ ); } else if ((userData && !isAdmin) || autoLogin) { return ; } else { diff --git a/src/frontend/src/components/authGuard/index.tsx b/src/frontend/src/components/authGuard/index.tsx index ea3e6f12a..550ec88e4 100644 --- a/src/frontend/src/components/authGuard/index.tsx +++ b/src/frontend/src/components/authGuard/index.tsx @@ -1,13 +1,40 @@ -import { LANGFLOW_AUTO_LOGIN_OPTION } from "@/constants/constants"; +import { + LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS, + LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV, + LANGFLOW_AUTO_LOGIN_OPTION, +} from "@/constants/constants"; +import { useRefreshAccessToken } from "@/controllers/API/queries/auth"; import useAuthStore from "@/stores/authStore"; +import { useEffect } from "react"; +import { Cookies } from "react-cookie"; export const ProtectedRoute = ({ children }) => { const isAuthenticated = useAuthStore((state) => state.isAuthenticated); const hasToken = !!localStorage.getItem(LANGFLOW_AUTO_LOGIN_OPTION); - const isLoginPage = location.pathname.includes("login"); const logout = useAuthStore((state) => state.logout); - if (!isAuthenticated && hasToken && !isLoginPage) { + const cookies = new Cookies(); + const refreshToken = cookies.get("refresh_token"); + const { mutate: mutateRefresh } = useRefreshAccessToken(); + + useEffect(() => { + const envRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV; + const automaticRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS; + + const accessTokenTimer = isNaN(envRefreshTime) + ? automaticRefreshTime + : envRefreshTime; + + const intervalId = setInterval(() => { + if (isAuthenticated) { + mutateRefresh({ refresh_token: refreshToken }); + } + }, accessTokenTimer * 1000); + + return () => clearInterval(intervalId); + }, []); + + if (!isAuthenticated && hasToken) { logout(); } else { return children; diff --git a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx index fece752c7..60c4eecd3 100644 --- a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx +++ b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx @@ -34,11 +34,6 @@ export const MenuBar = ({}: {}): JSX.Element => { const addFlow = useAddFlow(); const setErrorData = useAlertStore((state) => state.setErrorData); const setSuccessData = useAlertStore((state) => state.setSuccessData); - const setLockChat = useFlowStore((state) => state.setLockChat); - const setIsBuilding = useFlowStore((state) => state.setIsBuilding); - const revertBuiltStatusFromBuilding = useFlowStore( - (state) => state.revertBuiltStatusFromBuilding, - ); const undo = useFlowsManagerStore((state) => state.undo); const redo = useFlowsManagerStore((state) => state.redo); const saveLoading = useFlowsManagerStore((state) => state.saveLoading); diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index 3c671b90a..6bea5de6f 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -15,7 +15,6 @@ import { useLogout } from "@/controllers/API/queries/auth"; import useAuthStore from "@/stores/authStore"; import useAlertStore from "../../stores/alertStore"; import { useDarkStore } from "../../stores/darkStore"; -import { useLocationStore } from "../../stores/locationStore"; import { useStoreStore } from "../../stores/storeStore"; import IconComponent, { ForwardedIconComponent } from "../genericIconComponent"; import { Button } from "../ui/button"; @@ -48,29 +47,22 @@ export default function Header(): JSX.Element { const setDark = useDarkStore((state) => state.setDark); const stars = useDarkStore((state) => state.stars); - const routeHistory = useLocationStore((state) => state.routeHistory); - const profileImageUrl = `${BASE_URL_API}files/profile_pictures/${ userData?.profile_image ?? "Space/046-rocket.svg" }`; const redirectToLastLocation = () => { - const lastVisitedIndex = routeHistory - .reverse() - .findIndex((path) => path !== location.pathname); - - const lastFlowVisited = routeHistory[lastVisitedIndex]; - lastFlowVisited ? navigate(lastFlowVisited) : navigate("/all"); + const canGoBack = location.key !== "default"; + if (canGoBack) { + navigate(-1); + } else { + navigate("/", { replace: true }); + } }; - const visitedFlowPathBefore = () => { - const last100VisitedPaths = routeHistory.slice(-99); - return last100VisitedPaths.some((path) => path.includes("/flow/")); - }; - - const showArrowReturnIcon = - LOCATIONS_TO_RETURN.some((path) => location.pathname.includes(path)) && - visitedFlowPathBefore(); + const showArrowReturnIcon = LOCATIONS_TO_RETURN.some((path) => + location.pathname.includes(path), + ); const handleLogout = () => { mutationLogout(undefined, { diff --git a/src/frontend/src/components/pageLayout/index.tsx b/src/frontend/src/components/pageLayout/index.tsx index 8d045e572..bf815e9ac 100644 --- a/src/frontend/src/components/pageLayout/index.tsx +++ b/src/frontend/src/components/pageLayout/index.tsx @@ -1,4 +1,3 @@ -import Header from "../headerComponent"; import { Separator } from "../ui/separator"; export default function PageLayout({ @@ -15,25 +14,22 @@ export default function PageLayout({ betaIcon?: boolean; }) { return ( -
-
-
-
-
-

- {title} - {betaIcon && BETA} -

-

{description}

-
-
{button && button}
+
+
+
+

+ {title} + {betaIcon && BETA} +

+

{description}

- - {children} +
{button && button}
+ + {children}
); } diff --git a/src/frontend/src/controllers/API/api.tsx b/src/frontend/src/controllers/API/api.tsx index 1f1017b56..1a5091860 100644 --- a/src/frontend/src/controllers/API/api.tsx +++ b/src/frontend/src/controllers/API/api.tsx @@ -1,6 +1,5 @@ import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants"; import useAuthStore from "@/stores/authStore"; -import { useUtilityStore } from "@/stores/utilityStore"; import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios"; import { useContext, useEffect } from "react"; import { Cookies } from "react-cookie"; @@ -89,13 +88,13 @@ function ApiInterceptor() { // Request interceptor to add access token to every request const requestInterceptor = api.interceptors.request.use( (config) => { - const checkRequest = checkDuplicateRequestAndStoreRequest(config); - const controller = new AbortController(); - - if (!checkRequest) { - controller.abort("Duplicate Request"); - console.error("Duplicate Request"); + try { + checkDuplicateRequestAndStoreRequest(config); + } catch (e) { + const error = e as Error; + controller.abort(error.message); + console.error(error.message); } const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN); diff --git a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts index 85c6608ac..52ed60cbc 100644 --- a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts +++ b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts @@ -21,13 +21,11 @@ export function checkDuplicateRequestAndStoreRequest(config) { currentTime - parseInt(lastRequestTime, 10) < 300 && lastCurrentUrl === currentUrl ) { - return false; + throw new Error("Duplicate request: " + lastUrl); } localStorage.setItem("lastUrlCalled", config.url ?? ""); localStorage.setItem("lastMethodCalled", config.method ?? ""); localStorage.setItem("lastRequestTime", currentTime.toString()); localStorage.setItem("lastCurrentUrl", currentUrl); - - return true; } diff --git a/src/frontend/src/controllers/API/queries/_builds/use-get-builds.ts b/src/frontend/src/controllers/API/queries/_builds/use-get-builds.ts index ad062ac7a..b5f978fcd 100644 --- a/src/frontend/src/controllers/API/queries/_builds/use-get-builds.ts +++ b/src/frontend/src/controllers/API/queries/_builds/use-get-builds.ts @@ -9,35 +9,22 @@ import { UseRequestProcessor } from "../../services/request-processor"; interface BuildsQueryParams { flowId?: string; - nodeId?: string; } export const useGetBuildsQuery: useQueryFunctionType< BuildsQueryParams, AxiosResponse<{ vertex_builds: FlowPoolType }> -> = ({}) => { +> = (params) => { const { query } = UseRequestProcessor(); const setFlowPool = useFlowStore((state) => state.setFlowPool); const currentFlow = useFlowStore((state) => state.currentFlow); - const getBuildsFn = async ( - params: BuildsQueryParams, - ): Promise> => { + const responseFn = async () => { const config = {}; config["params"] = { flow_id: params.flowId }; - if (params.nodeId) { - config["params"] = { nodeId: params.nodeId }; - } - - return await api.get(`${getURL("BUILDS")}`, config); - }; - - const responseFn = async () => { - const response = await getBuildsFn({ - flowId: currentFlow!.id, - }); + const response = await api.get(`${getURL("BUILDS")}`, config); if (currentFlow) { const flowPool = response.data.vertex_builds; @@ -47,10 +34,14 @@ export const useGetBuildsQuery: useQueryFunctionType< return response; }; - const queryResult = query(["useGetBuildsQuery"], responseFn, { - placeholderData: keepPreviousData, - refetchOnWindowFocus: false, - }); + const queryResult = query( + ["useGetBuildsQuery", { key: params.flowId }], + responseFn, + { + placeholderData: keepPreviousData, + refetchOnWindowFocus: false, + }, + ); return queryResult; }; diff --git a/src/frontend/src/hooks/use-track-last-visited-path.tsx b/src/frontend/src/hooks/use-track-last-visited-path.tsx deleted file mode 100644 index fa5990800..000000000 --- a/src/frontend/src/hooks/use-track-last-visited-path.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { useEffect } from "react"; -import { useLocation } from "react-router-dom"; -import { useLocationStore } from "../stores/locationStore"; - -function useTrackLastVisitedPath() { - const location = useLocation(); - const setHistory = useLocationStore((state) => state.setRouteHistory); - - useEffect(() => { - setHistory(location.pathname); - }, [location]); -} - -export default useTrackLastVisitedPath; diff --git a/src/frontend/src/pages/AdminPage/index.tsx b/src/frontend/src/pages/AdminPage/index.tsx index 5605742a0..e2880ac9d 100644 --- a/src/frontend/src/pages/AdminPage/index.tsx +++ b/src/frontend/src/pages/AdminPage/index.tsx @@ -7,7 +7,6 @@ import { import { cloneDeep } from "lodash"; import { useContext, useEffect, useRef, useState } from "react"; import IconComponent from "../../components/genericIconComponent"; -import Header from "../../components/headerComponent"; import LoadingComponent from "../../components/loadingComponent"; import PaginatorComponent from "../../components/paginatorComponent"; import ShadTooltip from "../../components/shadTooltipComponent"; @@ -246,7 +245,6 @@ export default function AdminPage() { return ( <> -
{userData && (
diff --git a/src/frontend/src/pages/AppWrapperPage/index.tsx b/src/frontend/src/pages/AppWrapperPage/index.tsx index 595ccbd19..65c16df8d 100644 --- a/src/frontend/src/pages/AppWrapperPage/index.tsx +++ b/src/frontend/src/pages/AppWrapperPage/index.tsx @@ -10,7 +10,6 @@ import { TIMEOUT_ERROR_MESSAGE, } from "@/constants/constants"; import { useGetHealthQuery } from "@/controllers/API/queries/health"; -import useTrackLastVisitedPath from "@/hooks/use-track-last-visited-path"; import useFlowsManagerStore from "@/stores/flowsManagerStore"; import { useUtilityStore } from "@/stores/utilityStore"; import { cn } from "@/utils/utils"; @@ -20,8 +19,6 @@ import { ErrorBoundary } from "react-error-boundary"; import { Outlet } from "react-router-dom"; export function AppWrapperPage() { - useTrackLastVisitedPath(); - const isLoading = useFlowsManagerStore((state) => state.isLoading); const healthCheckMaxRetries = useFlowsManagerStore( diff --git a/src/frontend/src/pages/DashboardWrapperPage/index.tsx b/src/frontend/src/pages/DashboardWrapperPage/index.tsx new file mode 100644 index 000000000..991537cdc --- /dev/null +++ b/src/frontend/src/pages/DashboardWrapperPage/index.tsx @@ -0,0 +1,11 @@ +import Header from "@/components/headerComponent"; +import { Outlet } from "react-router-dom"; + +export function DashboardWrapperPage() { + return ( +
+
+ +
+ ); +} diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 61060f933..0e18a67ab 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -143,7 +143,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { }; }, [lastCopiedSelection, lastSelection, takeSnapshot, selectionMenuVisible]); - const { isFetching, refetch } = useGetBuildsQuery({}); + const { isFetching } = useGetBuildsQuery({ flowId: currentFlowId }); const showCanvas = Object.keys(templates).length > 0 && @@ -151,8 +151,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { !isFetching; useEffect(() => { - refetch(); - useFlowStore.setState({ autoSaveFlow }); if (checkOldComponents({ nodes })) { setNoticeData({ title: @@ -161,6 +159,10 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { } }, [currentFlowId]); + useEffect(() => { + useFlowStore.setState({ autoSaveFlow }); + }); + function handleUndo(e: KeyboardEvent) { if (!isWrappedWithClass(e, "noflow")) { e.preventDefault(); diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index 420ea9501..0321d9e11 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -8,7 +8,6 @@ import { customStringify } from "@/utils/reactflowUtils"; import { useEffect } from "react"; import { useBlocker, useNavigate, useParams } from "react-router-dom"; import FlowToolbar from "../../components/chatComponent"; -import Header from "../../components/headerComponent"; import { useDarkStore } from "../../stores/darkStore"; import useFlowStore from "../../stores/flowStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; @@ -81,7 +80,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element { } }; awaitgetTypes(); - }, [id, flows]); + }, [id, flows, currentFlowId]); useEffect(() => { setOnFlowPage(true); @@ -94,7 +93,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element { return ( <> -
{currentFlow && (
diff --git a/src/frontend/src/pages/Playground/index.tsx b/src/frontend/src/pages/Playground/index.tsx index 47c2aa177..09b4ae8c3 100644 --- a/src/frontend/src/pages/Playground/index.tsx +++ b/src/frontend/src/pages/Playground/index.tsx @@ -1,6 +1,9 @@ +import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows"; +import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; import { useStoreStore } from "@/stores/storeStore"; +import { useTypesStore } from "@/stores/typesStore"; import { useEffect } from "react"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import LoadingComponent from "../../components/loadingComponent"; import { getComponent } from "../../controllers/API"; import IOModal from "../../modals/IOModal"; @@ -8,7 +11,6 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore"; import cloneFLowWithParent from "../../utils/storeUtils"; export default function PlaygroundPage() { - const getFlowById = useFlowsManagerStore((state) => state.getFlowById); const flows = useFlowsManagerStore((state) => state.flows); const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow); @@ -21,19 +23,38 @@ export default function PlaygroundPage() { return newFlow; } + const navigate = useNavigate(); + useGetGlobalVariables(); + + const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); + const { mutateAsync: refreshFlows } = useGetRefreshFlows(); + const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading); + const getTypes = useTypesStore((state) => state.getTypes); + // Set flow tab id useEffect(() => { - if (flows) { - const flow = getFlowById(id!); - if (flow) { - setCurrentFlow(flow); - } else { - if (validApiKey) - getFlowData().then((flow) => { - setCurrentFlow(flow); - }); + const awaitgetTypes = async () => { + if (flows && currentFlowId === "") { + const isAnExistingFlow = flows.find((flow) => flow.id === id); + + if (!isAnExistingFlow) { + if (validApiKey) { + getFlowData().then((flow) => { + setCurrentFlow(flow); + }); + } else { + navigate("/"); + } + } + setCurrentFlow(isAnExistingFlow); + } else if (!flows) { + setIsLoading(true); + await refreshFlows(undefined); + await getTypes(); + setIsLoading(false); } - } + }; + awaitgetTypes(); }, [id, flows, validApiKey]); return ( diff --git a/src/frontend/src/pages/ViewPage/index.tsx b/src/frontend/src/pages/ViewPage/index.tsx index 1e7ad5b67..5c9de515a 100644 --- a/src/frontend/src/pages/ViewPage/index.tsx +++ b/src/frontend/src/pages/ViewPage/index.tsx @@ -1,6 +1,5 @@ import { useGetRefreshFlows } from "@/controllers/API/queries/flows/use-get-refresh-flows"; import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; -import useFlowStore from "@/stores/flowStore"; import { useTypesStore } from "@/stores/typesStore"; import { useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; @@ -10,7 +9,6 @@ import Page from "../FlowPage/components/PageComponent"; export default function ViewPage() { const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); - const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage); const { id } = useParams(); const navigate = useNavigate(); useGetGlobalVariables(); @@ -43,15 +41,6 @@ export default function ViewPage() { awaitgetTypes(); }, [id, flows]); - useEffect(() => { - setOnFlowPage(true); - - return () => { - setOnFlowPage(false); - setCurrentFlow(undefined); - }; - }, [id]); - return (
diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx index 6807bfdf2..8626998c9 100644 --- a/src/frontend/src/routes.tsx +++ b/src/frontend/src/routes.tsx @@ -1,17 +1,18 @@ -import React, { lazy } from "react"; +import { lazy } from "react"; import { createBrowserRouter, createRoutesFromElements, Navigate, + Outlet, Route, } from "react-router-dom"; import { ProtectedAdminRoute } from "./components/authAdminGuard"; import { ProtectedRoute } from "./components/authGuard"; import { ProtectedLoginRoute } from "./components/authLoginGuard"; import { AuthSettingsGuard } from "./components/authSettingsGuard"; -import { CatchAllRoute } from "./components/catchAllRoutes"; import { StoreGuard } from "./components/storeGuard"; import { AppWrapperPage } from "./pages/AppWrapperPage"; +import { DashboardWrapperPage } from "./pages/DashboardWrapperPage"; import FlowPage from "./pages/FlowPage"; import LoginPage from "./pages/LoginPage"; import MyCollectionComponent from "./pages/MainPage/components/myCollectionComponent"; @@ -34,132 +35,104 @@ const PlaygroundPage = lazy(() => import("./pages/Playground")); const SignUp = lazy(() => import("./pages/SignUpPage")); const router = createBrowserRouter( createRoutesFromElements([ - }> + }> - + } > - } /> - } - > + }> + }> + } /> + } + > + } + /> + + + } + > + + } + /> + + } + > + } + /> + + + }> + } /> + } /> + } /> + + + + } + /> + } /> + } /> + } - /> - - } - > - + + + + } + /> + + + + } + /> + + }> + + + + } /> - } - > - } - /> + + }> + } /> + } /> + + } /> + + + } /> - - - - } - > - } /> - } /> - } /> - - - - } - /> - } /> - } /> - - - - - - - } - /> - - - - - - } - /> - - - - - } - /> - - - - - - } - /> - - - - } - /> - - - - } - /> - - - - - } - /> } /> - - - - } - /> - - - - - } - > - + } /> , ]), ); diff --git a/src/frontend/tests/scheduled-end-to-end/generalBugs-shard-1.spec.ts b/src/frontend/tests/scheduled-end-to-end/generalBugs-shard-1.spec.ts index cec5acbac..83923b65e 100644 --- a/src/frontend/tests/scheduled-end-to-end/generalBugs-shard-1.spec.ts +++ b/src/frontend/tests/scheduled-end-to-end/generalBugs-shard-1.spec.ts @@ -1,4 +1,4 @@ -import { expect, test } from "@playwright/test"; +import { test } from "@playwright/test"; import * as dotenv from "dotenv"; import path from "path"; @@ -70,6 +70,8 @@ test("should delete rows from table message", async ({ page }) => { timeout: 30000, }); + await page.waitForTimeout(2000); + await page.getByTestId("user-profile-settings").last().click(); await page.getByText("Settings").last().click(); await page.getByText("Messages").last().click();