import {test,expect} from '@playwright/test'; import {bootMenuFixture,menuGeometry,exerciseMenuNavigation} from './mobile-menu-scenario.mjs'; test.use({serviceWorkers:'block'}); test('mobile menu puts editing above boxes and returns without losing unsaved values',async({page})=>{ await bootMenuFixture(page);await exerciseMenuNavigation(page); }); test('mobile menu viewport changes move the list, not the focused editor',async({page})=>{ await bootMenuFixture(page); await page.locator('[data-menu-item-v1762="mobile-box-1"]').click(); await expect(page.locator('#sunMenuDetailV1762 > .dialog')).toBeVisible(); // The automatic return is phone-only; desktop keeps its existing scroll. if(await page.evaluate(()=>matchMedia('(max-width:760px)').matches))await expect.poll(()=>page.evaluate(()=>window.scrollY)).toBe(0); await page.waitForTimeout(80); await page.locator('#boxName').fill('Сохрани ввод при повороте'); await page.locator('#boxName').focus(); await page.evaluate(()=>{window.formInput=document.getElementById('boxName');window.inputsSeen=0;formInput.addEventListener('input',()=>window.inputsSeen++);}); for(const width of [1440,390,760,1440,390]){ await page.setViewportSize({width,height:900}); await expect.poll(()=>page.evaluate(()=>{const d=document.getElementById('sunMenuDetailV1762'),list=document.querySelector('.sun-menu-list-pane');return matchMedia('(max-width:760px)').matches?d.nextElementSibling===list:list.nextElementSibling===d;})).toBe(true); await menuGeometry(page); await expect(page.locator('#boxName')).toHaveValue('Сохрани ввод при повороте'); await expect(page.locator('#boxName')).toBeFocused(); expect(await page.evaluate(()=>formInput===document.getElementById('boxName'))).toBe(true); } await page.locator('#boxName').fill('Обработчик остался'); expect(await page.evaluate(()=>inputsSeen)).toBeGreaterThan(0); await expect(page.locator('#ctMenuTop')).toHaveCount(1); }); test('mobile menu search, category and new item keep working; leaving prevents a delayed scroll',async({page})=>{ await bootMenuFixture(page); await page.locator('#sunMenuSearchV1762').fill('Бокс 07'); await expect(page.locator('[data-menu-item-v1762]')).toHaveCount(1); await page.locator('[data-menu-cat-v1762="5"]').click(); await page.locator('#sunMenuAddV1762').click(); await expect(page.locator('#sunMenuDetailV1762 > .dialog')).toBeVisible(); await expect(page.locator('#itemCategory')).toHaveValue('5'); if(await page.evaluate(()=>matchMedia('(max-width:760px)').matches))await expect.poll(()=>page.evaluate(()=>window.scrollY)).toBe(0); await menuGeometry(page); await page.locator('#boxName').fill('Премиум с телефона');await page.locator('#boxPrice').fill('2200');await page.locator('#saveBoxButton').click(); await expect.poll(()=>page.evaluate(()=>JSON.parse(localStorage.sunBoxes).find(x=>x.name==='Премиум с телефона')?.category)).toBe(5); await page.evaluate(()=>{document.getElementById('sunMenuAddV1762').click();[...document.querySelectorAll('header nav button')].find(b=>b.textContent.trim()==='Заказы').click();}); await page.waitForTimeout(100); await expect(page.locator('#editor > .dialog')).toHaveCount(1); await expect(page.locator('#ctMenuTop')).toBeHidden(); await page.locator('header nav').getByRole('button',{name:'Меню',exact:true}).click(); await expect(page.locator('#ctMenuTop')).toHaveCount(1); }); test('read-only mobile menu opens the card above the list without enabling editing',async({page})=>{ await bootMenuFixture(page); const before=await page.evaluate(()=>localStorage.sunBoxes); await page.evaluate(()=>{SunCloudV2.hasPermission=p=>p==='catalog.view'||p==='app.read';window.dispatchEvent(new Event('sun:cloud-permissions-changed'));}); await expect(page.locator('#sunMenuAddV1762')).toBeDisabled(); await page.locator('[data-menu-item-v1762="mobile-box-24"]').click(); await expect(page.locator('#sunMenuDetailV1762 .sun-menu-readonly')).toContainText('Бокс 24'); if(await page.evaluate(()=>matchMedia('(max-width:760px)').matches))await expect.poll(()=>page.evaluate(()=>window.scrollY)).toBe(0); await menuGeometry(page); await expect(page.locator('#sunMenuDetailV1762 > .dialog')).toHaveCount(0); expect(await page.evaluate(()=>localStorage.sunBoxes)).toBe(before); }); test('a queued mobile return does not steal focus once the user starts typing',async({page})=>{ await bootMenuFixture(page); await page.evaluate(()=>{ document.querySelector('[data-menu-item-v1762="mobile-box-1"]').click(); // Docking is queued by the existing menu handler. Start typing in the next // task, before the mobile requestAnimationFrame navigation callback runs. setTimeout(()=>{ const input=document.getElementById('boxName');input.value='Уже редактирую';input.focus({preventScroll:true}); },0); }); await expect(page.locator('#sunMenuDetailV1762 > .dialog #boxName')).toHaveValue('Уже редактирую'); await page.waitForTimeout(150); await expect(page.locator('#boxName')).toBeFocused(); await expect(page.locator('#boxName')).toHaveValue('Уже редактирую'); });