From 80332ff79e39748bcba2ed3c5045d5ef426e081e Mon Sep 17 00:00:00 2001 From: Mateusz Dybowski Date: Tue, 9 Jan 2024 18:23:49 +0100 Subject: [PATCH] Update UI dependencies (#1180) * Update package.json dependencies * Fix legacy react code * Fix ESLint errors and warnings --- ui/package.json | 55 +- ui/src/App.test.tsx | 6 +- .../TwoColumnHero/TwoColumnHero.tsx | 8 +- .../contexts/UserContext/UserContext.test.tsx | 85 +- ui/src/contexts/UserContext/UserContext.tsx | 18 +- ui/src/index.tsx | 5 +- ui/src/main/Footer/Footer.test.tsx | 12 +- ui/src/main/ForkMe/ForkMe.test.tsx | 10 +- ui/src/main/ForkMe/ForkMe.tsx | 6 +- ui/src/main/Loader/Loader.test.tsx | 6 +- ui/src/main/Main/Main.test.tsx | 20 +- .../main/Main/useLocalStoragedApiKey.test.tsx | 45 +- ui/src/main/Main/useLoginOnApiKey.test.tsx | 41 +- ui/src/main/Routes/ProtectedRoute.test.tsx | 14 +- ui/src/main/Routes/Routes.test.tsx | 26 +- ui/src/main/Top/Top.test.tsx | 43 +- ui/src/main/Top/Top.tsx | 42 +- ui/src/pages/Login/Login.test.tsx | 61 +- ui/src/pages/Login/Login.tsx | 2 +- ui/src/pages/NotFound/NotFound.test.tsx | 6 +- .../PasswordReset/PasswordReset.test.tsx | 60 +- ui/src/pages/PasswordReset/PasswordReset.tsx | 2 +- ui/src/pages/Profile/Profile.test.tsx | 10 +- .../components/PasswordDetails.test.tsx | 86 +- .../Profile/components/PasswordDetails.tsx | 2 +- .../components/ProfileDetails.test.tsx | 93 +- .../Profile/components/ProfileDetails.tsx | 2 +- .../RecoverLostPassword.test.tsx | 38 +- .../RecoverLostPassword.tsx | 2 +- ui/src/pages/Register/Register.test.tsx | 76 +- ui/src/pages/Register/Register.tsx | 4 +- ui/src/pages/SecretMain/SecretMain.test.tsx | 6 +- ui/src/pages/Welcome/Welcome.test.tsx | 8 +- ui/src/serviceWorker.ts | 58 +- .../PasswordService/PasswordService.test.ts | 4 +- .../services/UserService/UserService.test.ts | 10 +- .../VersionService/VersionService.test.ts | 2 +- ui/src/setupTests.ts | 2 +- ui/yarn.lock | 11269 +++++++--------- 39 files changed, 5693 insertions(+), 6552 deletions(-) diff --git a/ui/package.json b/ui/package.json index a7d5cc08d..6f07900b5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -7,37 +7,37 @@ "node": ">=16" }, "dependencies": { - "@testing-library/jest-dom": "^5.16.4", - "@testing-library/react": "^12.0.0", - "@testing-library/user-event": "^14.3.0", - "@types/jest": "^26.0.0", - "@types/node": "^16.0.0", - "@types/react": "^17.0.0", - "@types/react-dom": "^17.0.0", + "@testing-library/jest-dom": "^6.2.0", + "@testing-library/react": "^14.1.2", + "@testing-library/user-event": "^14.5.2", + "@types/jest": "^29.2.6", + "@types/node": "^20.10.7", + "@types/react": "^18.2.47", + "@types/react-dom": "^18.2.18", "@types/react-icons": "^3.0.0", - "@types/react-router-bootstrap": "^0.24.5", + "@types/react-router-bootstrap": "^0.26.6", "@types/react-router-dom": "^5.3.3", - "@types/yup": "^0.29.14", - "axios": "^0.27.2", - "bootstrap": "^5.2.0", - "eslint-config-prettier": "^8.5.0", - "formik": "^2.2.9", - "immer": "^9.0.15", - "prettier": "^2.7.1", - "react": "^17.0.0", - "react-bootstrap": "^2.4.0", - "react-dom": "^17.0.0", - "react-icons": "^4.4.0", + "@types/yup": "^0.32.0", + "axios": "^1.6.5", + "bootstrap": "^5.3.2", + "eslint-config-prettier": "^9.1.0", + "formik": "^2.4.5", + "immer": "^10.0.3", + "prettier": "^3.1.1", + "react": "^18.2.0", + "react-bootstrap": "^2.9.2", + "react-dom": "^18.2.0", + "react-icons": "^4.12.0", "react-router-bootstrap": "^0.26.2", - "react-router-dom": "^6.3.0", - "react-scripts": "^4.0.0", - "react-use-promise-matcher": "^1.4.2", - "typescript": "^4.7.4", - "yup": "^0.32.11" + "react-router-dom": "^6.21.1", + "react-scripts": "^5.0.1", + "react-use-promise-matcher": "^1.5.0", + "typescript": "5.1.6", + "yup": "^1.3.3" }, "scripts": { - "start": "react-scripts --openssl-legacy-provider start", - "build": "react-scripts --openssl-legacy-provider build", + "start": "react-scripts start", + "build": "react-scripts build", "test": "react-scripts test", "test:coverage": "react-scripts test --coverage --watchAll=false", "test:ci": "CI=true react-scripts test --maxWorkers 2", @@ -56,6 +56,9 @@ "src/**/*.{ts,tsx}", "!src/index.tsx", "!src/serviceWorker.ts" + ], + "transformIgnorePatterns": [ + "node_modules/(?!(axios)/)" ] }, "browserslist": { diff --git a/ui/src/App.test.tsx b/ui/src/App.test.tsx index 159488afc..579ce6dfd 100644 --- a/ui/src/App.test.tsx +++ b/ui/src/App.test.tsx @@ -1,8 +1,8 @@ -import { render } from "@testing-library/react"; +import { render, screen } from "@testing-library/react"; import { App } from "./App"; test("should render", () => { - const { getByText } = render(); - const header = getByText("Welcome to Bootzooka!"); + render(); + const header = screen.getByText("Welcome to Bootzooka!"); expect(header).toBeInTheDocument(); }); diff --git a/ui/src/components/TwoColumnHero/TwoColumnHero.tsx b/ui/src/components/TwoColumnHero/TwoColumnHero.tsx index a97f187b4..0ae7bdd14 100644 --- a/ui/src/components/TwoColumnHero/TwoColumnHero.tsx +++ b/ui/src/components/TwoColumnHero/TwoColumnHero.tsx @@ -1,3 +1,5 @@ +import React from "react"; + import Container from "react-bootstrap/Container"; import Col from "react-bootstrap/Col"; import Row from "react-bootstrap/Row"; @@ -5,7 +7,11 @@ import Fade from "react-bootstrap/Fade"; import Image from "react-bootstrap/Image"; import logo from "assets/sml-logo-vertical-white-all-trans.png"; -export const TwoColumnHero: React.FC = ({ children }) => { +interface TwoColumnHeroProps { + children: React.ReactNode; +} + +export const TwoColumnHero: React.FC = ({ children }) => { return ( diff --git a/ui/src/contexts/UserContext/UserContext.test.tsx b/ui/src/contexts/UserContext/UserContext.test.tsx index 42a9e5796..49f41a810 100644 --- a/ui/src/contexts/UserContext/UserContext.test.tsx +++ b/ui/src/contexts/UserContext/UserContext.test.tsx @@ -1,33 +1,34 @@ import React from "react"; -import { render, fireEvent } from "@testing-library/react"; +import { render, screen } from "@testing-library/react"; import { UserContextProvider, UserContext, UserAction } from "./UserContext"; +import { userEvent } from "@testing-library/user-event"; const TestComponent: React.FC<{ action?: UserAction; label?: string }> = ({ action, label }) => { const { state, dispatch } = React.useContext(UserContext); return ( <>
{JSON.stringify(state)}
- {action && dispatch(action)}>{label}} + {action && } ); }; -test("handles set api key action", () => { - const { getByText } = render( +test("handles set api key action", async () => { + render( - + , ); - expect(getByText('{"apiKey":null,"user":null,"loggedIn":null}')).toBeInTheDocument(); + expect(screen.getByText('{"apiKey":null,"user":null,"loggedIn":null}')).toBeInTheDocument(); - fireEvent.click(getByText("set api key")); + await userEvent.click(screen.getByText("set api key")); - expect(getByText('{"apiKey":"test-api-key","user":null,"loggedIn":null}')).toBeInTheDocument(); + expect(screen.getByText('{"apiKey":"test-api-key","user":null,"loggedIn":null}')).toBeInTheDocument(); }); -test("handles login action", () => { - const { getByText } = render( +test("handles login action", async () => { + render( { }} label="log in" /> - + , ); - expect(getByText('{"apiKey":null,"user":null,"loggedIn":null}')).toBeInTheDocument(); + expect(screen.getByText('{"apiKey":null,"user":null,"loggedIn":null}')).toBeInTheDocument(); - fireEvent.click(getByText("log in")); + await userEvent.click(screen.getByText("log in")); expect( - getByText( - '{"apiKey":null,"user":{"login":"user-login","email":"email@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}' - ) + screen.getByText( + '{"apiKey":null,"user":{"login":"user-login","email":"email@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}', + ), ).toBeInTheDocument(); }); -test("handles log in and update details action", () => { - const { getAllByText, getByText } = render( +test("handles log in and update details action", async () => { + render( { }} label="update user data" /> - + , ); - expect(getAllByText('{"apiKey":null,"user":null,"loggedIn":null}')[0]).toBeInTheDocument(); + expect(screen.getAllByText('{"apiKey":null,"user":null,"loggedIn":null}')[0]).toBeInTheDocument(); - fireEvent.click(getByText("update user data")); + await userEvent.click(screen.getByText("update user data")); - expect(getAllByText('{"apiKey":null,"user":null,"loggedIn":null}')[0]).toBeInTheDocument(); + expect(screen.getAllByText('{"apiKey":null,"user":null,"loggedIn":null}')[0]).toBeInTheDocument(); - fireEvent.click(getByText("log in")); + await userEvent.click(screen.getByText("log in")); expect( - getAllByText( - '{"apiKey":null,"user":{"login":"user-login","email":"email@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}' - )[0] + screen.getAllByText( + '{"apiKey":null,"user":{"login":"user-login","email":"email@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}', + )[0], ).toBeInTheDocument(); - fireEvent.click(getByText("update user data")); + await userEvent.click(screen.getByText("update user data")); expect( - getAllByText( - '{"apiKey":null,"user":{"login":"updated-user-login","email":"updatedEmail@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}' - )[0] + screen.getAllByText( + '{"apiKey":null,"user":{"login":"updated-user-login","email":"updatedEmail@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}', + )[0], ).toBeInTheDocument(); }); -test("handles log in and log out action", () => { - const { getAllByText, getByText } = render( +test("handles log in and log out action", async () => { + render( { }} label="log out" /> - + , ); - expect(getAllByText('{"apiKey":null,"user":null,"loggedIn":null}')[0]).toBeInTheDocument(); + expect(screen.getAllByText('{"apiKey":null,"user":null,"loggedIn":null}')[0]).toBeInTheDocument(); - fireEvent.click(getByText("log out")); + await userEvent.click(screen.getByText("log out")); - expect(getAllByText('{"apiKey":null,"user":null,"loggedIn":false}')[0]).toBeInTheDocument(); + expect(screen.getAllByText('{"apiKey":null,"user":null,"loggedIn":false}')[0]).toBeInTheDocument(); - fireEvent.click(getByText("log in")); + await userEvent.click(screen.getByText("log in")); expect( - getAllByText( - '{"apiKey":null,"user":{"login":"user-login","email":"email@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}' - )[0] + screen.getAllByText( + '{"apiKey":null,"user":{"login":"user-login","email":"email@address.pl","createdOn":"2020-10-09T09:57:17.995288Z"},"loggedIn":true}', + )[0], ).toBeInTheDocument(); - fireEvent.click(getByText("log out")); + await userEvent.click(screen.getByText("log out")); - expect(getAllByText('{"apiKey":null,"user":null,"loggedIn":false}')[0]).toBeInTheDocument(); + expect(screen.getAllByText('{"apiKey":null,"user":null,"loggedIn":false}')[0]).toBeInTheDocument(); }); diff --git a/ui/src/contexts/UserContext/UserContext.tsx b/ui/src/contexts/UserContext/UserContext.tsx index 72c447ac1..63bc65978 100644 --- a/ui/src/contexts/UserContext/UserContext.tsx +++ b/ui/src/contexts/UserContext/UserContext.tsx @@ -1,5 +1,5 @@ -import React from "react"; -import immer from "immer"; +import React, { ReactNode } from "react"; +import { produce } from "immer"; export interface UserDetails { createdOn: string; @@ -28,24 +28,24 @@ export type UserAction = const userReducer = (state: UserState, action: UserAction): UserState => { switch (action.type) { case "SET_API_KEY": - return immer(state, (draftState) => { + return produce(state, (draftState) => { draftState.apiKey = action.apiKey; }); case "UPDATE_USER_DATA": - return immer(state, (draftState) => { + return produce(state, (draftState) => { if (!draftState.user) return; draftState.user = { ...draftState.user, ...action.user }; }); case "LOG_IN": - return immer(state, (draftState) => { + return produce(state, (draftState) => { draftState.user = action.user; draftState.loggedIn = true; }); case "LOG_OUT": - return immer(state, (draftState) => { + return produce(state, (draftState) => { draftState.apiKey = null; draftState.user = null; draftState.loggedIn = false; @@ -64,7 +64,11 @@ export const UserContext = React.createContext<{ dispatch: () => {}, }); -export const UserContextProvider: React.FC = ({ children }) => { +interface UserContextProviderProps { + children: ReactNode; +} + +export const UserContextProvider: React.FC = ({ children }) => { const [state, dispatch] = React.useReducer(userReducer, initialUserState); return {children}; diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 0d74ae7ff..c5f284f4f 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -1,10 +1,11 @@ -import ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; import { App } from "./App"; import * as serviceWorker from "./serviceWorker"; import "bootstrap/dist/css/bootstrap.min.css"; import "./index.css"; -ReactDOM.render(, document.getElementById("root")); +const root = createRoot(document.getElementById("root")!); +root.render(); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. diff --git a/ui/src/main/Footer/Footer.test.tsx b/ui/src/main/Footer/Footer.test.tsx index 18267a74f..abab31dd2 100644 --- a/ui/src/main/Footer/Footer.test.tsx +++ b/ui/src/main/Footer/Footer.test.tsx @@ -1,4 +1,4 @@ -import { render } from "@testing-library/react"; +import { render, screen } from "@testing-library/react"; import { Footer } from "./Footer"; import { versionService } from "services"; @@ -11,15 +11,15 @@ beforeEach(() => { test("renders version data", async () => { (versionService.getVersion as jest.Mock).mockResolvedValueOnce({ buildDate: "testDate", buildSha: "testSha" }); - const { getByText, findByText, findAllByRole } = render(