/* Browser plugin not available; run only inside the supplied Docker Playwright image. * Required env: ADMIN_URL, ADMIN_USER, ADMIN_PASSWORD, UI_OUTPUT; optional SECOND_TARGET. */ const { chromium } = require('playwright-core'); const assert = require('node:assert/strict'); const fs = require('node:fs'); (async () => { const browser = await chromium.launch({headless:true,executablePath:process.env.CHROMIUM_PATH || '/usr/bin/chromium',args:['--no-sandbox']}); const page = await browser.newPage({baseURL:process.env.ADMIN_URL||'http://admin',viewport:{width:1440,height:1000}}); const errors=[],consoleMessages=[]; page.on('pageerror',e=>errors.push(e.message));page.on('console',m=>{if(['error','warning'].includes(m.type()))consoleMessages.push({type:m.type(),text:m.text()});}); await page.goto(process.env.ADMIN_URL || 'http://admin'); assert.match(await page.title(), /Прелендинги/); await page.getByLabel('Логин', {exact:true}).fill(process.env.ADMIN_USER || 'admin'); await page.getByLabel('Пароль', {exact:true}).fill(process.env.ADMIN_PASSWORD || 'test-password'); await page.getByRole('button',{name:'Войти',exact:true}).click(); await page.locator('#workspace').waitFor({state:'visible'}); await page.locator('#source-target').selectOption({index:1}); await page.getByLabel('Основной сайт', {exact:true}).waitFor(); const host = page.getByLabel('Основной сайт', {exact:true}); const original = await host.inputValue(); const secondOriginal=process.env.SECOND_TARGET?await page.request.get('/api/config?target='+encodeURIComponent(process.env.SECOND_TARGET)).then(r=>r.json()).then(r=>r.config.host):null; await host.fill('ui-check.example.org'); assert.match(await page.locator('#change-summary').innerText(), /host/); assert.equal(await page.locator('#targets input:checked').count(),1); if (process.env.SECOND_TARGET) await page.locator('#targets input[value="'+process.env.SECOND_TARGET+'"]').check(); const apply = async () => { const pending=page.waitForResponse(r=>r.url().endsWith('/api/apply')&&r.request().method()==='POST'); await page.getByRole('button',{name:'Применить к выбранным',exact:true}).click(); const accepted=await(await pending).json();assert.equal(accepted.ok,true,accepted.error); await page.locator('.job').filter({hasText:accepted.job.id}).getByText('Сохранено и проверено',{exact:true}).first().waitFor(); }; await apply(); await page.getByRole('button',{name:'Перечитать',exact:true}).click(); await page.waitForFunction(()=>document.querySelector('[aria-label="Основной сайт"]')?.value==='ui-check.example.org'); await host.fill(original); await apply(); if(process.env.SECOND_TARGET){await page.locator('#source-target').selectOption(process.env.SECOND_TARGET);await host.waitFor();await host.fill(secondOriginal);await apply();} assert.equal(await page.locator('#config-fields textarea').count(),0,'JSON textarea absent from config editor'); assert.equal(await page.locator('.indpic-sets-table th').allTextContents().then(x=>x.join('|')),'Номер / название|Default|Проверка|ZIP|Действия'); await page.screenshot({path:(process.env.UI_OUTPUT||'/tmp')+'/admin-desktop.png',fullPage:true}); await page.setViewportSize({width:390,height:844}); await page.screenshot({path:(process.env.UI_OUTPUT||'/tmp')+'/admin-mobile.png',fullPage:true}); assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),true,'no page-level horizontal overflow'); fs.writeFileSync((process.env.UI_OUTPUT||'/tmp')+'/admin-console.json',JSON.stringify({pageErrors:errors,console:consoleMessages},null,2)); assert.deepEqual(errors,[]);assert.deepEqual(consoleMessages.filter(m=>!m.text.includes('status of 401')),[],'no unexpected browser console errors'); await browser.close(); console.log('PASS real login/read/edit/apply/reload/restore + mobile + console'); })().catch(e=>{console.error(e);process.exit(1);});