-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(wishes):buyer should be able wish a products
-Buyer should wish a products -Buyer should add a wished products to cart -Seller should be able to see a wished products [Deliver #187419141]
- Loading branch information
1 parent
a738f89
commit 763fa5c
Showing
14 changed files
with
388 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import MockAdapter from "axios-mock-adapter"; | ||
import { configureStore } from "@reduxjs/toolkit"; | ||
|
||
import cartReducer, { | ||
cartManage, | ||
cartDelete, | ||
addToCart, | ||
removeFromCart, | ||
updateCarts, | ||
increaseQuantity, | ||
decreaseQuantity, | ||
} from "../redux/reducers/cartSlice"; | ||
import api from "../redux/api/action"; | ||
|
||
const mock = new MockAdapter(api); | ||
|
||
describe("test improvement on cart", () => { | ||
beforeEach(() => { | ||
mock.reset(); | ||
}); | ||
|
||
it("cartManage dispatches fulfilled action when data is returned", async () => { | ||
const store = configureStore({ reducer: { carts: cartReducer } }); | ||
const mockCartItems = [{ id: 1, name: "Item 1", quantity: 1 }]; | ||
mock.onGet("/carts").reply(200, { userCart: { items: mockCartItems } }); | ||
|
||
await store.dispatch(cartManage()); | ||
const state = store.getState(); | ||
expect(state.carts.data).toEqual(mockCartItems); | ||
expect(state.carts.isLoading).toBe(false); | ||
expect(state.carts.error).toBe(false); | ||
}); | ||
|
||
it("cartManage dispatches rejected action on failed request", async () => { | ||
const store = configureStore({ reducer: { carts: cartReducer } }); | ||
mock.onGet("/carts").networkError(); | ||
|
||
await store.dispatch(cartManage()); | ||
const state = store.getState(); | ||
expect(state.carts.data).toEqual([]); | ||
expect(state.carts.isLoading).toBe(false); | ||
expect(state.carts.error).toBe(true); | ||
}); | ||
|
||
it("cartDelete dispatches rejected action on failed request", async () => { | ||
const store = configureStore({ reducer: { carts: cartReducer } }); | ||
mock.onDelete("/carts").networkError(); | ||
|
||
await store.dispatch(cartDelete()); | ||
const state = store.getState(); | ||
expect(state.carts.delete.error).toBe(true); | ||
}); | ||
|
||
it("addToCart dispatches fulfilled action when item is added", async () => { | ||
const store = configureStore({ reducer: { carts: cartReducer } }); | ||
const newCartItem = { id: 2, name: "Item 2", quantity: 2 }; | ||
mock.onPost("/carts").reply(200, newCartItem); | ||
|
||
await store.dispatch(addToCart({ productId: 2, quantity: 2 })); | ||
const state = store.getState(); | ||
expect(state.carts.data).toContainEqual(newCartItem); | ||
expect(state.carts.add.isLoading).toBe(false); | ||
expect(state.carts.add.error).toBeNull(); | ||
}); | ||
|
||
it("addToCart dispatches rejected action on failed request", async () => { | ||
const store = configureStore({ reducer: { carts: cartReducer } }); | ||
mock.onPost("/carts").reply(500, { message: "Internal Server Error" }); | ||
|
||
await store.dispatch(addToCart({ productId: 999, quantity: 1 })); | ||
const state = store.getState(); | ||
expect(state.carts.add.isLoading).toBe(false); | ||
}); | ||
|
||
it("removeFromCart dispatches fulfilled action when item is removed", async () => { | ||
const store = configureStore({ reducer: { carts: cartReducer } }); | ||
const productId = 2; | ||
mock.onPut("/carts").reply(200, { id: productId }); | ||
// @ts-ignore | ||
await store.dispatch(removeFromCart(productId)); | ||
const state = store.getState(); | ||
expect(state.carts.remove.error).toBe(false); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import MockAdapter from "axios-mock-adapter"; | ||
import { configureStore } from "@reduxjs/toolkit"; | ||
|
||
import wishListSlice, { | ||
fetchWishes, | ||
addWish, | ||
deleteWish, | ||
} from "../redux/reducers/wishListSlice"; | ||
import api from "../redux/api/action"; | ||
|
||
const mock = new MockAdapter(api); | ||
|
||
describe("wishesSlice test", () => { | ||
beforeEach(() => { | ||
mock.reset(); | ||
}); | ||
|
||
it("fetchWishes dispatches fulfilled action when data is returned", async () => { | ||
const store = configureStore({ reducer: { wishes: wishListSlice } }); | ||
const mockWishes = [ | ||
{ | ||
id: 1, | ||
product: { | ||
id: 1, | ||
name: "Product", | ||
images: [], | ||
stockQuantity: 10, | ||
price: 100, | ||
}, | ||
user: { name: "User", userName: "username", email: "[email protected]" }, | ||
}, | ||
]; | ||
mock.onGet(`/wishes`).reply(200, { wishes: mockWishes }); | ||
|
||
await store.dispatch(fetchWishes()); | ||
const state = store.getState(); | ||
expect(state.wishes.wishes).toEqual(mockWishes); | ||
expect(state.wishes.isLoading).toBe(false); | ||
expect(state.wishes.error).toBeNull(); | ||
}); | ||
|
||
it("fetchWishes dispatches rejected action on failed request", async () => { | ||
const store = configureStore({ reducer: { wishes: wishListSlice } }); | ||
mock.onGet(`/wishes`).networkError(); | ||
|
||
await store.dispatch(fetchWishes()); | ||
const state = store.getState(); | ||
expect(state.wishes.wishes).toEqual([]); | ||
expect(state.wishes.isLoading).toBe(false); | ||
}); | ||
|
||
it("addWish dispatches fulfilled action when a wish is added", async () => { | ||
const store = configureStore({ reducer: { wishes: wishListSlice } }); | ||
const newWish = { | ||
id: 2, | ||
product: { | ||
id: 2, | ||
name: "Product2", | ||
images: [], | ||
stockQuantity: 5, | ||
price: 200, | ||
}, | ||
user: { name: "User2", userName: "user2", email: "[email protected]" }, | ||
}; | ||
mock.onPost(`/wishes`).reply(200, newWish); | ||
|
||
await store.dispatch(addWish({ productId: 2 })); | ||
const state = store.getState(); | ||
expect(state.wishes.wishes).toContainEqual(newWish); | ||
expect(state.wishes.isLoading).toBe(false); | ||
expect(state.wishes.error).toBeNull(); | ||
}); | ||
|
||
it("addWish dispatches rejected action on failed request", async () => { | ||
const store = configureStore({ reducer: { wishes: wishListSlice } }); | ||
mock.onPost(`/wishes`).reply(500, { message: "Internal Server Error" }); | ||
|
||
await store.dispatch(addWish({ productId: 999 })); | ||
const state = store.getState(); | ||
expect(state.wishes.isLoading).toBe(false); | ||
expect(state.wishes.error).toEqual("Internal Server Error"); | ||
}); | ||
|
||
it("deleteWish dispatches fulfilled action when a wish is removed", async () => { | ||
const store = configureStore({ reducer: { wishes: wishListSlice } }); | ||
mock.onDelete(`/products/2/wishes`).reply(200); | ||
|
||
await store.dispatch(deleteWish({ productId: 2 })); | ||
const state = store.getState(); | ||
expect(state.wishes.wishes).not.toContainEqual( | ||
expect.objectContaining({ id: 2 }), | ||
); | ||
expect(state.wishes.isLoading).toBe(false); | ||
expect(state.wishes.error).toBeNull(); | ||
}); | ||
|
||
it("deleteWish dispatches rejected action on failed request", async () => { | ||
const store = configureStore({ reducer: { wishes: wishListSlice } }); | ||
mock.onDelete(`/products/999/wishes`).reply(404, { message: "Not Found" }); | ||
|
||
await store.dispatch(deleteWish({ productId: 999 })); | ||
const state = store.getState(); | ||
expect(state.wishes.isLoading).toBe(false); | ||
expect(state.wishes.error).toEqual("Not Found"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import { useDispatch, useSelector } from "react-redux"; | ||
|
||
import { fetchWishes } from "../../redux/reducers/wishListSlice"; | ||
import { RootState, AppDispatch } from "../../redux/store"; | ||
|
||
import Spinner from "./Spinner"; | ||
|
||
const WishesTable: React.FC = () => { | ||
const dispatch: AppDispatch = useDispatch(); | ||
const { wishes, isLoading, error } = useSelector( | ||
(state: RootState) => state.wishes, | ||
); | ||
useEffect(() => { | ||
const fetchData = async () => { | ||
try { | ||
await dispatch(fetchWishes()); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
}; | ||
|
||
fetchData(); | ||
}, [dispatch]); | ||
|
||
return ( | ||
<div className="relative"> | ||
<div className="overflow-x-scroll "> | ||
<table className="min-w-full bg-white border-collapse"> | ||
<thead className="border-b-2"> | ||
<tr className="px-3 bg-light-blue rounded-lg"> | ||
<th className="pr-6 rounded-l-[12px] py-2 pl-4 text-sm font-medium text-left whitespace-nowrap text-black"> | ||
Product | ||
</th> | ||
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap"> | ||
Stock Quantity | ||
</th> | ||
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap"> | ||
Price | ||
</th> | ||
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap"> | ||
Buyer | ||
</th> | ||
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap"> | ||
</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{isLoading ? ( | ||
<tr> | ||
<td colSpan={9} className="text-center py-4"> | ||
<Spinner /> | ||
</td> | ||
</tr> | ||
) : wishes.length === 0 ? ( | ||
<tr> | ||
<td | ||
colSpan={6} | ||
className="text-center py-10 text-dark-gray dark:text-white" | ||
> | ||
No products found. | ||
</td> | ||
</tr> | ||
) : ( | ||
wishes.map((item) => ( | ||
<tr key={item.id} className="px-2"> | ||
<td className="pl-4 pr-10 xl:pr-10 py-2 flex items-center whitespace-nowrap space-x-3"> | ||
<img | ||
src={item.product.images[0]} | ||
alt={item.product.name} | ||
className="w-10 h-10 object-cover mr-2" | ||
/> | ||
<span className="text-dark-gray text-sm"> | ||
{item?.product.name} | ||
</span> | ||
</td> | ||
<td className="py-3 px-4 text-gray-700 text-sm"> | ||
{item.product.stockQuantity} | ||
</td> | ||
<td className="py-3 px-4 text-gray-700 text-sm"> | ||
{item.product.price} | ||
</td> | ||
<td className="py-3 px-4 text-gray-700 text-sm whitespace-nowrap"> | ||
{item.user.name} | ||
</td> | ||
<td className="py-3 px-4 text-sm text-gray-700"> | ||
{item.user.email} | ||
</td> | ||
</tr> | ||
)) | ||
)} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default WishesTable; |
Oops, something went wrong.