From 28916af8dcda2ccd4b2483e052a0044ec30a6e1c Mon Sep 17 00:00:00 2001 From: evgueniazet Date: Sun, 10 Sep 2023 19:23:29 +0200 Subject: [PATCH 1/4] refactor: remove console log --- .../UserPersonalAddAddress/UserPersonalAddAddress.tsx | 2 -- .../makeUpdateMyCustomerPersonalQueryActions.ts | 4 ---- 2 files changed, 6 deletions(-) diff --git a/e-commerce-app/src/components/UserPersonalAddAddress/UserPersonalAddAddress.tsx b/e-commerce-app/src/components/UserPersonalAddAddress/UserPersonalAddAddress.tsx index 9947179..4171f2d 100644 --- a/e-commerce-app/src/components/UserPersonalAddAddress/UserPersonalAddAddress.tsx +++ b/e-commerce-app/src/components/UserPersonalAddAddress/UserPersonalAddAddress.tsx @@ -108,8 +108,6 @@ const UserPersonalAddAddress = (): JSX.Element => { }; const submitForm: SubmitHandler = (data) => { - console.log('Submit, errors: ', errors); - console.log(data); const addressObject: IUpdateMyCustomerActionAddAddress = { action: 'addAddress', address: { diff --git a/e-commerce-app/src/utils/updateMyCustomerUtils/makeUpdateMyCustomerPersonalQueryActions.ts b/e-commerce-app/src/utils/updateMyCustomerUtils/makeUpdateMyCustomerPersonalQueryActions.ts index fc31e90..cde698f 100644 --- a/e-commerce-app/src/utils/updateMyCustomerUtils/makeUpdateMyCustomerPersonalQueryActions.ts +++ b/e-commerce-app/src/utils/updateMyCustomerUtils/makeUpdateMyCustomerPersonalQueryActions.ts @@ -16,7 +16,6 @@ export const makeUpdateMyCustomerPersonalQueryActions: MakeUpdateMyCustomerPerso action: 'setFirstName', firstName: current.firstName, }; - console.log('update first name: ', updateAction); actionsArray.push(updateAction); } @@ -25,7 +24,6 @@ export const makeUpdateMyCustomerPersonalQueryActions: MakeUpdateMyCustomerPerso action: 'setLastName', lastName: current.lastName, }; - console.log('update last name: ', updateAction); actionsArray.push(updateAction); } @@ -34,7 +32,6 @@ export const makeUpdateMyCustomerPersonalQueryActions: MakeUpdateMyCustomerPerso action: 'setDateOfBirth', dateOfBirth: current.birthDate, }; - console.log('update date of birth: ', updateAction); actionsArray.push(updateAction); } @@ -43,7 +40,6 @@ export const makeUpdateMyCustomerPersonalQueryActions: MakeUpdateMyCustomerPerso action: 'changeEmail', email: current.email, }; - console.log('update email: ', updateAction); actionsArray.push(updateAction); } From 15b102306dee808dcf1ed1461ec107566db9f657 Mon Sep 17 00:00:00 2001 From: evgueniazet Date: Sun, 17 Sep 2023 22:40:22 +0300 Subject: [PATCH 2/4] feat: add unit tests --- e-commerce-app/package-lock.json | 47 +++++++++++++++++ e-commerce-app/package.json | 10 ++++ .../components/BasketEmpty/BasketEmpty.tsx | 2 +- .../src/pages/AboutPage/AboutPage.tsx | 3 +- e-commerce-app/src/tests/AboutPage.test.tsx | 26 ++++++++++ e-commerce-app/src/tests/BasketEmpty.test.tsx | 42 ++++++++++++++++ e-commerce-app/src/tests/BasketPage.test.tsx | 46 +++++++++++++++++ e-commerce-app/src/tests/Footer.test.tsx | 8 +++ e-commerce-app/src/tests/HomePage.test.tsx | 30 +++++++++++ .../src/tests/UserPersonalContactRow.test.tsx | 50 +++++++++++++++++++ .../src/tests/capitalizeString.test.tsx | 48 ++++++++++++++++++ .../src/tests/getCountryCode.test.tsx | 30 +++++++++++ .../tests/makeGetQueryProductsString.test.tsx | 37 ++++++++++++++ e-commerce-app/src/tests/useValidate.test.tsx | 13 +++-- .../src/tests/validateCity.test.tsx | 44 ++++++++-------- e-commerce-app/tsconfig.json | 10 +--- 16 files changed, 408 insertions(+), 38 deletions(-) create mode 100644 e-commerce-app/src/tests/AboutPage.test.tsx create mode 100644 e-commerce-app/src/tests/BasketEmpty.test.tsx create mode 100644 e-commerce-app/src/tests/BasketPage.test.tsx create mode 100644 e-commerce-app/src/tests/Footer.test.tsx create mode 100644 e-commerce-app/src/tests/HomePage.test.tsx create mode 100644 e-commerce-app/src/tests/UserPersonalContactRow.test.tsx create mode 100644 e-commerce-app/src/tests/capitalizeString.test.tsx create mode 100644 e-commerce-app/src/tests/getCountryCode.test.tsx create mode 100644 e-commerce-app/src/tests/makeGetQueryProductsString.test.tsx diff --git a/e-commerce-app/package-lock.json b/e-commerce-app/package-lock.json index 74e9da4..375576e 100644 --- a/e-commerce-app/package-lock.json +++ b/e-commerce-app/package-lock.json @@ -15,8 +15,10 @@ "@mui/material": "^5.14.4", "@reduxjs/toolkit": "^1.9.5", "classnames": "^2.3.2", + "history": "^5.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.11", "react-hook-form": "^7.45.4", "react-redux": "^8.1.2", "react-router-dom": "^6.14.2", @@ -34,11 +36,13 @@ "@types/react": "^18.2.18", "@types/react-dom": "^18.2.7", "@types/react-router-dom": "^5.3.3", + "@types/redux-mock-store": "^1.0.3", "eslint": "^8.46.0", "eslint-config-prettier": "^8.9.0", "eslint-plugin-prettier": "^5.0.0", "husky": "^8.0.3", "prettier": "^3.0.0", + "redux-mock-store": "^1.5.4", "sass": "^1.66.1" } }, @@ -4731,6 +4735,15 @@ "@types/react": "*" } }, + "node_modules/@types/redux-mock-store": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.3.tgz", + "integrity": "sha512-Wqe3tJa6x9MxMN4DJnMfZoBRBRak1XTPklqj4qkVm5VBpZnC8PSADf4kLuFQ9NAdHaowfWoEeUMz7NWc2GMtnA==", + "dev": true, + "dependencies": { + "redux": "^4.0.5" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -9516,6 +9529,14 @@ "he": "bin/he" } }, + "node_modules/history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -12707,6 +12728,12 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -15292,6 +15319,17 @@ "react": "^18.2.0" } }, + "node_modules/react-error-boundary": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz", + "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-error-overlay": { "version": "6.0.11", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", @@ -15569,6 +15607,15 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/redux-mock-store": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz", + "integrity": "sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==", + "dev": true, + "dependencies": { + "lodash.isplainobject": "^4.0.6" + } + }, "node_modules/redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", diff --git a/e-commerce-app/package.json b/e-commerce-app/package.json index ed6597f..b27581b 100644 --- a/e-commerce-app/package.json +++ b/e-commerce-app/package.json @@ -11,8 +11,10 @@ "@mui/material": "^5.14.4", "@reduxjs/toolkit": "^1.9.5", "classnames": "^2.3.2", + "history": "^5.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.11", "react-hook-form": "^7.45.4", "react-redux": "^8.1.2", "react-router-dom": "^6.14.2", @@ -38,6 +40,12 @@ "react-app/jest" ] }, + "jest": { + "collectCoverageFrom": [ + "src/**/*.{js,jsx,ts,tsx}", + "!src/**/*.test.{js,jsx,ts,tsx}" + ] + }, "browserslist": { "production": [ ">0.2%", @@ -59,11 +67,13 @@ "@types/react": "^18.2.18", "@types/react-dom": "^18.2.7", "@types/react-router-dom": "^5.3.3", + "@types/redux-mock-store": "^1.0.3", "eslint": "^8.46.0", "eslint-config-prettier": "^8.9.0", "eslint-plugin-prettier": "^5.0.0", "husky": "^8.0.3", "prettier": "^3.0.0", + "redux-mock-store": "^1.5.4", "sass": "^1.66.1" } } diff --git a/e-commerce-app/src/components/BasketEmpty/BasketEmpty.tsx b/e-commerce-app/src/components/BasketEmpty/BasketEmpty.tsx index 9393286..9918038 100644 --- a/e-commerce-app/src/components/BasketEmpty/BasketEmpty.tsx +++ b/e-commerce-app/src/components/BasketEmpty/BasketEmpty.tsx @@ -11,7 +11,7 @@ const BasketEmpty = (): JSX.Element => { const navigate = useNavigate(); return ( <> - + Your cart is empty diff --git a/e-commerce-app/src/pages/AboutPage/AboutPage.tsx b/e-commerce-app/src/pages/AboutPage/AboutPage.tsx index c9a3d1c..69cf730 100644 --- a/e-commerce-app/src/pages/AboutPage/AboutPage.tsx +++ b/e-commerce-app/src/pages/AboutPage/AboutPage.tsx @@ -26,6 +26,7 @@ export const AboutPage: React.FC = () => { backgroundSize: 'cover', backgroundPosition: 'top', }} + > { We're on a mission to complete
eCommerce Application successfully. - + RSlogo diff --git a/e-commerce-app/src/tests/AboutPage.test.tsx b/e-commerce-app/src/tests/AboutPage.test.tsx new file mode 100644 index 0000000..45be3e7 --- /dev/null +++ b/e-commerce-app/src/tests/AboutPage.test.tsx @@ -0,0 +1,26 @@ +import { Provider } from 'react-redux'; +import { store } from '../store/store'; +import { AboutPage } from '../pages/AboutPage/AboutPage'; +import { render, screen } from '@testing-library/react'; + +describe('AboutPage component', () => { + test('renders component without errors', () => { + render( + + + , + ); + + expect(screen.getByText('About Us')).toBeInTheDocument(); + expect( + screen.getByText("We're on a mission to complete eCommerce Application successfully."), + ).toBeInTheDocument(); + + const githubLinks = screen.getAllByRole('link'); + githubLinks.forEach((link) => { + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute('href'); + expect(link).toHaveAttribute('target', '_blank'); + }); + }); +}); diff --git a/e-commerce-app/src/tests/BasketEmpty.test.tsx b/e-commerce-app/src/tests/BasketEmpty.test.tsx new file mode 100644 index 0000000..27a3223 --- /dev/null +++ b/e-commerce-app/src/tests/BasketEmpty.test.tsx @@ -0,0 +1,42 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import BasketEmpty from '../components/BasketEmpty/BasketEmpty'; +import { MemoryRouter } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; + +describe('BasketEmpty component', () => { + test('renders the empty cart message', () => { + render( + + + , + ); + + const emptyCartMessage = screen.getByText(/Your cart is empty/i); + expect(emptyCartMessage).toBeInTheDocument(); + }); + + test('renders the start shopping button', () => { + render( + + + , + ); + + const startShoppingButton = screen.getByRole('button', { name: /Start shopping/i }); + expect(startShoppingButton).toBeInTheDocument(); + }); + + test('clicking the start shopping button navigates to /products', () => { + const history = createMemoryHistory(); + render( + + + , + ); + + const startShoppingButton = screen.getByRole('button', { name: /Start shopping/i }); + fireEvent.click(startShoppingButton); + + expect(history.location.pathname).toBe('/'); + }); +}); diff --git a/e-commerce-app/src/tests/BasketPage.test.tsx b/e-commerce-app/src/tests/BasketPage.test.tsx new file mode 100644 index 0000000..5be2e8b --- /dev/null +++ b/e-commerce-app/src/tests/BasketPage.test.tsx @@ -0,0 +1,46 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { store } from '../store/store'; +import { BasketPage } from '../pages/BasketPage/BasketPage'; +import { MemoryRouter } from 'react-router-dom'; + +describe('BasketPage component', () => { + test('renders BasketFull when there are items in the cart', async () => { + jest.mock('../api/cartApi', () => ({ + getTotalQuantityLineItemsInCart: jest.fn().mockReturnValue(2), + })); + + render( + + + + + , + ); + + await waitFor(() => { + expect(screen.queryByText(/Shopping cart/)).toBeInTheDocument(); + expect(screen.queryByText(/Loading.../)).toBeNull(); + }); + }); + + test('renders BasketEmpty when there are no items in the cart', async () => { + jest.mock('../api/cartApi', () => ({ + getTotalQuantityLineItemsInCart: jest.fn().mockReturnValue(0), + })); + + render( + + + + + , + ); + + await waitFor(() => { + expect(screen.getByText('Your cart is empty')).toBeInTheDocument(); + expect(screen.queryByText(/Continue shopping/)).toBeNull(); + expect(screen.getByText(/Shopping cart/)).toBeInTheDocument(); + }); + }); +}); diff --git a/e-commerce-app/src/tests/Footer.test.tsx b/e-commerce-app/src/tests/Footer.test.tsx new file mode 100644 index 0000000..a1b94a0 --- /dev/null +++ b/e-commerce-app/src/tests/Footer.test.tsx @@ -0,0 +1,8 @@ +import { render } from '@testing-library/react'; +import { Footer } from '../components/Footer/Footer'; + +describe('Footer component', () => { + test('renders component without errors', () => { + render(