test(e2e): align web specs with current app routes

This commit is contained in:
Ho Ngoc Hai
2026-05-04 20:11:09 +07:00
parent f112045826
commit 39156fc107
21 changed files with 334 additions and 458 deletions

View File

@@ -1,4 +1,5 @@
import { test, expect } from '@playwright/test';
import { mockAuthenticatedUser } from './support/auth';
/**
* E2E coverage for the listing inquiry modal (TEC-2751 / TEC-2738.10).
@@ -9,71 +10,16 @@ import { test, expect } from '@playwright/test';
* profile fetch (to make the user look authenticated) and the inquiry POST.
*/
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.',
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'],
media: [{ id: 'm1', url: '/placeholder.jpg', type: 'IMAGE', order: 0 }],
},
seller: { id: 's1', fullName: 'Nguyen Van A', phone: '0912345678' },
agent: { id: 'a1', agency: 'GoodGo Realty', licenseNumber: 'AGT-001' },
};
const mockProfile = {
id: 'user-1',
email: 'buyer@example.com',
fullName: 'Buyer Test',
phone: '0911222333',
role: 'USER',
};
const seededListingId = 'seed-listing-001';
const seededListingTitle = /Căn hộ Vinhomes Central Park|Vinhomes Central Park/i;
test.describe('Listing inquiry modal', () => {
test.beforeEach(async ({ page }) => {
await page.route('**/listings/listing-1', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(mockListing),
}),
);
});
test('opens the inquiry modal when clicking "Nhắn tin"', async ({ page }) => {
await page.goto('/listings/listing-1');
await page.goto(`/listings/${seededListingId}`);
await expect(
page.getByRole('heading', { name: 'Căn hộ cao cấp Quận 1' }),
).toBeVisible({ timeout: 10000 });
await expect(page.getByRole('heading', { name: seededListingTitle })).toBeVisible({ timeout: 10000 });
await page.getByRole('button', { name: /Nhan tin/i }).click();
await page.getByRole('button', { name: /Nhắn tin|Nhan tin/i }).click();
await expect(
page.getByRole('heading', { name: /Nhắn tin cho người bán/ }),
@@ -82,13 +28,17 @@ test.describe('Listing inquiry modal', () => {
await expect(page.getByLabel(/Số điện thoại/)).toBeVisible();
});
test('shows validation errors when fields are missing or invalid', async ({ page }) => {
await page.goto('/listings/listing-1');
await page.getByRole('button', { name: /Nhan tin/i }).click();
test('shows validation errors when fields are missing or invalid', async ({ page, context, baseURL }) => {
await mockAuthenticatedUser(page, context, baseURL, { role: 'BUYER' });
// Submit empty form — zod should flag both fields.
await page.goto(`/listings/${seededListingId}`);
await page.getByRole('button', { name: /Nhắn tin|Nhan tin/i }).click();
// Native required validation keeps the modal open before zod validation runs.
await page.getByRole('button', { name: 'Gửi tin nhắn' }).click();
await expect(page.getByText('Vui lòng nhập nội dung tin nhắn')).toBeVisible();
await expect(
page.getByRole('heading', { name: /Nhắn tin cho người bán/ }),
).toBeVisible();
// Provide message but an obviously-invalid phone.
await page.getByLabel(/Nội dung tin nhắn/).fill('Tôi quan tâm tin đăng này.');
@@ -102,27 +52,12 @@ test.describe('Listing inquiry modal', () => {
test('submits the inquiry and calls POST /api/v1/inquiries (201)', async ({
page,
context,
baseURL,
}) => {
// Mark the user as authenticated for the client-side check in auth-store.
await context.addCookies([
{
name: 'goodgo_authenticated',
value: '1',
url: 'http://localhost:3000',
},
]);
// Stub the profile load so useAuthStore.isAuthenticated flips to true.
await page.route('**/auth/me', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(mockProfile),
}),
);
await mockAuthenticatedUser(page, context, baseURL, { role: 'BUYER' });
let inquiryRequestBody: Record<string, unknown> | null = null;
await page.route('**/inquiries', async (route) => {
await page.route('**/api/v1/inquiries', async (route) => {
if (route.request().method() !== 'POST') {
return route.fallback();
}
@@ -132,11 +67,11 @@ test.describe('Listing inquiry modal', () => {
contentType: 'application/json',
body: JSON.stringify({
id: 'inq-1',
listingId: 'listing-1',
listingTitle: mockListing.property.title,
userId: mockProfile.id,
userName: mockProfile.fullName,
userPhone: mockProfile.phone,
listingId: seededListingId,
listingTitle: 'Căn hộ Vinhomes Central Park 3PN view sông Sài Gòn',
userId: 'e2e-buyer-user',
userName: 'E2E BUYER',
userPhone: '+84900000002',
message: 'Tôi quan tâm tin đăng này.',
phone: '0911222333',
isRead: false,
@@ -145,8 +80,8 @@ test.describe('Listing inquiry modal', () => {
});
});
await page.goto('/listings/listing-1');
await page.getByRole('button', { name: /Nhan tin/i }).click();
await page.goto(`/listings/${seededListingId}`);
await page.getByRole('button', { name: /Nhắn tin|Nhan tin/i }).click();
await page.getByLabel(/Nội dung tin nhắn/).fill('Tôi quan tâm tin đăng này.');
// Phone pre-fills from the mocked profile; overwrite to ensure stability.
@@ -160,7 +95,7 @@ test.describe('Listing inquiry modal', () => {
]);
expect(request.postDataJSON()).toMatchObject({
listingId: 'listing-1',
listingId: seededListingId,
message: 'Tôi quan tâm tin đăng này.',
phone: '0911222333',
});
@@ -177,8 +112,8 @@ test.describe('Listing inquiry modal', () => {
});
test('redirects anonymous users to /login on submit', async ({ page }) => {
await page.goto('/listings/listing-1');
await page.getByRole('button', { name: /Nhan tin/i }).click();
await page.goto(`/listings/${seededListingId}`);
await page.getByRole('button', { name: /Nhắn tin|Nhan tin/i }).click();
await page.getByLabel(/Nội dung tin nhắn/).fill('Tôi quan tâm tin đăng này.');
await page.getByLabel(/Số điện thoại/).fill('0911222333');