From 898e9f101ae02311ec56c009c59dfb3063a19088 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Tue, 4 Jun 2024 20:48:39 -0300 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(GeneralPage):=20refactor?= =?UTF-8?q?=20API=20calls=20into=20custom=20hooks=20for=20better=20modular?= =?UTF-8?q?ity=20=E2=9C=A8=20(GeneralPage):=20add=20custom=20hooks=20for?= =?UTF-8?q?=20patching=20gradient,=20password,=20and=20saving=20API=20key?= =?UTF-8?q?=20=F0=9F=92=A1=20(GeneralPage):=20add=20useScrollToElement=20h?= =?UTF-8?q?ook=20to=20handle=20smooth=20scrolling=20to=20elements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ (hooks): add useSaveKey and useScrollToElement custom hooks ✅ (tests): update end-to-end tests for improved stability and accuracy --- .../SettingsPage/pages/GeneralPage/index.tsx | 120 ++++-------------- .../pages/hooks/use-patch-gradient.tsx | 37 ++++++ .../pages/hooks/use-patch-password.tsx | 36 ++++++ .../SettingsPage/pages/hooks/use-save-key.tsx | 48 +++++++ .../pages/hooks/use-scroll-to-element.tsx | 17 +++ .../end-to-end/deleteComponentFlows.spec.ts | 1 + .../tests/end-to-end/flowSettings.spec.ts | 3 +- src/frontend/tests/end-to-end/store.spec.ts | 19 +-- 8 files changed, 179 insertions(+), 102 deletions(-) create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx index 4ba04692f..755e7959c 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx @@ -1,6 +1,5 @@ import * as Form from "@radix-ui/react-form"; -import { cloneDeep } from "lodash"; -import { useContext, useEffect, useState } from "react"; +import { useContext, useState } from "react"; import { useParams } from "react-router-dom"; import ForwardedIconComponent from "../../../../components/genericIconComponent"; import GradientChooserComponent from "../../../../components/gradientChooserComponent"; @@ -14,14 +13,6 @@ import { CardHeader, CardTitle, } from "../../../../components/ui/card"; -import { - API_ERROR_ALERT, - API_SUCCESS_ALERT, - EDIT_PASSWORD_ALERT_LIST, - EDIT_PASSWORD_ERROR_ALERT, - SAVE_ERROR_ALERT, - SAVE_SUCCESS_ALERT, -} from "../../../../constants/alerts_constants"; import { CONTROL_PATCH_USER_STATE, CREATE_API_KEY, @@ -30,11 +21,6 @@ import { NO_API_KEY, } from "../../../../constants/constants"; import { AuthContext } from "../../../../contexts/authContext"; -import { - addApiKeyStore, - resetPassword, - updateUser, -} from "../../../../controllers/API"; import useAlertStore from "../../../../stores/alertStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { useStoreStore } from "../../../../stores/storeStore"; @@ -43,6 +29,10 @@ import { patchUserInputStateType, } from "../../../../types/components"; import { gradients } from "../../../../utils/styleUtils"; +import usePatchGradient from "../hooks/use-patch-gradient"; +import usePatchPassword from "../hooks/use-patch-password"; +import useSaveKey from "../hooks/use-save-key"; +import useScrollToElement from "../hooks/use-scroll-to-element"; export default function GeneralPage() { const setCurrentFlowId = useFlowsManagerStore( @@ -51,29 +41,16 @@ export default function GeneralPage() { const { scrollId } = useParams(); - useEffect(() => { - const element = document.getElementById(scrollId ?? "null"); - if (element) { - // 👇 Will scroll smoothly to the top of the next section - element.scrollIntoView({ behavior: "smooth" }); - } - }, [scrollId]); - const [inputState, setInputState] = useState( CONTROL_PATCH_USER_STATE ); const { autoLogin } = useContext(AuthContext); - // set null id - useEffect(() => { - setCurrentFlowId(""); - }, []); const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const { userData, setUserData } = useContext(AuthContext); const hasStore = useStoreStore((state) => state.hasStore); - const { storeApiKey } = useContext(AuthContext); const validApiKey = useStoreStore((state) => state.validApiKey); const hasApiKey = useStoreStore((state) => state.hasApiKey); @@ -83,71 +60,28 @@ export default function GeneralPage() { const setLoadingApiKey = useStoreStore((state) => state.updateLoadingApiKey); const { password, cnfPassword, gradient, apikey } = inputState; - async function handlePatchPassword() { - if (password !== cnfPassword) { - setErrorData({ - title: EDIT_PASSWORD_ERROR_ALERT, - list: [EDIT_PASSWORD_ALERT_LIST], - }); - return; - } - try { - if (password !== "") await resetPassword(userData!.id, { password }); - handleInput({ target: { name: "password", value: "" } }); - handleInput({ target: { name: "cnfPassword", value: "" } }); - setSuccessData({ title: SAVE_SUCCESS_ALERT }); - } catch (error) { - setErrorData({ - title: SAVE_ERROR_ALERT, - list: [(error as any).response.data.detail], - }); - } - } + const { handlePatchPassword } = usePatchPassword( + userData, + setSuccessData, + setErrorData + ); - async function handlePatchGradient() { - try { - if (gradient !== "") - await updateUser(userData!.id, { profile_image: gradient }); - if (gradient !== "") { - let newUserData = cloneDeep(userData); - newUserData!.profile_image = gradient; + const { handlePatchGradient } = usePatchGradient( + setSuccessData, + setErrorData, + userData, + setUserData + ); - setUserData(newUserData); - } - setSuccessData({ title: SAVE_SUCCESS_ALERT }); - } catch (error) { - setErrorData({ - title: SAVE_ERROR_ALERT, - list: [(error as any).response.data.detail], - }); - } - } + useScrollToElement(scrollId, setCurrentFlowId); - const handleSaveKey = () => { - if (apikey) { - addApiKeyStore(apikey).then( - () => { - setSuccessData({ - title: API_SUCCESS_ALERT, - }); - storeApiKey(apikey); - setHasApiKey(true); - setValidApiKey(true); - setLoadingApiKey(false); - handleInput({ target: { name: "apikey", value: "" } }); - }, - (error) => { - setErrorData({ - title: API_ERROR_ALERT, - list: [error["response"]["data"]["detail"]], - }); - setHasApiKey(false); - setValidApiKey(false); - setLoadingApiKey(false); - } - ); - } - }; + const { handleSaveKey } = useSaveKey( + setSuccessData, + setErrorData, + setHasApiKey, + setValidApiKey, + setLoadingApiKey + ); function handleInput({ target: { name, value }, @@ -175,7 +109,7 @@ export default function GeneralPage() {
{ - handlePatchGradient(); + handlePatchGradient(gradient); event.preventDefault(); }} > @@ -213,7 +147,7 @@ export default function GeneralPage() { {!autoLogin && ( { - handlePatchPassword(); + handlePatchPassword(password, cnfPassword, handleInput); event.preventDefault(); }} > @@ -281,7 +215,7 @@ export default function GeneralPage() { { event.preventDefault(); - handleSaveKey(); + handleSaveKey(apikey, handleInput); }} > diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx new file mode 100644 index 000000000..9adb923a7 --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx @@ -0,0 +1,37 @@ +import cloneDeep from "lodash/cloneDeep"; +import { + SAVE_ERROR_ALERT, + SAVE_SUCCESS_ALERT, +} from "../../../../constants/alerts_constants"; +import { updateUser } from "../../../../controllers/API"; + +const usePatchGradient = ( + setSuccessData, + setErrorData, + currentUserData, + setUserData +) => { + const handlePatchGradient = async (gradient) => { + try { + if (gradient !== "") { + await updateUser(currentUserData.id, { profile_image: gradient }); + let newUserData = cloneDeep(currentUserData); + newUserData.profile_image = gradient; + setUserData(newUserData); + } + setSuccessData({ title: SAVE_SUCCESS_ALERT }); + } catch (error) { + setErrorData({ + title: SAVE_ERROR_ALERT, + list: [(error as any)?.response?.data?.detail], + }); + } + }; + + return { + currentUserData, + handlePatchGradient, + }; +}; + +export default usePatchGradient; diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx new file mode 100644 index 000000000..c4b452e6b --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx @@ -0,0 +1,36 @@ +import { + EDIT_PASSWORD_ALERT_LIST, + EDIT_PASSWORD_ERROR_ALERT, + SAVE_ERROR_ALERT, + SAVE_SUCCESS_ALERT, +} from "../../../../constants/alerts_constants"; +import { resetPassword } from "../../../../controllers/API"; + +const usePatchPassword = (userData, setSuccessData, setErrorData) => { + const handlePatchPassword = async (password, cnfPassword, handleInput) => { + if (password !== cnfPassword) { + setErrorData({ + title: EDIT_PASSWORD_ERROR_ALERT, + list: [EDIT_PASSWORD_ALERT_LIST], + }); + return; + } + try { + if (password !== "") await resetPassword(userData.id, { password }); + handleInput({ target: { name: "password", value: "" } }); + handleInput({ target: { name: "cnfPassword", value: "" } }); + setSuccessData({ title: SAVE_SUCCESS_ALERT }); + } catch (error) { + setErrorData({ + title: SAVE_ERROR_ALERT, + list: [(error as any)?.response?.data?.detail], + }); + } + }; + + return { + handlePatchPassword, + }; +}; + +export default usePatchPassword; diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx new file mode 100644 index 000000000..bdd105fef --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx @@ -0,0 +1,48 @@ +import { useContext } from "react"; +import { + API_ERROR_ALERT, + API_SUCCESS_ALERT, +} from "../../../../constants/alerts_constants"; +import { AuthContext } from "../../../../contexts/authContext"; +import { addApiKeyStore } from "../../../../controllers/API"; + +const useSaveKey = ( + setSuccessData, + setErrorData, + setHasApiKey, + setValidApiKey, + setLoadingApiKey +) => { + const { storeApiKey } = useContext(AuthContext); + + const handleSaveKey = (apikey, handleInput) => { + if (apikey) { + setLoadingApiKey(true); + addApiKeyStore(apikey).then( + () => { + setSuccessData({ title: API_SUCCESS_ALERT }); + storeApiKey(apikey); + setHasApiKey(true); + setValidApiKey(true); + setLoadingApiKey(false); + handleInput({ target: { name: "apikey", value: "" } }); + }, + (error) => { + setErrorData({ + title: API_ERROR_ALERT, + list: [error.response.data.detail], + }); + setHasApiKey(false); + setValidApiKey(false); + setLoadingApiKey(false); + } + ); + } + }; + + return { + handleSaveKey, + }; +}; + +export default useSaveKey; diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx new file mode 100644 index 000000000..a56c2d5d6 --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx @@ -0,0 +1,17 @@ +import { useEffect } from "react"; + +const useScrollToElement = (scrollId, setCurrentFlowId) => { + useEffect(() => { + const element = document.getElementById(scrollId ?? "null"); + if (element) { + // Scroll smoothly to the top of the next section + element.scrollIntoView({ behavior: "smooth" }); + } + }, [scrollId]); + + useEffect(() => { + setCurrentFlowId(""); + }, [setCurrentFlowId]); +}; + +export default useScrollToElement; diff --git a/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts b/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts index 854ac388c..836e8fe81 100644 --- a/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts +++ b/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts @@ -11,6 +11,7 @@ test("shoud delete a flow", async ({ page }) => { .fill(process.env.STORE_API_KEY ?? ""); await page.getByText("Save").last().click(); await page.waitForTimeout(8000); + await page.getByText("Store").nth(0).click(); await page.getByTestId("install-Website Content QA").click(); await page.waitForTimeout(5000); diff --git a/src/frontend/tests/end-to-end/flowSettings.spec.ts b/src/frontend/tests/end-to-end/flowSettings.spec.ts index e0a3b8f27..a2e9f25c0 100644 --- a/src/frontend/tests/end-to-end/flowSettings.spec.ts +++ b/src/frontend/tests/end-to-end/flowSettings.spec.ts @@ -45,7 +45,8 @@ test("flowSettings", async ({ page }) => { ); await page.getByTestId("save-flow-settings").click(); - await page.getByTestId("save-flow-settings").click(); + + await page.getByText("Close").last().click(); await page.waitForTimeout(1000); diff --git a/src/frontend/tests/end-to-end/store.spec.ts b/src/frontend/tests/end-to-end/store.spec.ts index f1ea0d84c..13963d698 100644 --- a/src/frontend/tests/end-to-end/store.spec.ts +++ b/src/frontend/tests/end-to-end/store.spec.ts @@ -96,27 +96,29 @@ test("should filter by type", async ({ page }) => { await page.getByText("Website Content QA").isVisible(); await page.getByTestId("flows-button-store").click(); - await page.waitForTimeout(3000); + await page.waitForTimeout(8000); let iconGroup = await page.getByTestId("icon-Group")?.count(); expect(iconGroup).not.toBe(0); - await page.getByText("icon-ToyBrick").isHidden(); + await page.getByText("icon-ToyBrick").last().isHidden(); await page.getByTestId("components-button-store").click(); - await page.waitForTimeout(3000); + await page.waitForTimeout(8000); - await page.getByTestId("icon-Group").isHidden(); + await page.getByTestId("icon-Group").last().isHidden(); let toyBrick = await page.getByTestId("icon-ToyBrick")?.count(); expect(toyBrick).not.toBe(0); await page.getByTestId("all-button-store").click(); - await page.waitForTimeout(3000); + await page.waitForTimeout(8000); - iconGroup = await page.getByTestId("icon-Group")?.count(); - toyBrick = await page.getByTestId("icon-ToyBrick")?.count(); + let iconGroupAllCount = await page.getByTestId("icon-Group")?.count(); + await page.waitForTimeout(2000); + let toyBrickAllCount = await page.getByTestId("icon-ToyBrick")?.count(); + await page.waitForTimeout(2000); - if (iconGroup === 0 || toyBrick === 0) { + if (iconGroupAllCount === 0 || toyBrickAllCount === 0) { expect(false).toBe(true); } }); @@ -252,6 +254,7 @@ test("should share component with share button", async ({ page }) => { .getByPlaceholder("Flow description") .inputValue(); await page.getByText("Save").last().click(); + await page.getByText("Close").last().click(); await page.getByTestId("icon-Share3").first().click(); await page.getByText("Name:").isVisible();