Skip to content

Commit

Permalink
fix(test-cov): increase test coverage (#132)
Browse files Browse the repository at this point in the history
- add more tests to cover uncovered lines

[#Fixes #124]
  • Loading branch information
jkarenzi authored Jul 26, 2024
1 parent 7a96292 commit 362caba
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 12 deletions.
22 changes: 22 additions & 0 deletions src/__test__/Cart/Cart.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { configureStore } from '@reduxjs/toolkit';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { Provider } from 'react-redux';
import { render, screen } from '@testing-library/react';
import cartReducer, {
fetchCartItems,
addCartItem,
updateCartItemQuantity,
removeCartItem,
} from '@/features/Cart/cartSlice';
import CartItem from '@/components/Cart/CartItem';

describe('cartSlice', () => {
let store = configureStore({ reducer: { cartItems: cartReducer } });
Expand Down Expand Up @@ -79,3 +82,22 @@ describe('cartSlice', () => {
expect(state.error).toBeNull();
});
});

describe('Cart component', () => {
const store = configureStore({
reducer: {},
});

it('renders cart item', async () => {
render(
<Provider store={store}>
<CartItem id={1} price={100} name="Test Product" quantity={3} />
</Provider>
);

expect(screen.getByText('$300')).toBeInTheDocument();
expect(screen.getByText('Test Product')).toBeInTheDocument();
expect(screen.getByText('Size')).toBeInTheDocument();
expect(screen.getByText('3')).toBeInTheDocument();
});
});
169 changes: 164 additions & 5 deletions src/__test__/productDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { Store, configureStore } from '@reduxjs/toolkit';
import { waitFor } from '@testing-library/dom';
import { render, screen } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import { Provider } from 'react-redux';
import { MemoryRouter, Route, Routes } from 'react-router';
import productsReducer, {
fetchProductDetails,
} from '@/features/Products/ProductSlice';
import signInReducer from '@/features/Auth/SignInSlice';
import signInReducer, { SignInState } from '@/features/Auth/SignInSlice';
import { AppDispatch, RootState } from '@/app/store';
import ProductDetails from '@/pages/ProductDetails';
import bestSellingReducer from '@/features/Popular/bestSellingProductSlice';
Expand All @@ -24,8 +24,22 @@ const mockProduct = {
totalQtySold: 25,
longDesc: 'This is a mock product used for testing purposes.',
shortDesc: 'This is a short description',
category: 'Electronics',
similarProducts: [],
category: {
id: 5,
name: 'Electronics',
},
similarProducts: [
{
id: 3,
name: 'Mock Similar Product',
image: '/images/mock.png',
averageRating: 0,
salesPrice: 100,
regularPrice: 200,
longDesc: 'This is a mock product used for testing purposes.',
shortDesc: 'This is a short description',
},
],
reviews: [
{
id: 1,
Expand All @@ -42,7 +56,8 @@ const mockProduct = {
gallery: [],
tags: ['testTag'],
vendor: {
name: 'Tester',
firstName: 'Tester',
lastName: 'Testing',
email: 'testervendor@gmail.com',
picture: 'https://fake.png',
},
Expand All @@ -57,6 +72,22 @@ const renderWithProviders = (
bestSellingProducts: bestSellingReducer,
signIn: signInReducer,
},
preloadedState: {
signIn: {
token: 'fake token',
user: null,
role: null,
loading: null,
error: null,
message: null,
needsVerification: false,
needs2FA: false,
vendor: {
id: null,
email: null,
},
} as unknown as SignInState,
},
}),
} = {}
) => {
Expand Down Expand Up @@ -101,6 +132,12 @@ describe('ProductDetails Page', () => {
expect(screen.getByText(/\$149.99/i)).toBeInTheDocument();
expect(screen.getByText('1')).toBeInTheDocument();
expect(screen.getByText('25')).toBeInTheDocument();
expect(screen.getByText('33% Off')).toBeInTheDocument();
expect(screen.getByText('Add to Cart')).toBeInTheDocument();
expect(screen.getByText('Add to wishlist')).toBeInTheDocument();
// expect(screen.getAllByTestId('ratingStar').length).toBe(4);
// expect(screen.getAllByTestId('halfStar').length).toBe(1);

expect(
screen.getByText(/This is a mock product used for testing purposes./i)
).toBeInTheDocument();
Expand All @@ -123,6 +160,128 @@ describe('ProductDetails Page', () => {
).toBeInTheDocument();
});
});

it('should display similar products', async () => {
mock
.onGet(`${import.meta.env.VITE_BASE_URL}/buyer/get_product/1`)
.reply(200, { product: mockProduct });

renderWithProviders(<ProductDetails />);

await waitFor(() => {
expect(screen.getByText('Mock Similar Prod...')).toBeInTheDocument();
expect(screen.getByText('$100')).toBeInTheDocument();
expect(screen.getByText('50% Off')).toBeInTheDocument();
});
});

it('should display product details, reviews, about store', async () => {
mock
.onGet(`${import.meta.env.VITE_BASE_URL}/buyer/get_product/1`)
.reply(200, { product: mockProduct });

renderWithProviders(<ProductDetails />);

await waitFor(() => {
expect(screen.getByText('Product Details')).toBeInTheDocument();
expect(
screen.getByText('This is a mock product used for testing purposes.')
).toBeInTheDocument();
expect(
screen.getByText('This is a short description')
).toBeInTheDocument();
});

fireEvent.click(screen.getByRole('button', { name: 'Reviews (1)' }));

await waitFor(() => {
expect(screen.getByText('new user')).toBeInTheDocument();
expect(screen.getByText('excellent product')).toBeInTheDocument();
});

fireEvent.click(screen.getByRole('button', { name: 'About Store' }));

await waitFor(() => {
expect(screen.getAllByText('Tester Testing')[0]).toBeInTheDocument();
expect(
screen.getAllByText('testervendor@gmail.com')[0]
).toBeInTheDocument();
});
});

it('should display error message when no reviews are found', async () => {
mock
.onGet(`${import.meta.env.VITE_BASE_URL}/buyer/get_product/1`)
.reply(200, { product: { ...mockProduct, reviews: [] } });

renderWithProviders(<ProductDetails />);

await waitFor(() => {
fireEvent.click(screen.getByRole('button', { name: 'Reviews (0)' }));
});

await waitFor(() => {
expect(screen.getByText('No reviews found')).toBeInTheDocument();
});
});

it('should submit a review successfully', async () => {
mock
.onGet(`${import.meta.env.VITE_BASE_URL}/buyer/get_product/1`)
.reply(200, { product: mockProduct });

mock
.onPost(`${import.meta.env.VITE_BASE_URL}/review`)
.reply(200, { message: 'Review submitted successfully' });

renderWithProviders(<ProductDetails />);

mock
.onGet(`${import.meta.env.VITE_BASE_URL}/buyer/get_product/1`)
.reply(200, {
product: {
...mockProduct,
reviews: [
{
id: 1,
user: {
id: 1,
firstName: 'new',
lastName: 'rating',
picture: 'http://fake.png',
},
rating: 1,
content: 'this is a bad product',
},
],
},
});

await waitFor(() => {
const star = screen.getAllByTitle('inputStar')[0];
fireEvent.click(star);
const contentTextArea = screen.getByTitle('inputContent');
fireEvent.change(contentTextArea, {
target: {
value: 'this is a bad product',
},
});
});

await waitFor(() => {
const submitBtn = screen.getByText('Submit');
fireEvent.click(submitBtn);
});

await waitFor(() => {
fireEvent.click(screen.getByRole('button', { name: 'Reviews (1)' }));
});

await waitFor(() => {
expect(screen.getByText('this is a bad product')).toBeInTheDocument();
expect(screen.getByText('new rating')).toBeInTheDocument();
});
});
});

