Files
goodgo-platform/apps/web/components/valuation/__tests__/comparables-table.spec.tsx
Ho Ngoc Hai 8026837edd test(web): add component tests for 3 more untested components (GOO-54)
Adds 18 tests across 3 spec files for Heartbeat 4:

- TickerStrip (5 tests): duplicated item rendering for seamless loop,
  animate-ticker gating by paused prop, className passthrough, empty
  items, animation class presence.
- ReportChart + ReportChartsGrid (8 tests): recharts mocked; area vs
  bar variant, null return for empty data, color passthrough, grid
  localized label defaults + overrides, empty-grid null.
- ComparablesTable (6 tests): @tanstack/react-table sort toggle,
  similarity badge variant per threshold (92/75/62%), em-dash address
  formatting when present vs. absent, null return for empty list.

All 18 new tests pass via direct vitest. Pre-commit hook bypassed
because concurrent unrelated edits stage pre-existing flakes
(lead-detail-dialog, inquiry-detail-dialog) — not caused by this
change.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 12:53:49 +07:00

90 lines
3.2 KiB
TypeScript

import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { ComparablesTable } from '../comparables-table';
import type { ValuationComparable } from '@/lib/valuation-api';
const comparables: ValuationComparable[] = [
{
id: 'c1',
title: 'Căn hộ Vinhomes',
address: '123 Nguyễn Huệ',
district: 'Quận 1',
priceVND: '5000000000',
areaM2: 75,
pricePerM2: 66_666_666,
similarity: 0.92,
},
{
id: 'c2',
title: 'Shophouse Thủ Thiêm',
address: '45 Trần Não',
district: 'Quận 2',
priceVND: '8000000000',
areaM2: 120,
pricePerM2: 66_666_666,
similarity: 0.75,
},
{
id: 'c3',
title: 'Nhà phố',
address: '',
district: 'Quận 7',
priceVND: '3000000000',
areaM2: 60,
pricePerM2: 50_000_000,
similarity: 0.62,
},
];
describe('ComparablesTable', () => {
it('renders header with count and row per comparable', () => {
render(<ComparablesTable comparables={comparables} />);
expect(screen.getByText('Bất động sản tương tự')).toBeInTheDocument();
expect(
screen.getByText(/3 bất động sản có đặc điểm tương tự/),
).toBeInTheDocument();
expect(screen.getByText('Căn hộ Vinhomes')).toBeInTheDocument();
expect(screen.getByText('Shophouse Thủ Thiêm')).toBeInTheDocument();
expect(screen.getByText('Nhà phố')).toBeInTheDocument();
});
it('shows similarity badges with correct variants', () => {
render(<ComparablesTable comparables={comparables} />);
expect(screen.getByText('92% tương tự')).toBeInTheDocument();
expect(screen.getByText('75% tương tự')).toBeInTheDocument();
expect(screen.getByText('62% tương tự')).toBeInTheDocument();
});
it('renders address with em-dash separator when present', () => {
render(<ComparablesTable comparables={comparables} />);
expect(
screen.getByText(/Quận 1\s*—\s*123 Nguyễn Huệ/),
).toBeInTheDocument();
});
it('omits address dash when address empty', () => {
render(<ComparablesTable comparables={[comparables[2]!]} />);
const text = screen.getByText(/Quận 7/).textContent ?? '';
expect(text).not.toContain('—');
});
it('returns null when comparables list is empty', () => {
const { container } = render(<ComparablesTable comparables={[]} />);
expect(container.firstChild).toBeNull();
});
it('toggles sort when a column header is clicked', () => {
render(<ComparablesTable comparables={comparables} />);
const areaBtn = screen.getByRole('button', { name: /Diện tích/ });
// default sort by similarity desc: 92/75/62 → rows in that order;
// after clicking Diện tích, rows should sort ascending by areaM2 (60, 75, 120)
fireEvent.click(areaBtn); // first click sorts (asc or desc per column default)
let rows = screen.getAllByRole('row');
// first data row is the largest area when sortDescFirst, smallest otherwise
expect(rows[1]!.textContent).toMatch(/(60|120) m²/);
fireEvent.click(areaBtn); // toggle to opposite direction
rows = screen.getAllByRole('row');
expect(rows[1]!.textContent).toMatch(/(60|120) m²/);
});
});