import { expect, test, type Page } from '@playwright/test'; async function login(page: Page, username: string) { await page.addInitScript(() => localStorage.setItem('pipu.locale', 'ru')); await page.goto('/login'); await page.getByLabel('Логин', { exact: true }).fill(username); await page.getByRole('textbox', { name: 'Пароль', exact: true }).fill(`${username}-password`); await page.getByRole('button', { name: 'Войти', exact: true }).click(); await expect(page.getByRole('button', { name: 'Ещё', exact: true })).toBeVisible(); } async function fits(page: Page) { await expect.poll(() => page.evaluate(() => { const main = document.querySelector('main')!; return Math.max(document.documentElement.scrollWidth - innerWidth, main.scrollWidth - main.clientWidth); })).toBeLessThanOrEqual(1); } for (const [width, username] of [[320, 'mobile-admin'], [390, 'moderator'], [430, 'alice']] as const) { test(`mobile ${width}: ${username} navigation, forms and search`, async ({ page }, info) => { test.setTimeout(60_000); await page.setViewportSize({ width, height: 844 }); await login(page, username); await fits(page); const bottom = page.getByRole('navigation', { name: 'Навигация' }).filter({ has: page.getByRole('button', { name: 'Ещё', exact: true }) }); await expect(bottom.getByRole('link')).toHaveCount(3); await page.getByRole('button', { name: 'Ещё', exact: true }).click(); const dialog = page.getByRole('dialog', { name: 'Навигация' }); await expect(dialog).toBeVisible(); await expect(dialog.getByRole('link', { name: 'Дизлайки', exact: true })).toBeVisible(); if (username !== 'alice') { await expect(dialog.getByRole('link', { name: 'Пользователи', exact: true })).toBeVisible(); await expect(dialog.getByRole('link', { name: 'Очередь', exact: true })).toBeVisible(); await expect(dialog.getByRole('link', { name: 'Хранилища', exact: true })).toHaveCount(username === 'mobile-admin' ? 1 : 0); await dialog.getByRole('link', { name: 'Пользователи', exact: true }).click(); await expect(dialog).not.toBeVisible(); await expect(page.getByRole('heading', { name: 'Пользователи', exact: true })).toBeVisible(); await fits(page); await page.screenshot({ path: info.outputPath('users.png') }); await page.getByRole('button', { name: /Действия.*alice/ }).click(); const menu = page.getByRole('menu'); await expect(menu).toBeVisible(); const rect = await menu.boundingBox(); expect(rect!.x).toBeGreaterThanOrEqual(0); expect(rect!.x + rect!.width).toBeLessThanOrEqual(width); await page.keyboard.press('Escape'); await page.getByRole('button', { name: 'Создать пользователя', exact: true }).click(); await expect(page.getByRole('dialog')).toBeVisible(); await page.screenshot({ path: info.outputPath('create-user.png') }); await page.getByRole('button', { name: 'Закрыть', exact: true }).click(); } else { await expect(dialog.getByRole('link', { name: 'Пользователи', exact: true })).toHaveCount(0); await page.getByRole('button', { name: 'Закрыть', exact: true }).click(); } await page.getByRole('button', { name: 'Меню профиля' }).click(); await page.getByRole('menuitem', { name: 'Настройки', exact: true }).click(); await expect(page.getByRole('heading', { name: 'Настройки', exact: true })).toBeVisible(); await fits(page); await page.screenshot({ path: info.outputPath('settings.png') }); for (const name of ['Слева', 'По центру', 'Справа']) { await page.getByRole('button', { name, exact: true }).click(); await page.getByRole('combobox', { name: 'Поиск', exact: true }).fill('кино'); await expect(page.getByRole('option', { name: /Кино/ }).first()).toBeVisible(); await fits(page); await page.getByRole('combobox', { name: 'Поиск', exact: true }).press('Escape'); } if (username !== 'alice') { await page.getByRole('button', { name: 'Ещё', exact: true }).click(); await page.getByRole('dialog').getByRole('link', { name: 'Очередь', exact: true }).click(); await expect(page.getByRole('heading', { name: 'Очередь на добавление' })).toBeVisible(); await expect(page.locator('audio')).toBeVisible(); await fits(page); await page.getByRole('checkbox', { name: /^Выбрать Длинное/ }).check(); await expect(page.getByRole('region', { name: 'Действия с выбранными' })).toBeVisible(); await fits(page); await page.screenshot({ path: info.outputPath('upload-queue.png') }); await page.getByRole('region', { name: 'Действия с выбранными' }).getByRole('button', { name: 'Отклонить' }).click(); await expect(page.getByRole('dialog')).toBeVisible(); await page.getByRole('button', { name: 'Закрыть', exact: true }).click(); } if (username === 'mobile-admin') { await page.getByRole('button', { name: 'Ещё', exact: true }).click(); await page.getByRole('dialog').getByRole('link', { name: 'Хранилища', exact: true }).click(); await expect(page.getByRole('article', { name: 'Музыкальное хранилище' })).toBeVisible(); await fits(page); await page.getByRole('button', { name: 'Шаблоны путей', exact: true }).click(); await page.getByRole('button', { name: 'Добавить шаблон', exact: true }).click(); await fits(page); await page.screenshot({ path: info.outputPath('patterns.png') }); } }); } test('mobile player keeps the island above navigation and exposes the queue', async ({ page }, info) => { await page.setViewportSize({ width: 320, height: 740 }); await login(page, 'alice'); const box = page.getByRole('combobox', { name: 'Поиск', exact: true }); await box.fill('кино'); await box.press('Enter'); await page.getByRole('link', { name: /Группа крови/ }).first().click(); await page.getByRole('listitem').first().click(); const player = page.getByRole('contentinfo', { name: 'Плеер' }); await expect(player.getByRole('button', { name: 'Очередь', exact: true })).toBeVisible(); expect((await player.getByText('Группа крови', { exact: true }).boundingBox())!.width).toBeGreaterThan(40); await fits(page); await page.getByRole('button', { name: 'Меню профиля' }).click(); await page.getByRole('menuitem', { name: 'Настройки', exact: true }).click(); await page.getByRole('button', { name: 'Остров', exact: true }).click(); const nav = page.getByRole('navigation').filter({ has: page.getByRole('button', { name: 'Ещё', exact: true }) }); await expect.poll(async () => { const p = await player.boundingBox(); const n = await nav.boundingBox(); return p!.y + p!.height <= n!.y + 1; }).toBe(true); await player.getByRole('button', { name: 'Очередь', exact: true }).click(); await expect(page.getByRole('complementary', { name: 'Очередь', exact: true })).toBeVisible(); await page.screenshot({ path: info.outputPath('player-queue.png') }); });