import { test, expect } from '@playwright/test'; import { mockAuthenticatedUser } from './support/auth'; const mockMarketReport = { districts: [ { district: 'Quan 1', propertyType: 'APARTMENT', avgPriceM2: 85000000, medianPriceM2: 80000000, totalListings: 150, daysOnMarket: 45, yoyChange: 5.2 }, { district: 'Quan 7', propertyType: 'HOUSE', avgPriceM2: 65000000, medianPriceM2: 60000000, totalListings: 200, daysOnMarket: 60, yoyChange: -2.1 }, ], }; const mockHeatmap = { dataPoints: [ { district: 'Quan 1', avgPriceM2: 85000000, totalListings: 150, lat: 10.7769, lng: 106.7009 }, { district: 'Quan 7', avgPriceM2: 65000000, totalListings: 200, lat: 10.7385, lng: 106.7218 }, ], }; const mockDistrictStats = { districts: [ { district: 'Quan 1', propertyType: 'APARTMENT', medianPrice: 5000000000, pricePerM2: 85000000, totalListings: 150, daysOnMarket: 45, yoyChange: 5.2 }, ], }; const mockTrends = { dataPoints: [ { period: '2025-Q1', avgPriceM2: 78000000, totalListings: 130, transactionVolume: 80 }, { period: '2025-Q2', avgPriceM2: 80000000, totalListings: 140, transactionVolume: 85 }, { period: '2026-Q1', avgPriceM2: 85000000, totalListings: 150, transactionVolume: 95 }, ], }; test.describe('Analytics Page', () => { test.beforeEach(async ({ page, context, baseURL }) => { await mockAuthenticatedUser(page, context, baseURL, { role: 'AGENT' }); await page.route('**/api/v1/analytics/market-report**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockMarketReport) }), ); await page.route('**/api/v1/analytics/heatmap**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockHeatmap) }), ); await page.route('**/api/v1/analytics/district-stats**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockDistrictStats) }), ); await page.route('**/api/v1/analytics/price-trend**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockTrends) }), ); }); test('renders analytics page with city selector', async ({ page }) => { await page.goto('/analytics'); // City selector buttons should be visible await expect(page.getByRole('button', { name: /Ho Chi Minh/i })).toBeVisible({ timeout: 10000 }); await expect(page.getByRole('button', { name: /Ha Noi/i })).toBeVisible(); await expect(page.getByRole('button', { name: /Da Nang/i })).toBeVisible(); }); test('displays tabs for different views', async ({ page }) => { await page.goto('/analytics'); await expect(page.getByRole('tab', { name: /Tổng quan/i })).toBeVisible({ timeout: 10000 }); await expect(page.getByRole('tab', { name: /Xu hướng giá/i })).toBeVisible(); }); test('switches city when selector clicked', async ({ page }) => { await page.goto('/analytics'); await expect(page.getByRole('button', { name: /Ha Noi/i })).toBeVisible({ timeout: 10000 }); await page.getByRole('button', { name: /Ha Noi/i }).click(); // The Ha Noi button should now appear selected/active // Page should re-fetch data for the new city await expect(page.getByRole('button', { name: /Ha Noi/i })).toBeVisible(); }); test('handles empty data gracefully', async ({ page }) => { await page.route('**/api/v1/analytics/market-report**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ districts: [] }) }), ); await page.route('**/api/v1/analytics/heatmap**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ dataPoints: [] }) }), ); await page.goto('/analytics'); // Page should still render without crashing await expect(page.getByRole('button', { name: /Ho Chi Minh/i })).toBeVisible({ timeout: 10000 }); }); });