From 3740839321ded56cb0bbfd50550351105bd0eeb9 Mon Sep 17 00:00:00 2001 From: Panfilov Konstantin Date: Wed, 21 Oct 2026 17:35:07 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=81=D0=BE=D0=B2=D0=BF=D0=B0=D0=B4?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=B8=D0=B4=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=BB=D0=BE=D0=B3=D0=BE=D1=82=D0=B8=D0=BF=D0=B0?= =?UTF-8?q?=20Preland=20=D0=B8=20Tour=20(refs=20#12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ release/assets.json | 6 +++--- style.css | 3 +++ style.min.c.css | 3 +++ style.min.css | 3 +++ tests/e2e/brand-footer.cjs | 31 +++++++++++++++++++++++++++++++ 6 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 tests/e2e/brand-footer.cjs diff --git a/README.md b/README.md index beb5ba6..88a5307 100644 --- a/README.md +++ b/README.md @@ -197,3 +197,5 @@ Workflow `.gitea/workflows/deploy.yml` содержит единственную primary guard теперь сверяет PDF/favicon, а не заставляет возвращать Zoomzilla и реквизиты на преленд. Реквизиты не отображаются и больше автоматически не синхронизируются; хостовый config и его существующие значения не меняются этим поручением. Устаревшие requisites.html и patch-скрипт удалены из поставки. Статический manifest183, отдельный каталог брендов проверяется по собственным SHA. История #11 сохранена; её прежние инструкции о видимом бренде/реквизитах заменены этим датированным решением. Перед выпуском: девять tests/check-*.php и lint, --check/--primary/--stage-sets, оба бренда шапки/подвала на desktop/mobile, перенос Host, отсутствие карт/реквизитов, холодная/медленная загрузка, реальные локальные Preland→Tour→Join/error/retry и геометрия. Production-преленды по #4 не включать; выпускать только staging→zoomstage.online. API продуктовый код не меняется. + +Дополнение приёмки #12: tests/e2e/brand-footer.cjs проверяет именно видимый SVG по центру шапки, а не только headerbox. PUBLISHED=1 использует zoomstage.online (или PRELAND_URL); без него — изолированный pre301.localhost QA proxy. Запускать в имеющемся Docker Playwright с /candidate=репозиторий,/qa=артефакты; POST/PUT блокируются. Пропущенное при compact CSS правило Bootstrap navbar>.container восстановлено во всех3CSS: API уже имеет его из полного Bootstrap. Это устраняет переход logo y0→13.5px, который рамочный тест не замечал. diff --git a/release/assets.json b/release/assets.json index 8c2ecd8..90e0f8e 100644 --- a/release/assets.json +++ b/release/assets.json @@ -724,15 +724,15 @@ "source": "https://zoomstage.online/images/sprite.svg" }, "style.css": { - "sha256": "102aee00546d897dd94887d69b35fec0b3430448ef6a21e072f5b7d76964ce06", + "sha256": "cc88246e4efb4f2884d0f268b897950cd75620998e0d3ad9985076ce1021abfc", "source": "https://zoomstage.online/style.css" }, "style.min.c.css": { - "sha256": "d4afd7aeaa56c97d6415beee220398f654d4d9ca0cac35770e7ea3d89412c878", + "sha256": "7fc5684ab3eb00b046acf90c4c891ec393b9dc11da97db02a297876e342cad18", "source": "https://zoomstage.online/style.min.c.css" }, "style.min.css": { - "sha256": "3e579e90cd8dd1193db45cf0d433ae8acfac76eb38444755f0b4d15a3af7f2f2", + "sha256": "524c44557f1a9631e88c8b8f5bbc603abcfff7dd4de76a402cec127c0895a404", "source": "https://zoomstage.online/style.min.css" } } diff --git a/style.css b/style.css index 0e4b87a..70ea04d 100644 --- a/style.css +++ b/style.css @@ -2942,3 +2942,6 @@ body { isolation: isolate; } /* preland#11: общий с API адаптивный платёжный бейдж подвала. */ .footer-main img[src$="/images/payment.svg"]{max-width:100%;height:auto;} + +/* preland#12: восстановлен пропущенный в critical CSS Bootstrap-контракт navbar. */ +.navbar>.container{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between;} diff --git a/style.min.c.css b/style.min.c.css index d80cc76..6ac7475 100644 --- a/style.min.c.css +++ b/style.min.c.css @@ -844,3 +844,6 @@ padding-top: 2svh !important; /* preland#11: общий с API адаптивный платёжный бейдж подвала. */ .footer-main img[src$="/images/payment.svg"]{max-width:100%;height:auto;} + +/* preland#12: восстановлен пропущенный в critical CSS Bootstrap-контракт navbar. */ +.navbar>.container{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between;} diff --git a/style.min.css b/style.min.css index 3030fe0..7585e7a 100644 --- a/style.min.css +++ b/style.min.css @@ -143,3 +143,6 @@ body { isolation: isolate; } /* preland#11: общий с API адаптивный платёжный бейдж подвала. */ .footer-main img[src$="/images/payment.svg"]{max-width:100%;height:auto;} + +/* preland#12: восстановлен пропущенный в critical CSS Bootstrap-контракт navbar. */ +.navbar>.container{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between;} diff --git a/tests/e2e/brand-footer.cjs b/tests/e2e/brand-footer.cjs new file mode 100644 index 0000000..519e7a7 --- /dev/null +++ b/tests/e2e/brand-footer.cjs @@ -0,0 +1,31 @@ +const {chromium}=require('/e2e/node_modules/playwright-core'),fs=require('fs'),http=require('http'),assert=require('assert/strict'),crypto=require('crypto'); +const production=process.env.PUBLISHED==='1'; +const server=production?null:http.createServer((req,res)=>{const r=http.request(new URL(req.url,'http://api:8001'),{method:req.method,headers:{...req.headers,connection:'close'}},up=>{res.writeHead(up.statusCode,up.headers);up.pipe(res)});req.pipe(r)}).listen(8121); +const base=process.env.PRELAND_URL||(production?'https://zoomstage.online':'http://pre301.localhost:8121'); +(async()=>{const browser=await chromium.launch({args:['--no-sandbox']});const out=[]; +try{for(const width of [1440,768,390,320])for(const theme of ['light','dark'])for(const indpic of [1,2]){ + console.log('CASE',width,theme,indpic); + const ctx=await browser.newContext({viewport:{width,height:900},isMobile:width<768,hasTouch:width<768}),page=await ctx.newPage(),errors=[]; + page.on('pageerror',e=>errors.push(e.message)); + await page.route('**/*',route=>['GET','HEAD','OPTIONS'].includes(route.request().method())?route.continue():route.abort()); + await page.goto(base+'/?theme='+theme+'&indpic='+indpic,{waitUntil:'networkidle'}); + await page.waitForFunction(()=>document.querySelector('#examples').dataset.examplesState==='ready'); + assert.equal(await page.title(),'Улучшение изображения');assert(await page.locator('#dropzone-form').isVisible()); + const footer=await page.locator('.footer').innerText();assert(!/Наши реквизиты|ИНН:|ОГРНИП:|Кардаполов|Иванов/.test(footer));assert.equal(await page.locator('.footer img[src*="payment.svg"] ').count(),0);assert(footer.includes(new URL(base).hostname)); + const alignment=await page.evaluate(()=>{const h=document.querySelector('.header').getBoundingClientRect(),logo=[...document.querySelectorAll('.navbar-brand img')].find(e=>getComputedStyle(e).display!=='none').getBoundingClientRect();return {delta:logo.y-h.y-(h.height-logo.height)/2}});assert(Math.abs(alignment.delta)<1,'Видимый логотип должен быть по центру шапки'); + const logos=await page.locator('.navbar-brand img,.footer-logo img').evaluateAll(es=>es.map(e=>({src:e.src,visible:getComputedStyle(e).display!=='none',width:e.naturalWidth,loaded:e.complete}))); + assert.equal(logos.length,4);assert.equal(logos.filter(l=>l.visible).length,2); + for(const logo of logos){const url=new URL(logo.src);assert(url.searchParams.get('v'));const file=url.pathname.slice(1);const bytes=fs.readFileSync('/candidate/'+file);const digest=production?JSON.parse(fs.readFileSync('/candidate/release/brands.json'))[new URL(base).hostname].logos[file].sha256:crypto.createHash('sha256').update(bytes).digest('hex');assert.equal(url.searchParams.get('v'),digest.slice(0,16));if(logo.visible)assert(logo.loaded&&logo.width>0);} + assert(logos.filter(l=>l.visible).every(l=>new URL(l.src).pathname.endsWith(theme==='dark'?'logo-alt.svg':'logo.svg'))); + await page.waitForFunction(()=>document.querySelectorAll('.image-examples-item')[1].getAttribute('aria-disabled')==='false'); + await page.locator('.image-examples-item').nth(1).click(); + await page.waitForFunction(()=>/02_(desk|mob)_1-source/.test(document.querySelector('#image-compare-source').src)); + assert((await page.locator('#image-compare-source').getAttribute('src')).includes('/'+indpic+'/')); + assert(/02_(desk|mob)_2-result/.test(await page.locator('#image-compare-result').getAttribute('src'))); + if(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth)){await page.screenshot({path:'/qa/overflow.png',fullPage:true});console.log(await page.evaluate(()=>[...document.querySelectorAll('body *')].map(e=>({tag:e.tagName,cls:e.className,x:e.getBoundingClientRect().x,right:e.getBoundingClientRect().right,width:e.getBoundingClientRect().width})).filter(e=>e.right>innerWidth+1)));} + assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth),false);assert.deepEqual(errors,[]); + const row={width,theme,indpic,logos,errors};out.push(row); + if(indpic===1&&(width===1440||width===390))await page.screenshot({path:'/qa/brand-'+(production?'published':'local')+'-'+width+'-'+theme+'.png',fullPage:true}); + await ctx.close(); +}fs.writeFileSync('/qa/brand-'+(production?'published':'local')+'.json',JSON.stringify(out,null,2));console.log('PASS brand + thumbnail interactions',out.length,'cases');} +finally{await browser.close();server?.close();}})().catch(e=>{console.error(e);process.exit(1)});