import { expect, test } from './fixtures' test.use({ viewport: { width: 390, height: 844 } }) test('mobile shell exposes floating navigation, notification sheet, calendar and date picker', async ({ page }) => { const now = new Date().toISOString() await page.route('**/api/**', async (route) => { const request = route.request() const url = new URL(request.url()) if (!url.pathname.startsWith('/api/')) return route.continue() if (url.pathname === '/api/auth/me') return route.fulfill({ json: { data: { id: 11, name: 'کارشناس موبایل', email: 'agent@example.test', phone: null, avatar: null, roles: ['agent'], permissions: ['view_agent_dashboard', 'view_leads', 'view_own_tasks', 'create_tasks'], is_active: true, team_id: 2, created_at: '', updated_at: '', } } }) if (url.pathname === '/api/settings/public') return route.fulfill({ json: { data: {} } }) if (url.pathname === '/api/notifications/unread-count') return route.fulfill({ json: { data: 1 } }) if (url.pathname === '/api/notifications') return route.fulfill({ json: paginated([{ id: 81, type: 'task_assigned', title: 'کار جدید', message: 'پیگیری قرارداد', data: { url: '/tasks' }, is_read: false, read_at: null, created_at: now, }]) }) if (url.pathname === '/api/calendar/events') return route.fulfill({ json: { events: [{ id: 'task-31', entity_id: 31, type: 'task', title: 'پیگیری قرارداد', starts_at: now, status: 'open', priority: 'normal', assignee: { id: 11, name: 'کارشناس موبایل' }, related: null, url: '/tasks', }] } }) if (url.pathname === '/api/tasks') return route.fulfill({ json: paginated([]) }) if (url.pathname === '/api/users/assignable') return route.fulfill({ json: { data: [] } }) return route.fulfill({ json: { data: {} } }) }) await page.goto('/tasks') await expect(page.getByRole('navigation', { name: 'ناوبری اصلی موبایل' })).toBeVisible() await expect(page.locator('aside')).toBeHidden() await expect(page.getByRole('button', { name: 'تقویم کارها و پیگیری‌ها' })).toBeVisible() await page.getByRole('button', { name: 'تقویم کارها و پیگیری‌ها' }).click() await expect(page).toHaveURL(/\/calendar$/) await page.getByRole('tab', { name: 'برنامه روز' }).click() await expect(page.getByRole('button').filter({ hasText: 'پیگیری قرارداد' }).last()).toBeVisible() await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth === document.documentElement.clientWidth)).toBe(true) const calendarDialog = page.getByRole('dialog', { name: 'تقویم کارها و پیگیری‌ها' }) await calendarDialog.getByRole('button', { name: 'بستن' }).click() await expect(calendarDialog).toBeHidden() await expect(page).toHaveURL(/\/tasks$/) await page.getByRole('button', { name: /اعلان خوانده‌نشده/ }).click() const notificationDialog = page.getByRole('dialog', { name: 'اعلان‌ها' }) await expect(notificationDialog).toBeVisible() await expect(notificationDialog.getByText('کار جدید')).toBeVisible() const notificationBox = await notificationDialog.boundingBox() expect(notificationBox?.x).toBeGreaterThanOrEqual(12) expect(notificationBox ? 390 - notificationBox.x - notificationBox.width : 0).toBeGreaterThanOrEqual(12) await page.keyboard.press('Escape') await expect(page.getByRole('dialog', { name: 'اعلان‌ها' })).toBeHidden() await expect(page.getByRole('heading', { name: 'مرکز کارها' })).toBeVisible() await page.getByRole('button', { name: 'کار جدید' }).click() await page.getByLabel('موضوع کار').fill('کار آزمایشی') await page.getByRole('button', { name: 'ادامه' }).click() await page.getByRole('button', { name: /^موعد:/ }).click() await expect(page.getByRole('dialog', { name: 'موعد' })).toBeVisible() await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth === document.documentElement.clientWidth)).toBe(true) await page.getByRole('button', { name: 'امروز' }).click() await expect(page.getByRole('button', { name: /^موعد:/ })).not.toContainText('انتخاب تاریخ و ساعت') }) test('selected calendar day number stays visible in dark mode', async ({ page }) => { await page.addInitScript(() => localStorage.setItem('theme', 'dark')) await page.route('**/api/**', async (route) => { const url = new URL(route.request().url()) if (!url.pathname.startsWith('/api/')) return route.continue() if (url.pathname === '/api/auth/me') return route.fulfill({ json: { data: { id: 11, name: 'کارشناس موبایل', email: 'agent@example.test', roles: ['agent'], permissions: ['view_agent_dashboard', 'view_leads', 'view_own_tasks'], is_active: true, } } }) if (url.pathname === '/api/settings/public') return route.fulfill({ json: { data: {} } }) if (url.pathname === '/api/notifications/unread-count') return route.fulfill({ json: { data: 0 } }) if (url.pathname === '/api/calendar/events') return route.fulfill({ json: { events: [] } }) return route.fulfill({ json: { data: [] } }) }) await page.goto('/calendar') const selectedNumber = page.locator('button[aria-pressed="true"] > span').first() await expect(selectedNumber).toBeVisible() const colors = await selectedNumber.evaluate((element) => { const style = getComputedStyle(element) return { foreground: style.color, background: style.backgroundColor, text: element.textContent?.trim() } }) expect(colors.text).toBeTruthy() expect(colors.background).not.toBe('rgba(0, 0, 0, 0)') expect(colors.foreground).not.toBe(colors.background) }) test('desktop calendar keeps the first event readable inside its day cell', async ({ page }) => { await page.setViewportSize({ width: 1440, height: 820 }) const now = new Date().toISOString() await page.route('**/api/**', async (route) => { const url = new URL(route.request().url()) if (!url.pathname.startsWith('/api/')) return route.continue() if (url.pathname === '/api/auth/me') return route.fulfill({ json: { data: { id: 11, name: 'کارشناس تقویم', email: 'agent@example.test', roles: ['agent'], permissions: ['view_agent_dashboard', 'view_leads', 'view_own_tasks'], is_active: true, } } }) if (url.pathname === '/api/settings/public') return route.fulfill({ json: { data: {} } }) if (url.pathname === '/api/notifications/unread-count') return route.fulfill({ json: { data: 0 } }) if (url.pathname === '/api/calendar/events') return route.fulfill({ json: { events: [ { id: 'task-1', entity_id: 1, type: 'task', title: 'رویداد قابل خواندن', starts_at: now, status: 'open', priority: 'normal', assignee: null, related: null, url: '/tasks' }, { id: 'follow-2', entity_id: 2, type: 'follow_up', title: 'پیگیری دوم', starts_at: now, status: 'pending', assignee: null, related: null, url: '/follow-ups' }, ] } }) return route.fulfill({ json: { data: [] } }) }) await page.goto('/calendar') const event = page.locator('span[title="رویداد قابل خواندن"]') await expect(event).toBeVisible() await expect(page.getByText('+۱ مورد دیگر')).toBeVisible() const cell = event.locator('xpath=ancestor::button[1]') const eventBox = await event.boundingBox() const cellBox = await cell.boundingBox() expect(eventBox?.height).toBeGreaterThanOrEqual(12) expect(eventBox?.y).toBeGreaterThanOrEqual(cellBox?.y ?? 0) expect((eventBox?.y ?? 0) + (eventBox?.height ?? 0)).toBeLessThanOrEqual((cellBox?.y ?? 0) + (cellBox?.height ?? 0)) }) function paginated(items: Record[]) { return { data: items, meta: { current_page: 1, last_page: 1, per_page: 20, total: items.length, from: items.length ? 1 : 0, to: items.length } } }