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 mockListings = { data: [ { id: 'l1', transactionType: 'SALE', priceVND: '5000000000', pricePerM2: 66666667, rentPriceMonthly: null, commissionPct: null, status: 'ACTIVE', viewCount: 120, saveCount: 15, inquiryCount: 8, publishedAt: '2026-01-15T00:00:00Z', property: { id: 'p1', propertyType: 'APARTMENT', title: 'Căn hộ test', description: 'Desc', address: '123 Test', ward: 'W1', district: 'Quận 1', city: 'Hồ Chí Minh', latitude: 10.77, longitude: 106.70, areaM2: 75, bedrooms: 2, bathrooms: 2, floors: 1, direction: 'SOUTH', yearBuilt: null, legalStatus: null, projectName: null, amenities: [], media: [], }, seller: { id: 's1', fullName: 'Test Seller', phone: '0912345678' }, agent: null, }, ], total: 1, page: 1, limit: 6, totalPages: 1, }; test.describe('Dashboard Page', () => { test.beforeEach(async ({ page, context, baseURL }) => { await mockAuthenticatedUser(page, context, baseURL, { role: 'AGENT' }); // Mock all API calls 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/listings**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockListings) }), ); }); test('renders dashboard with title and post button', async ({ page }) => { await page.goto('/dashboard'); await expect(page.getByRole('heading', { name: 'Bảng điều khiển' })).toBeVisible(); await expect(page.getByText('Tổng quan thị trường và tin đăng của bạn')).toBeVisible(); await expect(page.getByRole('link', { name: /Đăng tin mới/i })).toBeVisible(); }); test('displays stat cards', async ({ page }) => { await page.goto('/dashboard'); const main = page.getByRole('main'); await expect(main.getByText('Tin đăng của tôi', { exact: true })).toBeVisible({ timeout: 10000 }); await expect(main.getByText('Lượt xem', { exact: true })).toBeVisible(); await expect(main.getByText('Liên hệ', { exact: true })).toBeVisible(); await expect(main.getByText('Giá TB thị trường', { exact: true })).toBeVisible(); }); test('shows market summary card', async ({ page }) => { await page.goto('/dashboard'); await expect(page.getByText('Tin đăng của tôi')).toBeVisible({ timeout: 10000 }); await expect(page.getByText('Tổng tin đăng')).toBeVisible(); await expect(page.getByText('Giá TB/m²')).toBeVisible(); await expect(page.getByText('Ngày TB để bán')).toBeVisible(); await expect(page.getByText('Số quận')).toBeVisible(); }); test('shows recent listings section', async ({ page }) => { await page.goto('/dashboard'); await expect(page.getByText('Tin đăng gần đây')).toBeVisible({ timeout: 10000 }); await expect(page.getByText('Căn hộ test')).toBeVisible(); }); test('navigates to create listing page', async ({ page }) => { await page.goto('/dashboard'); await expect(page.getByRole('heading', { name: 'Bảng điều khiển' })).toBeVisible(); await page.getByRole('link', { name: /Đăng tin mới/i }).click(); await expect(page).toHaveURL(/\/my-listings\/new/); }); test('navigates to analytics page', async ({ page }) => { await page.goto('/dashboard'); await expect(page.getByText('Xem phân tích chi tiết')).toBeVisible({ timeout: 10000 }); await page.getByText('Xem phân tích chi tiết').click(); await expect(page).toHaveURL(/\/analytics/); }); test('handles API failures gracefully', async ({ page }) => { await page.route('**/api/v1/analytics/market-report**', (route) => route.fulfill({ status: 500, body: 'Error' }), ); await page.route('**/api/v1/analytics/heatmap**', (route) => route.fulfill({ status: 500, body: 'Error' }), ); await page.route('**/api/v1/listings**', (route) => route.fulfill({ status: 500, body: 'Error' }), ); await page.goto('/dashboard'); // Page should still render (with fallback states) await expect(page.getByRole('heading', { name: 'Bảng điều khiển' })).toBeVisible(); }); });