-
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.
ft(search):A buyer should be able to search for products
- Loading branch information
1 parent
5e6047c
commit faef270
Showing
9 changed files
with
454 additions
and
191 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,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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,121 @@ | ||
'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(''); | ||
|
||
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); | ||
} 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 ) { | ||
|
||
} else if(trimmedValue.length > 0 ) { | ||
|
||
queryParams.name = Value; | ||
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); | ||
|
||
} | ||
catch (error) { | ||
console.error('Error fetching search results:', error); | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<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"> | ||
<ProductList searchResults={searchResults} /> | ||
</div> | ||
</div> | ||
</div> | ||
<Footer /> | ||
|
||
</> | ||
); | ||
} | ||
|
||
export default Page; | ||
export default function Page() { | ||
return ( | ||
<Suspense fallback={<div>Loading...</div>}> | ||
<SuspenseWrapper /> | ||
</Suspense> | ||
); | ||
} |
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
Oops, something went wrong.