admin/e2e/admin/chat-oversight.spec.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

147 lines
4.7 KiB
TypeScript

import { expect, test } from '@playwright/test'
import { authenticateAs } from '@/e2e/fixtures/session'
const conversationId = 'oversight-conversation-1'
const apiEnvelope = (data: unknown) => ({ success: true, data })
const participants = [
{
userId: 'user-1',
mobile: '09121234567',
firstName: 'سارا',
lastName: 'احمدی',
avatarUrl: null,
gender: 'female',
status: 'active',
joinedAt: '2026-08-01T10:00:00.000Z',
},
{
userId: 'user-2',
mobile: '09351234567',
firstName: 'امیر',
lastName: 'رضایی',
avatarUrl: null,
gender: 'male',
status: 'active',
joinedAt: '2026-08-01T10:01:00.000Z',
},
]
test.beforeEach(async ({ context, page }) => {
await authenticateAs(context, page, 'admin')
await page.route('**/api/v1/**', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(apiEnvelope({ items: [], response: { page: 1, totalPages: 1 } })),
})
)
})
test('renders the admin thread like consumer chat while remaining strictly read-only', async ({ page }) => {
const oversightMethods: string[] = []
await page.route(`**/api/v1/admin/conversations/${conversationId}`, (route) => {
oversightMethods.push(route.request().method())
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(
apiEnvelope({
id: conversationId,
type: 'direct',
eventId: null,
eventTitle: null,
participantPreview: participants,
memberCount: 2,
preview: 'پاسخ کاربر دوم',
closesAt: null,
lastMessageAt: '2026-08-01T12:02:00.000Z',
createdAt: '2026-08-01T10:00:00.000Z',
})
),
})
})
await page.route(`**/api/v1/admin/conversations/${conversationId}/participants**`, (route) => {
oversightMethods.push(route.request().method())
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(
apiEnvelope({
items: participants,
response: { page: 1, pageSize: 100, totalItemsCount: 2, totalPages: 1 },
})
),
})
})
await page.route(`**/api/v1/admin/conversations/${conversationId}/messages**`, (route) => {
oversightMethods.push(route.request().method())
return route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(
apiEnvelope({
items: [
{
id: 'message-1',
conversationId,
senderId: 'user-1',
senderMobile: '09121234567',
senderFirstName: 'سارا',
senderLastName: 'احمدی',
senderAvatarUrl: null,
senderGender: 'female',
body: 'پیام کاربر اول',
imageUrl: null,
createdAt: '2026-08-01T12:00:00.000Z',
},
{
id: 'message-2',
conversationId,
senderId: 'user-2',
senderMobile: '09351234567',
senderFirstName: 'امیر',
senderLastName: 'رضایی',
senderAvatarUrl: null,
senderGender: 'male',
body: 'پاسخ کاربر دوم',
imageUrl: null,
createdAt: '2026-08-01T12:02:00.000Z',
},
],
response: {
olderCursor: null,
newerCursor: null,
hasMoreBefore: false,
hasMoreAfter: false,
},
})
),
})
})
await page.goto(`/chat-oversight/${conversationId}`)
await expect(page.getByText('حالت نظارت — فقط خواندنی')).toBeVisible()
await expect(page.getByText('گفتگوی خصوصی', { exact: true })).toBeVisible()
await expect(page.getByText('پیام کاربر اول')).toBeVisible()
await expect(page.getByText('پاسخ کاربر دوم')).toBeVisible()
const primaryBubble = page.locator('.justify-start').filter({ hasText: 'پیام کاربر اول' })
const secondaryBubble = page.locator('.justify-end').filter({ hasText: 'پاسخ کاربر دوم' })
await expect(primaryBubble).toBeVisible()
await expect(primaryBubble.getByText('سارا احمدی')).toBeVisible()
await expect(secondaryBubble).toBeVisible()
await expect(secondaryBubble.getByText('امیر رضایی')).toBeVisible()
await expect(page.getByPlaceholder('پیام خود را بنویسید...')).toHaveCount(0)
await expect(page.getByRole('button', { name: 'ارسال' })).toHaveCount(0)
expect(oversightMethods).toHaveLength(3)
expect(oversightMethods.every((method) => method === 'GET')).toBe(true)
})