Files
goodgo-platform/e2e/web/analytics.spec.ts
2026-05-04 20:11:09 +07:00

91 lines
3.9 KiB
TypeScript

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 });
});
});