From 7cdb65f8db399c75a83e2c80cf497c072008be63 Mon Sep 17 00:00:00 2001 From: Shimnas E S Date: Thu, 25 Sep 2025 16:57:24 +0530 Subject: [PATCH 1/4] latest updated navbar --- src/App.js | 7 ++++++- src/index.css | 7 ++++++- src/ui/navbar.jsx | 3 ++- src/utility/auth.js | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/App.js b/src/App.js index b90d8b3..fac6bcb 100644 --- a/src/App.js +++ b/src/App.js @@ -42,8 +42,13 @@ const router = createBrowserRouter([ errorElement: , }, { + path: "/Edit", - element: , + element:( + + + + ), errorElement: , }, ]); diff --git a/src/index.css b/src/index.css index 201d410..8e6e6be 100644 --- a/src/index.css +++ b/src/index.css @@ -32,7 +32,7 @@ body { .nav-bar { display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; align-items: center; height: 7.2rem; padding: 0 3.2rem; @@ -40,6 +40,11 @@ body { border-radius: 0.9rem; } +.user-info{ + font-weight: bolder; + font-size: large; +} + .logo { display: flex; align-items: center; diff --git a/src/ui/navbar.jsx b/src/ui/navbar.jsx index 35940b0..95f2187 100644 --- a/src/ui/navbar.jsx +++ b/src/ui/navbar.jsx @@ -68,7 +68,8 @@ export default function Navbar({ )}

- +

Hey, {userInfo?.name?.split(" ")?.[0]}

+
{ const token = localStorage.getItem("user"); return !!token; From b67423a97a925d8d1803a083597b9ad4b73cfcb9 Mon Sep 17 00:00:00 2001 From: Shimnas E S Date: Fri, 26 Sep 2025 11:53:31 +0530 Subject: [PATCH 2/4] filter issue resolved --- src/features/products/productFilter.jsx | 5 +++-- src/ui/home.jsx | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/features/products/productFilter.jsx b/src/features/products/productFilter.jsx index 0b5f536..f5992e1 100644 --- a/src/features/products/productFilter.jsx +++ b/src/features/products/productFilter.jsx @@ -1,6 +1,6 @@ import { useOutletContext } from "react-router-dom"; import { useState } from "react"; -export default function ProductFilter({ applyFilters }) { +export default function ProductFilter({ applyFilters,clearFilters }) { const [isDrawerOpen, setIsDrawerOpen] = useState(false); const toggleDrawer = () => { @@ -8,10 +8,11 @@ export default function ProductFilter({ applyFilters }) { }; const clearFilter = () => { - setSortBy(""); + setSortBy("none"); setFilter("none"); setPriceRange([0, 1000]); setIsDrawerOpen(false); + clearFilters(); }; const { filter, setFilter, sortBy, setSortBy, priceRange, setPriceRange } = diff --git a/src/ui/home.jsx b/src/ui/home.jsx index 9e952fe..6886d44 100644 --- a/src/ui/home.jsx +++ b/src/ui/home.jsx @@ -32,6 +32,10 @@ export default function Home() { setFiltersApplied((prev) => !prev); } + function clearFilters() { + setFiltersApplied(false); + } + return ( <> From e569f91c6c20bb6066b6626d1bb337cf1233764a Mon Sep 17 00:00:00 2001 From: Shimnas E S Date: Mon, 29 Sep 2025 22:19:36 +0530 Subject: [PATCH 3/4] Jest unit test and configurations --- .babelrc | 6 + package-lock.json | 210 +++++++++++------- package.json | 19 +- src/App.test.js | 8 - src/features/cart/_tests/cartItem.test.jsx | 72 ++++++ src/features/cart/cartItem.jsx | 6 +- src/features/orders/_tests/orderList.test.jsx | 62 ++++++ src/features/orders/_tests/review.test.js | 105 +++++++++ src/features/orders/review.jsx | 8 +- .../products/_test/productDetails.test.jsx | 78 +++++++ .../products/_test/productItem.test.jsx | 59 +++++ .../products/_test/productList.test.jsx | 36 +++ .../products/_test/productReview.test.jsx | 54 +++++ src/features/products/productDetails.jsx | 6 +- src/jest.config.js | 17 ++ src/jest.setup.js | 13 ++ 16 files changed, 660 insertions(+), 99 deletions(-) create mode 100644 .babelrc delete mode 100644 src/App.test.js create mode 100644 src/features/cart/_tests/cartItem.test.jsx create mode 100644 src/features/orders/_tests/orderList.test.jsx create mode 100644 src/features/orders/_tests/review.test.js create mode 100644 src/features/products/_test/productDetails.test.jsx create mode 100644 src/features/products/_test/productItem.test.jsx create mode 100644 src/features/products/_test/productList.test.jsx create mode 100644 src/features/products/_test/productReview.test.jsx create mode 100644 src/jest.config.js create mode 100644 src/jest.setup.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..a8a04a8 --- /dev/null +++ b/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + "@babel/preset-env", + ["@babel/preset-react", { "runtime": "automatic" }] + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 029326e..cbe8aa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,23 +11,30 @@ "@fortawesome/fontawesome-free": "^7.0.1", "@reduxjs/toolkit": "^2.9.0", "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.8.0", - "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.12.2", "react": "^19.1.1", "react-dom": "^19.1.1", "react-redux": "^9.2.0", - "react-router-dom": "^7.9.1", + "react-router-dom": "^7.9.3", "react-saga": "^0.3.1", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@babel/preset-react": "^7.27.1", + "@testing-library/jest-dom": "^6.8.0", + "@testing-library/react": "^16.3.0", + "jest": "^27.5.1", + "jest-environment-jsdom": "^27.0.3", + "redux-mock-store": "^1.5.5" } }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, "license": "MIT" }, "node_modules/@alloc/quick-lru": { @@ -2780,18 +2787,6 @@ "node": ">=0.10.0" } }, - "node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, "node_modules/@jest/source-map": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", @@ -3276,12 +3271,6 @@ "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", "license": "MIT" }, - "node_modules/@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "license": "MIT" - }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -3577,6 +3566,7 @@ "version": "6.8.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.8.0.tgz", "integrity": "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==", + "dev": true, "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", @@ -3596,12 +3586,14 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, "license": "MIT" }, "node_modules/@testing-library/react": { "version": "16.3.0", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" @@ -6387,6 +6379,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, "license": "MIT" }, "node_modules/cssdb": { @@ -8599,20 +8592,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -9415,6 +9394,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10279,6 +10259,24 @@ } } }, + "node_modules/jest-config/node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -10323,17 +10321,18 @@ } }, "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "version": "27.0.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.0.3.tgz", + "integrity": "sha512-5KLmgv1bhiimpSA8oGTnZYk6g4fsNyZiA/6gI2tAZUgrufd7heRUSVh4gRokzZVEj8zlwAQYT0Zs6tuJSW/ECA==", + "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/environment": "^27.0.3", + "@jest/fake-timers": "^27.0.3", + "@jest/types": "^27.0.2", "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", + "jest-mock": "^27.0.3", + "jest-util": "^27.0.2", "jsdom": "^16.6.0" }, "engines": { @@ -10574,6 +10573,24 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/jest-runner/node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/jest-runtime": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", @@ -10734,6 +10751,18 @@ "node": ">=8" } }, + "node_modules/jest-watch-typeahead/node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", @@ -10766,6 +10795,12 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/jest-watch-typeahead/node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "license": "MIT" + }, "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -11104,6 +11139,27 @@ } } }, + "node_modules/jsdom/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -11371,6 +11427,13 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, + "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, + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -11604,6 +11667,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -14161,9 +14225,9 @@ } }, "node_modules/react-router": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz", - "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==", + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.3.tgz", + "integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -14183,12 +14247,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.1.tgz", - "integrity": "sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==", + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.3.tgz", + "integrity": "sha512-1QSbA0TGGFKTAc/aWjpfW/zoEukYfU4dc1dLkT/vvf54JoGMkW+fNA+3oyo2gWVW1GM7BxjJVHz5GnPJv40rvg==", "license": "MIT", "dependencies": { - "react-router": "7.9.1" + "react-router": "7.9.3" }, "engines": { "node": ">=20.0.0" @@ -14347,6 +14411,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, "license": "MIT", "dependencies": { "indent-string": "^4.0.0", @@ -14362,6 +14427,19 @@ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", "license": "MIT" }, + "node_modules/redux-mock-store": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.5.tgz", + "integrity": "sha512-YxX+ofKUTQkZE4HbhYG4kKGr7oCTJfB0GLy7bSeqx86GLpGirrbUWstMnqXkqHNaQpcnbMGbof2dYs5KsPE6Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.isplainobject": "^4.0.6" + }, + "peerDependencies": { + "redux": "*" + } + }, "node_modules/redux-saga": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.3.0.tgz", @@ -15864,6 +15942,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, "license": "MIT", "dependencies": { "min-indent": "^1.0.0" @@ -17186,27 +17265,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/webpack-manifest-plugin": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", @@ -17833,16 +17891,16 @@ } }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { diff --git a/package.json b/package.json index 3d18432..46693f5 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,12 @@ "@fortawesome/fontawesome-free": "^7.0.1", "@reduxjs/toolkit": "^2.9.0", "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.8.0", - "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.12.2", "react": "^19.1.1", "react-dom": "^19.1.1", "react-redux": "^9.2.0", - "react-router-dom": "^7.9.1", + "react-router-dom": "^7.9.3", "react-saga": "^0.3.1", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" @@ -21,7 +19,7 @@ "scripts": { "start": "react-scripts start", "build": "react-scripts build", - "test": "react-scripts test", + "test": "jest", "eject": "react-scripts eject" }, "eslintConfig": { @@ -30,6 +28,11 @@ "react-app/jest" ] }, + "jest": { + "setupFilesAfterEnv": [ + "/src/setupTests.js" + ] + }, "browserslist": { "production": [ ">0.2%", @@ -41,5 +44,13 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@babel/preset-react": "^7.27.1", + "@testing-library/jest-dom": "^6.8.0", + "@testing-library/react": "^16.3.0", + "jest": "^27.5.1", + "jest-environment-jsdom": "^27.0.3", + "redux-mock-store": "^1.5.5" } } diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index 1f03afe..0000000 --- a/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/features/cart/_tests/cartItem.test.jsx b/src/features/cart/_tests/cartItem.test.jsx new file mode 100644 index 0000000..3edda7f --- /dev/null +++ b/src/features/cart/_tests/cartItem.test.jsx @@ -0,0 +1,72 @@ +/** + * @jest-environment jsdom + */ + +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import CartItem from '../CartItem'; + +// Mock redux useDispatch +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => ({ + useDispatch: () => mockDispatch, +})); + +// Mock OrderReview component when actions is false +jest.mock('../../orders/review', () => () =>
); + +describe('CartItem component', () => { + const baseCartItem = { + id: 1, + image: 'test-image.jpg', + title: 'Test Product', + category: 'electronics', + unitPrice: 100, + quantity: 1, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('renders cart item details correctly', () => { + render(); + + expect(screen.getByAltText('Test Product')).toBeInTheDocument(); + expect(screen.getByText('Test Product')).toBeInTheDocument(); + expect(screen.getByText('electronics')).toBeInTheDocument(); + expect(screen.getByText(/Price:/)).toBeInTheDocument("$100"); + expect(screen.getByText('1')).toBeInTheDocument(); + }); + + test('shows delete button when quantity is 1, and clicking calls dispatch with deleteFromCart', () => { + render(); + + const deleteBtn = screen.getByTestId('delete-icon') ; + fireEvent.click(deleteBtn); + expect(mockDispatch).toHaveBeenCalled(); + // Optional: check called action type and payload if you mock cartSlice action creators + }); + + test('shows decrease button when quantity is > 1, and clicking calls dispatch with decreaseItemQuantity', () => { + render(); + + const decreaseBtn =screen.getByTestId('minus-icon') ; + fireEvent.click(decreaseBtn); + expect(mockDispatch).toHaveBeenCalled(); + }); + + test('shows increase button and clicking calls dispatch with increaseItemQuantity', () => { + render(); + + const increaseBtn = screen.getByTestId('plus-icon') ; + fireEvent.click(increaseBtn); + expect(mockDispatch).toHaveBeenCalled(); + }); + + test('renders OrderReview component when actions is false', () => { + render(); + + expect(screen.getByTestId('order-review')).toBeInTheDocument(); + }); +}); diff --git a/src/features/cart/cartItem.jsx b/src/features/cart/cartItem.jsx index 1bf483d..9f8eb51 100644 --- a/src/features/cart/cartItem.jsx +++ b/src/features/cart/cartItem.jsx @@ -33,7 +33,7 @@ export default function CartItem({ cartItem, actions }) { className="btn delete-btn" onClick={() => dispatch(deleteFromCart(cartItem.id))} > - + )} @@ -42,7 +42,7 @@ export default function CartItem({ cartItem, actions }) { className="btn delete-btn" onClick={() => dispatch(decreaseItemQuantity(cartItem.id))} > - + )} @@ -51,7 +51,7 @@ export default function CartItem({ cartItem, actions }) { className="btn add-btn" onClick={() => dispatch(increaseItemQuantity(cartItem.id))} > - +
)} diff --git a/src/features/orders/_tests/orderList.test.jsx b/src/features/orders/_tests/orderList.test.jsx new file mode 100644 index 0000000..77b72a0 --- /dev/null +++ b/src/features/orders/_tests/orderList.test.jsx @@ -0,0 +1,62 @@ +/** + * @jest-environment jsdom + */ + +import React from "react"; +import { render, screen } from "@testing-library/react"; +import OrderList from "../orderList"; +import { useSelector } from "react-redux"; +import * as reactRedux from 'react-redux'; + +// Mock CartItem to isolate OrderList testing +jest.mock("../../cart/cartItem", () => ({ cartItem, actions }) => ( +
{cartItem.title}
+)); + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useSelector: jest.fn(), // Make useSelector a Jest mock function +})); + +describe("OrderList component", () => { + beforeEach(() => { + reactRedux.useSelector.mockImplementation((selector) => + selector({ + order: { + orders: [ + { id: 1, title: "Order 1" }, + { id: 2, title: "Order 2" }, + ], + }, + }) + ); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + test("renders order list header and items from Redux state", () => { + // Mock useSelector to return orders array + useSelector.mockImplementation((selectorFn) => + selectorFn({ + order: { + orders: [ + { id: 1, title: "Order 1" }, + { id: 2, title: "Order 2" }, + ], + }, + }) + ); + + render(); + + expect(screen.getByText(/your order\(s\)/i)).toBeInTheDocument(); + + // Check mocked CartItem components rendered for each order entry + const orderItems = screen.getAllByTestId("cart-item"); + expect(orderItems).toHaveLength(2); + expect(orderItems[0]).toHaveTextContent("Order 1"); + expect(orderItems[1]).toHaveTextContent("Order 2"); + }); +}); diff --git a/src/features/orders/_tests/review.test.js b/src/features/orders/_tests/review.test.js new file mode 100644 index 0000000..96391d0 --- /dev/null +++ b/src/features/orders/_tests/review.test.js @@ -0,0 +1,105 @@ +/** + * @jest-environment jsdom + */ + +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; +import OrderReview from "../review"; + +// Mock useDispatch hook +const mockDispatch = jest.fn(); +jest.mock("react-redux", () => ({ + useDispatch: () => mockDispatch, +})); + +describe("OrderReview component", () => { + beforeAll(() => { + global.crypto = { + randomUUID: jest.fn(() => "test-uuid"), + }; + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + test("renders no reviews message initially", () => { + render(); + expect(screen.getByText(/no reviews yet/i)).toBeInTheDocument(); + }); + + test("allows user to submit a review", () => { + render(); + + // Click on the 5th star to give rating 5 + const stars = screen.getAllByText("★"); + fireEvent.click(stars[4]); + + // Input comment + const textarea = screen.getByPlaceholderText(/write your review/i); + fireEvent.change(textarea, { target: { value: "Great product!" } }); + + // Click submit button + const submitButton = screen.getByRole("button", { name: /submit review/i }); + fireEvent.click(submitButton); + + // Verify dispatch was called with an object containing rating, comments, itemId + expect(mockDispatch).toHaveBeenCalledWith( + expect.objectContaining({ + type: expect.any(String), + payload: expect.objectContaining({ + itemId: "123", + rating: 5, + comments: "Great product!", + }), + }) + ); + + // Review should appear in the review list + expect(screen.getByText(/great product!/i)).toBeInTheDocument(); + }); + + test("prevents submitting empty rating or comment", () => { + render(); + + const submitButton = screen.getByRole("button", { name: /submit review/i }); + fireEvent.click(submitButton); + + // Dispatch should not be called + expect(mockDispatch).not.toHaveBeenCalled(); + }); + + test("allows editing and deleting reviews", () => { + render(); + + // Add a review first + const stars = screen.getAllByText("★"); + fireEvent.click(stars[4]); + const textarea = screen.getByPlaceholderText(/write your review/i); + fireEvent.change(textarea, { target: { value: "Nice product!" } }); + fireEvent.click(screen.getByRole("button", { name: /submit review/i })); + + // Click edit button (the edit icon button) + const editButton = screen.getByTestId("edit-icon"); + fireEvent.click(editButton); + + // The textarea should be filled with existing comment + expect(textarea.value).toBe("Nice product!"); + + // Change comment and submit updated review + fireEvent.change(textarea, { target: { value: "Updated review" } }); + fireEvent.click(screen.getByRole("button", { name: /update review/i })); + + expect(mockDispatch).toHaveBeenCalledTimes(2); // 1 for add, 1 for update + expect(screen.getByText(/updated review/i)).toBeInTheDocument(); + + // Mock window.confirm to always return true for delete + window.confirm = jest.fn(() => true); + + // Click delete button + const deleteButton = screen.getByTitle("Delete"); + fireEvent.click(deleteButton); + + expect(mockDispatch).toHaveBeenCalledTimes(3); // 3rd call for delete + }); +}); diff --git a/src/features/orders/review.jsx b/src/features/orders/review.jsx index 71fb221..7ccd86c 100644 --- a/src/features/orders/review.jsx +++ b/src/features/orders/review.jsx @@ -10,9 +10,7 @@ export default function OrderReview({ itemId }) { const [reviewId, setReviewId] = useState(null); const dispatch = useDispatch(); -// const currentState = useSelector((state) => state.order); -// console.log(currentState); - // if(currentState){} + const handleSubmit = () => { if (rating === 0 || comment.trim() === "") return; @@ -102,7 +100,7 @@ export default function OrderReview({ itemId }) { alignItems: "center", }} > - {console.log("review", review)} +
{"★".repeat(review.rating)} @@ -122,7 +120,7 @@ export default function OrderReview({ itemId }) { cursor: "pointer", }} > - + +
+)); + +//describe +describe("Product details Component", () => { + const product = { + id: 1, + title: "Test Product", + description: + "This is a test product description that is definitely longer than 100 characters for testing truncation of the description text in the component.", + image: "test-image.jpg", + price: 42.5, + rating: 4.2, + }; + + test("product detail page render with truncated description", () => { + render(); + const name = screen.getByText("Test Product"); + expect(name).toBeInTheDocument(); + expect(name).toHaveTextContent("Test Product"); + expect(screen.getByText(/Price:/)).toBeInTheDocument("$42.5"); + const truncatedDescription = product.description.slice(0, 50) + "..."; + expect(screen.getByText(truncatedDescription)).toBeInTheDocument(); + expect(screen.getByTestId("product-review")).toHaveTextContent( + "Rating: 4.2" + ); + }); + + test("shows and hides popup on click and close button", () => { + render(); + // Popup should not be hidden + expect(screen.queryByTestId("product-description")).not.toBeInTheDocument(); + + // Click on product card to show popup + fireEvent.click(screen.getByText("Test Product")); + expect(screen.getByTestId("product-description")).toBeInTheDocument(); + + // Click close button in popup to hide it + fireEvent.click(screen.getByText("Close")); + expect(screen.queryByTestId("product-description")).not.toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/src/features/products/_test/productList.test.jsx b/src/features/products/_test/productList.test.jsx new file mode 100644 index 0000000..74ed4fd --- /dev/null +++ b/src/features/products/_test/productList.test.jsx @@ -0,0 +1,36 @@ +/** + * @jest-environment jsdom + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import ProductList from '../productList'; + +// Mock Product component to isolate ProductList test +jest.mock('../productItem', () => ({ product }) => ( +
{product.title}
+)); + +describe('ProductList component', () => { + test('renders no products message when list is empty', () => { + render( {}} />); + expect(screen.getByText(/no products found/i)).toBeInTheDocument(); + }); + + test('renders product items when products are present', () => { + const products = [ + { id: 1, title: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops' }, + { id: 2, title: 'Mens Casual Premium Slim Fit T-Shirts' }, + ]; + render( {}} />); + + // The "No products found." message should not be there + expect(screen.queryByText(/no products found/i)).not.toBeInTheDocument(); + + // Check that both product items render via mock Product component + const productItems = screen.getAllByTestId('product-item'); + expect(productItems).toHaveLength(2); + expect(productItems[0]).toHaveTextContent('Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops'); + expect(productItems[1]).toHaveTextContent('Mens Casual Premium Slim Fit T-Shirts'); + }); +}); diff --git a/src/features/products/_test/productReview.test.jsx b/src/features/products/_test/productReview.test.jsx new file mode 100644 index 0000000..0712b3b --- /dev/null +++ b/src/features/products/_test/productReview.test.jsx @@ -0,0 +1,54 @@ + + +/** + * @jest-environment jsdom + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import ProductReview from '../productReview'; + +describe('ProductReview rating component', () => { + test('renders should show number of gold , gray stars, and review count based on rating', () => { + const rating = { rate: 3.6, count: 42 }; + render(); + + // Based on provided rating ,Rounded stars should be 4 gold stars + const goldStars = screen.getAllByText('★').filter( + (el) => el.style.color === 'rgb(255, 215, 0)' + ); + //check the length/count of the gold star + expect(goldStars).toHaveLength(4); + + // Remaining stars should be gray + const grayStars = screen.getAllByText('★').filter( + (el) => el.style.color === 'rgb(204, 204, 204)' + ); + //check the length/count of the grey star + expect(grayStars).toHaveLength(1); + + // Check review count text content + expect(screen.getByText(/\(42 reviews\)/)).toBeInTheDocument(); + + // Check that the prefix "Rating:" is present + expect(screen.getByText(/Rating:/)).toBeInTheDocument(); + }); + + test('render grey star when rating is not provided', () => { + render(); + + //gold star should be of count 0 + const goldStars = screen.queryAllByText('★').filter( + (el) => el.style.color === 'rgb(255, 215, 0)' + ); + expect(goldStars).toHaveLength(0); + + //all the star should be grey + const grayStars = screen.queryAllByText('★').filter( + (el) => el.style.color === 'rgb(204, 204, 204)' + ); + expect(grayStars).toHaveLength(5); + + expect(screen.getByText(/\(0 reviews\)/)).toBeInTheDocument(); + }); +}); diff --git a/src/features/products/productDetails.jsx b/src/features/products/productDetails.jsx index 2a265a0..cc1d952 100644 --- a/src/features/products/productDetails.jsx +++ b/src/features/products/productDetails.jsx @@ -64,16 +64,16 @@ export default function ProductDescription({ id, handleShowPopup }) {

{product.description}

- Price: ${product.price} + Price: ${product.price}

- Product Category :{product.category} + Product Category : {product.category}

-