import { test, expect } from '@playwright/test'; const mockFlaggedList = { items: [ { listingId: 'l-flagged-1', propertyTitle: 'Căn hộ bị báo cáo', sellerName: 'Nguyen Van A', status: 'ACTIVE', totalReports: 5, reasons: ['SCAM', 'DUPLICATE'], latestReportAt: '2026-04-20T03:00:00.000Z', }, { listingId: 'l-flagged-2', propertyTitle: 'Nhà phố đáng ngờ', sellerName: 'Tran Thi B', status: 'ACTIVE', totalReports: 3, reasons: ['WRONG_INFO'], latestReportAt: '2026-04-21T03:00:00.000Z', }, ], total: 2, page: 1, limit: 20, }; const mockFlaggedDetail = { listing: { id: 'l-flagged-1', title: 'Căn hộ bị báo cáo', status: 'ACTIVE', priceVND: 3500000000, propertyType: 'APARTMENT', transactionType: 'SALE', photos: ['https://example.test/photo-1.jpg'], createdAt: '2026-04-15T03:00:00.000Z', seller: { id: 's1', fullName: 'Nguyen Van A', email: 'a@example.com', phone: '0900000001' }, }, flags: [ { id: 'f1', reason: 'SCAM', description: 'Giá rẻ bất thường', status: 'PENDING', reporter: { id: 'r1', fullName: 'Le Van C' }, createdAt: '2026-04-20T03:00:00.000Z', }, ], totalReports: 1, distinctReasons: ['SCAM'], }; test.describe('Admin flagged listings dashboard', () => { test.beforeEach(async ({ page }) => { await page.route('**/admin/listings/flagged**', (route) => { if (route.request().method() === 'GET') { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockFlaggedList), }); } return route.continue(); }); await page.route('**/admin/listings/*/flags', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(mockFlaggedDetail), }), ); await page.route('**/admin/listings/moderate', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ processed: 1, skipped: 0 }), }), ); }); test('list: filter + bulk dismiss happy path', async ({ page }) => { await page.goto('/admin/moderation/flagged'); await expect(page.getByText('Căn hộ bị báo cáo')).toBeVisible({ timeout: 10000 }); await expect(page.getByText('Nhà phố đáng ngờ')).toBeVisible(); await page.getByLabel('Lọc theo lý do').selectOption('SCAM'); await expect(page).toHaveURL(/reason=SCAM/); await page.getByLabel('Chọn Căn hộ bị báo cáo').check(); await page.getByRole('button', { name: 'Bỏ qua báo cáo' }).first().click(); await expect(page.getByRole('dialog')).toBeVisible(); await page.getByRole('button', { name: /^Bỏ qua \d+ tin$/ }).click(); await expect(page.getByRole('dialog')).toBeHidden(); }); test('detail: renders reporters and runs suspend action', async ({ page }) => { await page.goto('/admin/moderation/flagged/l-flagged-1'); await expect(page.getByRole('heading', { name: 'Căn hộ bị báo cáo' })).toBeVisible({ timeout: 10000 }); await expect(page.getByText('Le Van C')).toBeVisible(); await expect(page.getByText('Giá rẻ bất thường')).toBeVisible(); await page.getByRole('button', { name: 'Tạm ngưng' }).click(); await expect(page.getByRole('dialog')).toBeVisible(); await page.getByLabel('Ghi chú kiểm duyệt').fill('Có dấu hiệu lừa đảo'); await page.getByRole('button', { name: 'Tạm ngưng', exact: true }).last().click(); await expect(page.getByRole('dialog')).toBeHidden(); }); test('empty state when no flagged listings', async ({ page }) => { await page.route('**/admin/listings/flagged**', (route) => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ items: [], total: 0, page: 1, limit: 20 }), }), ); await page.goto('/admin/moderation/flagged'); await expect(page.getByText('Không có tin đăng nào bị báo cáo.')).toBeVisible({ timeout: 10000 }); }); });