describe('Product Details async action', () => {
Expand Down
13 changes: 13 additions & 0 deletions src/__test__/shop.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,19 @@ describe('Shop Component', () => {
expect(screen.queryByText(/Product 2/i)).not.toBeInTheDocument();
});
});

it('displays the filter section on mobile devices', async () => {
renderWithProviders(<Shop />);
const filterBtn = screen.getByTitle('filter');
fireEvent.click(filterBtn);

await waitFor(() => {
expect(screen.getAllByText('Filters')[1]).toBeInTheDocument();
expect(screen.getAllByText('Clear All')[1]).toBeInTheDocument();
expect(screen.getAllByText('Categories')[1]).toBeInTheDocument();
expect(screen.getAllByText('Rating')[1]).toBeInTheDocument();
});
});
});

describe('ProductSlice', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/salesMap/SalesMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function SalesMap() {
(label as any).html(
`<div style="background-color: white; border: 1px solid white; outline: 10px solid white; border-radius: 6px; min-height: 70px; width: 150px; color: black; padding-left: 10px;">
<p><b>${(label as any).html()}</b></p>
<p>Sales: <b>${Data ? Data[code] : 0}</b></p>
<p>Sales: <b>${code in Data ? Data[code] : 0}</b></p>
</div>`
);
}}
Expand Down
2 changes: 1 addition & 1 deletion src/features/Auth/SignInSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface User {
};
}

