test(e2e): align web specs with current app routes
This commit is contained in:
@@ -1,193 +1,123 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
const mockListing = {
|
||||
id: 'listing-1',
|
||||
transactionType: 'SALE',
|
||||
priceVND: '5000000000',
|
||||
pricePerM2: 66666667,
|
||||
rentPriceMonthly: null,
|
||||
commissionPct: 2.5,
|
||||
status: 'ACTIVE',
|
||||
viewCount: 120,
|
||||
saveCount: 15,
|
||||
inquiryCount: 8,
|
||||
publishedAt: '2026-01-15T00:00:00Z',
|
||||
property: {
|
||||
id: 'prop-1',
|
||||
propertyType: 'APARTMENT',
|
||||
title: 'Căn hộ cao cấp Quận 1',
|
||||
description: 'Căn hộ đẹp view sông Sài Gòn, nội thất cao cấp, tiện ích đầy đủ.',
|
||||
address: '123 Nguyễn Huệ',
|
||||
ward: 'Bến Nghé',
|
||||
district: 'Quận 1',
|
||||
city: 'Hồ Chí Minh',
|
||||
latitude: 10.7769,
|
||||
longitude: 106.7009,
|
||||
areaM2: 75,
|
||||
bedrooms: 2,
|
||||
bathrooms: 2,
|
||||
floors: 1,
|
||||
direction: 'SOUTH',
|
||||
yearBuilt: 2022,
|
||||
legalStatus: 'Sổ hồng',
|
||||
projectName: 'Vinhomes Central Park',
|
||||
amenities: ['Hồ bơi', 'Gym', 'Bãi đỗ xe'],
|
||||
media: [
|
||||
{ id: 'm1', url: '/placeholder.jpg', type: 'IMAGE', order: 0 },
|
||||
{ id: 'm2', url: '/placeholder2.jpg', type: 'IMAGE', order: 1 },
|
||||
],
|
||||
},
|
||||
seller: { id: 's1', fullName: 'Nguyen Van A', phone: '0912345678' },
|
||||
agent: { id: 'a1', agency: 'GoodGo Realty', licenseNumber: 'AGT-001' },
|
||||
};
|
||||
const seededListingId = 'seed-listing-001';
|
||||
const listingPath = `/listings/${seededListingId}`;
|
||||
const listingTitle = /Căn hộ Vinhomes Central Park|Vinhomes Central Park/i;
|
||||
|
||||
test.describe('Listing Detail Page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.route('**/listings/listing-1', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(mockListing),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('renders listing title and price', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Căn hộ cao cấp Quận 1' })).toBeVisible({
|
||||
await expect(page.getByRole('heading', { name: listingTitle })).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(page.getByText(/5\.0 tỷ/)).toBeVisible();
|
||||
await expect(page.getByText('VND')).toBeVisible();
|
||||
await expect(page.getByText('8.500.000.000 đ').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays breadcrumb navigation', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByRole('link', { name: 'Trang chu' })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: 'Tim kiem' })).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.locator('#main-content').getByRole('link', { name: /Trang chủ|Trang chu/i })).toBeVisible();
|
||||
await expect(page.locator('#main-content').getByRole('link', { name: /Tìm kiếm|Tim kiem/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows property badges (transaction type and property type)', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
test('shows property badges', async ({ page }) => {
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
// Transaction type and property type badges
|
||||
const badges = page.locator('[class*="badge"]');
|
||||
await expect(badges.first()).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText(/Bán|Sale/i).first()).toBeVisible();
|
||||
await expect(page.getByText(/Căn hộ|Apartment/i).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays address information', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText(/123 Nguyễn Huệ/)).toBeVisible();
|
||||
await expect(page.getByText(/Bến Nghé/)).toBeVisible();
|
||||
await expect(page.getByText(/Quận 1/)).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText(/208 Nguyễn Hữu Cảnh/i)).toBeVisible();
|
||||
await expect(page.getByText(/Phường 22/i)).toBeVisible();
|
||||
await expect(page.getByText(/Bình Thạnh/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows quick stats bar', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('75 m²')).toBeVisible();
|
||||
await expect(page.getByText('Dien tich')).toBeVisible();
|
||||
await expect(page.getByText('Phong ngu')).toBeVisible();
|
||||
await expect(page.getByText('Phong tam')).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('108 m²').first()).toBeVisible();
|
||||
await expect(page.getByText(/Diện tích|Dien tich/i).first()).toBeVisible();
|
||||
await expect(page.getByText(/Phòng ngủ|Phong ngu/i).first()).toBeVisible();
|
||||
await expect(page.getByText(/Phòng tắm|Phong tam/i).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays description section', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('Mo ta')).toBeVisible();
|
||||
await expect(page.getByText('Căn hộ đẹp view sông Sài Gòn')).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByRole('heading', { name: /Mô tả|Mo ta/i })).toBeVisible();
|
||||
await expect(page.locator('#main-content').getByText(/Căn hộ 3 phòng ngủ tại/i).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows detailed property info grid', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('Thong tin chi tiet')).toBeVisible();
|
||||
await expect(page.getByText('Loai BDS')).toBeVisible();
|
||||
await expect(page.getByText('Sổ hồng')).toBeVisible();
|
||||
await expect(page.getByText('Vinhomes Central Park')).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByRole('heading', { name: /Thông tin chi tiết|Thong tin chi tiet/i })).toBeVisible();
|
||||
await expect(page.locator('#main-content').getByText(/Loại BĐS|Loai BDS|Loại bất động sản/i).first()).toBeVisible();
|
||||
await expect(page.getByText(/SO_HONG|Sổ hồng|So hong/i).first()).toBeVisible();
|
||||
await expect(page.getByText(/Vinhomes Central Park/i).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays amenities', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('Tien ich')).toBeVisible();
|
||||
await expect(page.getByText('Hồ bơi')).toBeVisible();
|
||||
await expect(page.getByText('Gym')).toBeVisible();
|
||||
await expect(page.getByText('Bãi đỗ xe')).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByRole('heading', { name: /Tiện ích|Tien ich/i })).toBeVisible();
|
||||
await expect(page.getByText(/hồ bơi/i)).toBeVisible();
|
||||
await expect(page.getByText(/gym/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows seller contact card', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('Lien he')).toBeVisible();
|
||||
await expect(page.getByText('Nguyen Van A')).toBeVisible();
|
||||
await expect(page.getByText('0912345678')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /Goi ngay/i })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /Nhan tin/i })).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText(/Liên hệ người đăng|Liên hệ|Lien he/i).first()).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /Gọi ngay|Goi ngay/i })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /Nhắn tin|Nhan tin/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows agent info when available', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('Moi gioi')).toBeVisible();
|
||||
await expect(page.getByText('GoodGo Realty')).toBeVisible();
|
||||
await expect(page.getByText(/2\.5%/)).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText(/Môi giới|Moi gioi|Hoa hồng|Hoa hong/i).first()).toBeVisible();
|
||||
await expect(page.getByText(/2%|2\.0%/)).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays listing statistics', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText('120')).toBeVisible(); // viewCount
|
||||
await expect(page.getByText('Luot xem')).toBeVisible();
|
||||
await expect(page.getByText('Luot luu')).toBeVisible();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByText(/Lượt xem|Luot xem/i)).toBeVisible();
|
||||
await expect(page.getByText(/Lượt lưu|Luot luu/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows error state for non-existent listing', async ({ page }) => {
|
||||
await page.route('**/listings/nonexistent', (route) =>
|
||||
route.fulfill({ status: 404, contentType: 'application/json', body: '{}' }),
|
||||
);
|
||||
|
||||
await page.goto('/listings/nonexistent');
|
||||
|
||||
await expect(page.getByText(/Khong/)).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.getByRole('link', { name: /Quay lai tim kiem/i })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: /Không tìm thấy trang|not found/i })).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
test('shows loading skeleton initially', async ({ page }) => {
|
||||
await page.route('**/listings/listing-1', async (route) => {
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(mockListing),
|
||||
});
|
||||
});
|
||||
test('renders page after server fetch', async ({ page }) => {
|
||||
await page.goto(listingPath);
|
||||
|
||||
await page.goto('/listings/listing-1');
|
||||
|
||||
// Skeleton elements should be visible during loading
|
||||
const skeleton = page.locator('.animate-pulse');
|
||||
await expect(skeleton.first()).toBeVisible({ timeout: 3000 });
|
||||
await expect(page.getByRole('heading', { name: listingTitle })).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
test('breadcrumb navigates to search page', async ({ page }) => {
|
||||
await page.goto('/listings/listing-1');
|
||||
await page.goto(listingPath);
|
||||
|
||||
await expect(page.getByText('Căn hộ cao cấp Quận 1').first()).toBeVisible({ timeout: 10000 });
|
||||
await page.getByRole('link', { name: 'Tim kiem' }).click();
|
||||
await expect(page.getByText(listingTitle).first()).toBeVisible({ timeout: 10000 });
|
||||
await page.locator('#main-content').getByRole('link', { name: /Tìm kiếm|Tim kiem/i }).click();
|
||||
await expect(page).toHaveURL(/\/search/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user