import { test, expect } from '@playwright/test'; import { mockAuthenticatedUser } from './support/auth'; /** * E2E coverage for the listing inquiry modal (TEC-2751 / TEC-2738.10). * * The backend route is `POST /api/v1/inquiries` and is guarded by JwtAuthGuard. * The web app talks to it via `apiClient.post('/inquiries', ...)`, so the * request URL contains `/inquiries` — we intercept it and stub both the * profile fetch (to make the user look authenticated) and the inquiry POST. */ const seededListingId = 'seed-listing-001'; const seededListingTitle = /Căn hộ Vinhomes Central Park|Vinhomes Central Park/i; test.describe('Listing inquiry modal', () => { test('opens the inquiry modal when clicking "Nhắn tin"', async ({ page }) => { await page.goto(`/listings/${seededListingId}`); await expect(page.getByRole('heading', { name: seededListingTitle })).toBeVisible({ timeout: 10000 }); 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/ }), ).toBeVisible(); await expect(page.getByLabel(/Nội dung tin nhắn/)).toBeVisible(); await expect(page.getByLabel(/Số điện thoại/)).toBeVisible(); }); test('shows validation errors when fields are missing or invalid', async ({ page, context, baseURL }) => { await mockAuthenticatedUser(page, context, baseURL, { role: 'BUYER' }); 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.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.'); await page.getByLabel(/Số điện thoại/).fill('123'); await page.getByRole('button', { name: 'Gửi tin nhắn' }).click(); await expect( page.getByText(/Vui lòng nhập số điện thoại hợp lệ|Số điện thoại không hợp lệ/), ).toBeVisible(); }); test('submits the inquiry and calls POST /api/v1/inquiries (201)', async ({ page, context, baseURL, }) => { await mockAuthenticatedUser(page, context, baseURL, { role: 'BUYER' }); let inquiryRequestBody: Record | null = null; await page.route('**/api/v1/inquiries', async (route) => { if (route.request().method() !== 'POST') { return route.fallback(); } inquiryRequestBody = route.request().postDataJSON() as Record; await route.fulfill({ status: 201, contentType: 'application/json', body: JSON.stringify({ id: 'inq-1', 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, createdAt: new Date().toISOString(), }), }); }); 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. await page.getByLabel(/Số điện thoại/).fill('0911222333'); const [request] = await Promise.all([ page.waitForRequest( (req) => req.url().includes('/inquiries') && req.method() === 'POST', ), page.getByRole('button', { name: 'Gửi tin nhắn' }).click(), ]); expect(request.postDataJSON()).toMatchObject({ listingId: seededListingId, message: 'Tôi quan tâm tin đăng này.', phone: '0911222333', }); // Modal should close after success. await expect( page.getByRole('heading', { name: /Nhắn tin cho người bán/ }), ).toBeHidden(); // Sonner success toast appears. await expect(page.getByText('Đã gửi thành công!')).toBeVisible(); expect(inquiryRequestBody).not.toBeNull(); }); test('redirects anonymous users to /login on submit', async ({ page }) => { 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'); await Promise.all([ page.waitForURL(/\/login/), page.getByRole('button', { name: 'Gửi tin nhắn' }).click(), ]); await expect(page).toHaveURL(/\/login/); }); });