interface SignInState {
export interface SignInState {
token: string | null;
user: User | null;
loading: boolean;
Expand Down
27 changes: 26 additions & 1 deletion src/features/Orders/ordersSlice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import axios from 'axios';
import Order from '@/interfaces/order';
import { RootState } from '../../app/store';
import { RootState, store } from '../../app/store';

interface OrdersState {
orders: Order[];
Expand Down Expand Up @@ -35,6 +35,31 @@ export const fetchOrders = createAsyncThunk('orders/fetchOrders', async () => {
},
};
});

const { signIn } = store.getState();

if (signIn.user?.userType.name === 'Admin') {
return orders;
}
if (signIn.user?.userType.name === 'Vendor') {
const filteredOrders: Order[] = [];
/* eslint-disable no-restricted-syntax */
for (const order of orders) {
const newDetails = [];
/* eslint-disable no-restricted-syntax */
for (const orderDetail of order.orderDetails) {
if (orderDetail.product.vendor.id === signIn.user.id) {
newDetails.push(orderDetail);
}
}

if (newDetails.length > 0) {
filteredOrders.push({ ...order, orderDetails: newDetails });
}
}

return filteredOrders;
}
return orders;
});

Expand Down
2 changes: 1 addition & 1 deletion src/pages/Coupons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function Coupons() {
<IoIosSearch size={24} />
<input
id="searchInput"
placeholder="Search order..."
placeholder="Search coupons..."
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
Expand Down
2 changes: 2 additions & 0 deletions src/pages/ProductDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ function ProductDetails() {
<div className="flex items-center gap-2">
{Array.from({ length: 5 }, (_, i) => (
<FaStar
title="inputStar"
size="25"
color={
review.rating && i + 1 <= review.rating
Expand All @@ -706,6 +707,7 @@ function ProductDetails() {
Your review<span className="text-red-700"> *</span>
</h2>
<textarea
title="inputContent"
className="xs:w-full lg:w-4/5 h-40 rounded-md border-[1.5px] outline-none border-textareaBorder"
onChange={(e) =>
setReview({
Expand Down
1 change: 1 addition & 0 deletions src/pages/Shop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ function Shop() {
</div>
<div className="xs:flex lg:hidden pl-2 py-2 mt-4 items-center justify-start gap-2 rounded-lg w-full bg-grayLight">
<CiFilter
title="filter"
size={20}
onClick={() => setToggleFilterMenu(true)}
className="cursor-pointer"
Expand Down
19 changes: 16 additions & 3 deletions src/routes/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,22 @@ function AppRoutes() {
}
/>
<Route path="product-details/:id" element={<ProductDetails />} />
<Route path="wishlist" element={<Wishlist />} />
<Route path="/cart" element={<Cart />} />
<Route path="/checkout" element={<CheckoutPage />} />
<Route
path="cart"
element={
<ProtectedRoute roles={['Buyer']}>
<Cart />
</ProtectedRoute>
}
/>
<Route
path="checkout"
element={
<ProtectedRoute roles={['Buyer']}>
<CheckoutPage />
</ProtectedRoute>
}
/>
</Route>
<Route path="/signup" element={<SignUp />} />
<Route path="/signIn" element={<SignIn />} />
Expand Down
Loading

0 comments on commit 362caba

Please sign in to comment.