import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { describe, expect, it, vi } from 'vitest'; import type { ListingDetail, PaginatedResult } from '@/lib/listings-api'; import { SearchResults } from '../search-results'; // Mock PropertyCard vi.mock('../property-card', () => ({ PropertyCard: ({ listing }: { listing: ListingDetail }) => (
{listing.property.title}
), })); function makeListing(id: string): ListingDetail { return { id, status: 'ACTIVE', transactionType: 'SALE', priceVND: '3500000000', pricePerM2: 40_000_000, rentPriceMonthly: null, commissionPct: null, viewCount: 100, saveCount: 10, inquiryCount: 5, publishedAt: '2026-01-01T00:00:00Z', createdAt: '2025-12-01T00:00:00Z', property: { id: `prop-${id}`, propertyType: 'APARTMENT', title: `Listing ${id}`, description: 'Test listing', address: '123 Test St', ward: 'Ward', district: 'District', city: 'HCMC', areaM2: 75, bedrooms: 2, bathrooms: 2, floors: null, direction: null, yearBuilt: null, legalStatus: null, amenities: [], projectName: null, latitude: null, longitude: null, media: [], }, seller: { id: 's1', fullName: 'Seller', phone: '0901234567' }, agent: null, }; } function makeResult(count: number, page = 1, totalPages = 1): PaginatedResult { return { data: Array.from({ length: count }, (_, i) => makeListing(`${i + 1}`)), total: count, page, limit: 10, totalPages, }; } const defaultProps = { page: 1, sort: '', onPageChange: vi.fn(), onSortChange: vi.fn(), }; describe('SearchResults', () => { it('renders loading spinner when loading', () => { const { container } = render( , ); expect(container.querySelector('.animate-spin')).toBeInTheDocument(); }); it('renders error state', () => { render( , ); expect(screen.getByText('Không thể tải kết quả tìm kiếm')).toBeInTheDocument(); }); it('renders retry button in error state', () => { const onRetry = vi.fn(); render( , ); expect(screen.getByText('Thử lại')).toBeInTheDocument(); }); it('calls onRetry when retry button clicked', async () => { const user = userEvent.setup(); const onRetry = vi.fn(); render( , ); await user.click(screen.getByText('Thử lại')); expect(onRetry).toHaveBeenCalled(); }); it('renders empty state when no results', () => { render( , ); expect(screen.getByText('Không tìm thấy kết quả')).toBeInTheDocument(); }); it('renders empty state with null result', () => { render( , ); expect(screen.getByText('Không tìm thấy kết quả')).toBeInTheDocument(); }); it('renders property cards for results', () => { render( , ); expect(screen.getByTestId('property-card-1')).toBeInTheDocument(); expect(screen.getByTestId('property-card-2')).toBeInTheDocument(); expect(screen.getByTestId('property-card-3')).toBeInTheDocument(); }); it('renders total results count', () => { render( , ); expect(screen.getByText('3 kết quả')).toBeInTheDocument(); }); it('renders sort select', () => { render( , ); expect(screen.getByText('Mới nhất')).toBeInTheDocument(); expect(screen.getByText('Giá: Thấp đến cao')).toBeInTheDocument(); }); it('renders pagination buttons for multi-page results', () => { render( , ); expect(screen.getByText('Trước')).toBeInTheDocument(); expect(screen.getByText('Tiếp')).toBeInTheDocument(); }); it('disables Trước button on first page', () => { render( , ); expect(screen.getByText('Trước')).toBeDisabled(); }); it('disables Tiếp button on last page', () => { render( , ); expect(screen.getByText('Tiếp')).toBeDisabled(); }); it('calls onPageChange when Tiếp clicked', async () => { const user = userEvent.setup(); const onPageChange = vi.fn(); render( , ); await user.click(screen.getByText('Tiếp')); expect(onPageChange).toHaveBeenCalledWith(2); }); it('does not render pagination for single page', () => { render( , ); expect(screen.queryByText('Trước')).not.toBeInTheDocument(); }); });