-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bg-fix(search):A buyer should be able to search for products
- Loading branch information
1 parent
f6cfec4
commit 424a97b
Showing
14 changed files
with
526 additions
and
196 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,78 @@ | ||
import React from 'react'; | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
import Page from '@/app/products/page'; | ||
import * as nextNavigation from 'next/navigation'; | ||
import request from '@/utils/axios'; | ||
|
||
// Mock dependencies | ||
jest.mock('next/navigation', () => ({ | ||
useSearchParams: jest.fn(), | ||
})); | ||
|
||
jest.mock('@/utils/axios', () => ({ | ||
get: jest.fn(), | ||
})); | ||
|
||
jest.mock('@/components/Side', () => () => <div>Side Component</div>); | ||
jest.mock('@/components/Header', () => () => <div>Header Component</div>); | ||
jest.mock('@/components/Footer', () => () => <div>Footer Component</div>); | ||
jest.mock('@/components/ProductList', () => ({ searchResults }: { searchResults: any[] }) => ( | ||
<div>Product List with {searchResults.length} results</div> | ||
)); | ||
|
||
describe('Page Component', () => { | ||
it('renders without crashing and fetches search results', async () => { | ||
const mockSearchParams = { | ||
toString: jest.fn().mockReturnValue(''), | ||
}; | ||
const mockResponse = [{ id: 1, name: 'Product 1' }, { id: 2, name: 'Product 2' }]; | ||
|
||
(nextNavigation.useSearchParams as jest.Mock).mockReturnValue(mockSearchParams); | ||
(request.get as jest.Mock).mockResolvedValue(mockResponse); | ||
|
||
render(<Page />); | ||
|
||
// Wait for search results to be fetched and rendered | ||
await waitFor(() => { | ||
expect(screen.getByText('Header Component')).toBeInTheDocument(); | ||
expect(screen.getByText('Footer Component')).toBeInTheDocument(); | ||
expect(screen.getByText('Product List with 2 results')).toBeInTheDocument(); | ||
expect(screen.getByText('Side Component')).toBeInTheDocument(); | ||
}); | ||
|
||
// Verify that the select options are rendered | ||
expect(screen.getByRole('combobox')).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Popular' })).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Recent' })).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Clothes' })).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Electronics' })).toBeInTheDocument(); | ||
}); | ||
|
||
it('handles error in fetching search results', async () => { | ||
const mockSearchParams = { | ||
toString: jest.fn().mockReturnValue(''), | ||
}; | ||
const mockError = new Error('Network Error'); | ||
|
||
(nextNavigation.useSearchParams as jest.Mock).mockReturnValue(mockSearchParams); | ||
(request.get as jest.Mock).mockRejectedValue(mockError); | ||
|
||
render(<Page />); | ||
|
||
// Wait for error handling | ||
await waitFor(() => { | ||
expect(screen.getByText('Header Component')).toBeInTheDocument(); | ||
expect(screen.getByText('Footer Component')).toBeInTheDocument(); | ||
expect(screen.getByText('Product List with 0 results')).toBeInTheDocument(); | ||
expect(screen.getByText('Side Component')).toBeInTheDocument(); | ||
}); | ||
|
||
// Verify that the select options are rendered | ||
expect(screen.getByRole('combobox')).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Popular' })).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Recent' })).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Clothes' })).toBeInTheDocument(); | ||
expect(screen.getByRole('option', { name: 'Electronics' })).toBeInTheDocument(); | ||
}); | ||
}); |
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,50 @@ | ||
import { renderHook } from '@testing-library/react'; | ||
import axios from 'axios'; | ||
import MockAdapter from 'axios-mock-adapter'; | ||
import { useDispatch } from 'react-redux'; | ||
import { useQueryClient } from '@tanstack/react-query'; | ||
import { handleFetchUserCart, handleCartInfoManipulation } from '@/hooks/userCart'; | ||
import { ProductType } from '@/types/Product'; | ||
|
||
// Mocking useDispatch and useQueryClient | ||
jest.mock('react-redux', () => ({ | ||
useDispatch: jest.fn(), | ||
})); | ||
|
||
jest.mock('@tanstack/react-query', () => ({ | ||
useQueryClient: jest.fn(), | ||
useQuery: jest.fn(), | ||
})); | ||
|
||
|
||
describe('handleCartInfoManipulation', () => { | ||
it('manipulates cart information correctly', () => { | ||
const mockCart: any = { | ||
product: [{ product: '1', quantity: 2 }], | ||
}; | ||
const mockProducts: ProductType[] = [ | ||
{ | ||
id: '1', productName: 'Product 1', productPrice: 10, productDescription: 'Description 1', productThumbnail: 'thumb1.jpg', | ||
productCurrency: '', | ||
productPictures: [], | ||
reviews: [], | ||
related: undefined | ||
}, | ||
{ | ||
id: '2', productName: 'Product 2', productPrice: 20, productDescription: 'Description 2', productThumbnail: 'thumb2.jpg', | ||
productCurrency: '', | ||
productPictures: [], | ||
reviews: [], | ||
related: undefined | ||
}, | ||
]; | ||
|
||
const manipulatedCart = handleCartInfoManipulation(mockCart, mockProducts); | ||
|
||
expect(manipulatedCart).toHaveLength(1); | ||
expect(manipulatedCart[0].name).toEqual('Product 1'); | ||
expect(manipulatedCart[0].unitPrice).toEqual(10); | ||
expect(manipulatedCart[0].description).toEqual('Description 1'); | ||
expect(manipulatedCart[0].thumbnail).toEqual('thumb1.jpg'); | ||
}); | ||
}); |
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 |
---|---|---|
|
@@ -230,4 +230,4 @@ function Page() { | |
</div> | ||
); | ||
} | ||
export default Page; | ||
export default Page; |
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 |
---|---|---|
@@ -1,46 +1,118 @@ | ||
'use client'; | ||
import React from 'react'; | ||
'use client' | ||
import React, { useState, useEffect, Suspense } from 'react'; | ||
import { AiOutlineSearch } from 'react-icons/ai'; | ||
import Side from '../../components/Side'; | ||
import Header from '@/components/Header'; | ||
import Footer from '@/components/Footer'; | ||
import ProductList from '@/components/ProductList'; | ||
import { checkIsAdmin } from '@/components/isAdmin'; | ||
import { useSearchParams } from 'next/navigation'; | ||
import request from '@/utils/axios'; | ||
|
||
function SuspenseWrapper() { | ||
const searchParams = useSearchParams(); | ||
const [Value, setValue] = useState(''); | ||
const [searchResults, setSearchResults] = useState([]); | ||
const [searchQuery, setSearchQuery] = useState(''); | ||
const [searched, setSearched] = useState(false); // New state for tracking if a search has been performed | ||
|
||
function Page() { | ||
// const isAdmin = checkIsAdmin(); | ||
|
||
// if (isAdmin) { | ||
// console.log('User is an admin!'); | ||
// } else { | ||
// console.log('User is not an admin.'); | ||
// } | ||
useEffect(() => { | ||
const fetchSearchResults = async () => { | ||
const queryParams = new URLSearchParams(searchParams.toString()); | ||
if (searchQuery) { | ||
queryParams.set('query', searchQuery); | ||
} | ||
try { | ||
const response: any = await request.get(`/search?${queryParams.toString()}`); | ||
setSearchResults(response); | ||
setSearched(true); | ||
} catch (error) { | ||
console.error('Error fetching search results:', error); | ||
} | ||
}; | ||
|
||
fetchSearchResults(); | ||
}, [searchParams, searchQuery]); | ||
|
||
const handleSearch = async (e: { preventDefault: () => void }) => { | ||
e.preventDefault(); | ||
console.log("Search query:", Value); | ||
|
||
try { | ||
let queryParams: any = {}; | ||
const trimmedValue = Value.trim(); | ||
const numericValue = parseFloat(trimmedValue); | ||
|
||
if (!isNaN(numericValue)) { | ||
queryParams.minPrice = numericValue; | ||
} else if (!isNaN(numericValue)) { | ||
queryParams.maxPrice = numericValue; | ||
} else if (!isNaN(numericValue)) { | ||
queryParams.minPrice = numericValue; | ||
queryParams.maxPrice = numericValue; | ||
} else if (trimmedValue.length > 0) { | ||
queryParams.name = Value; | ||
} else if (trimmedValue.length > 0) { | ||
queryParams.category = Value; | ||
} | ||
|
||
const queryString = new URLSearchParams(queryParams).toString(); | ||
console.log(queryString); | ||
const url = `/search?${new URLSearchParams(queryParams).toString()}`; | ||
const response: any = await request.get(url); | ||
|
||
setSearchResults(response); | ||
setSearched(true); | ||
} catch (error) { | ||
console.error('Error fetching search results:', error); | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<Header/> | ||
|
||
<Header /> | ||
<div className="w-full h-[50%] flex flex-col px-10 py-5"> | ||
<div className="w-full flex justify-between px-10"> | ||
<h1>All Products</h1> | ||
<div className="flex self-end"> | ||
<select name="" id="" className="border"> | ||
<option value="volvo">Popular</option> | ||
<option value="saab">Recent</option> | ||
<option value="opel">Clothes</option> | ||
<option value="audi">Electronics</option> | ||
<form onSubmit={handleSearch} className="flex items-center"> | ||
<div className="relative flex items-center border px-2 py-1"> | ||
<input type="text" name="search" placeholder="Search products" className="border-none outline-none" | ||
value={Value} | ||
onChange={(e) => setValue(e.target.value)} | ||
/> | ||
<button type="submit" className="flex items-center justify-center bg-transparent text-[#8F8F8F] absolute right-2"> | ||
<AiOutlineSearch size={24} /> | ||
</button> | ||
</div> | ||
</form> | ||
<select name="" id="" className="border ml-2"> | ||
<option value="popular">Popular</option> | ||
<option value="recent">Recent</option> | ||
<option value="clothes">Clothes</option> | ||
<option value="electronics">Electronics</option> | ||
</select> | ||
</div> | ||
</div> | ||
<div className="w-full flex mx-auto px-10"> | ||
<Side /> | ||
<ProductList /> | ||
<div className="flex flex-wrap"> | ||
{searched && searchResults.length === 0 ? ( | ||
<p>No product found</p> | ||
) : ( | ||
<ProductList searchResults={searchResults} /> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
<Footer /> | ||
|
||
</> | ||
); | ||
} | ||
|
||
export default Page; | ||
export default function Page() { | ||
return ( | ||
<Suspense fallback={<div>Loading...</div>}> | ||
<SuspenseWrapper /> | ||
</Suspense> | ||
); | ||
} |
Oops, something went wrong.