food-market/tests/e2e/tools/site-fixes-verify-4.mjs
nns 03c1dd98ce
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
docs(site): отчёт и скриншоты Круга 4 (цена не разрывается, 320–402px, Safari)
Автопроверка 44/44 на ширинах 320/375/390/402 × главная/pricing/signup —
скриншоты k4-*.png и скрипт site-fixes-verify-4.mjs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-12 00:35:50 +05:00

76 lines
5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Круг 4: проверка, что «сумма + ₸ + /мес» нигде не разрывается по строкам.
// Ширины из прогона владельца в Safari: 320, 375, 390, 402.
import { chromium } from '@playwright/test'
import { mkdirSync } from 'node:fs'
import { resolve } from 'node:path'
const BASE = process.env.SITE_URL ?? 'https://test.food-market.kz'
const OUT = resolve('/home/nns/food-market', 'docs/site-fixes-2026-10-11')
mkdirSync(OUT, { recursive: true })
const WIDTHS = [320, 375, 390, 402]
const results = []
const ok = (n, c, d = '') => { results.push({ n, c: !!c, d }); console.log(`${c ? '✓' : '✗'} ${n} ${d}`) }
// высота одной строки: text-xs ~16px, text-3xl ~36px, text-sm ~20px. Один ряд => ниже порога.
const oneLine = (box, max) => box && box.height > 0 && box.height <= max
const b = await chromium.launch({ args: ['--ignore-certificate-errors'] })
for (const w of WIDTHS) {
const ctx = await b.newContext({
viewport: { width: w, height: 900 }, deviceScaleFactor: 2,
ignoreHTTPSErrors: true, isMobile: true, hasTouch: true,
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
})
const p = await ctx.newPage()
// нет горизонтальной прокрутки
const noHScroll = async (label) => {
const over = await p.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth)
ok(`${label}@${w}: нет горизонтальной прокрутки`, over <= 1, `overflow=${over}`)
}
// ---- Главная ----
await p.goto(`${BASE}/`, { waitUntil: 'networkidle' }); await p.waitForTimeout(400)
// hero-строка: span с «5 000 ₸/мес»
const hero = await p.locator('p:has-text("Подписка от") span').first().boundingBox().catch(() => null)
ok(`home@${w}: hero «5 000 ₸/мес» одной строкой`, oneLine(hero, 24), hero ? `h=${Math.round(hero.height)}` : 'нет')
// карточки тарифов: nowrap-спаны внутри цен
const startCard = await p.locator('section:has(h2:has-text("Простые тарифы")) p.text-3xl').first().boundingBox().catch(() => null)
ok(`home@${w}: карточка «Старт» цена одной строкой`, oneLine(startCard, 48), startCard ? `h=${Math.round(startCard.height)}` : 'нет')
await p.screenshot({ path: resolve(OUT, `k4-home-${w}.png`), fullPage: true })
await noHScroll('home')
// ---- /pricing ----
await p.goto(`${BASE}/pricing`, { waitUntil: 'networkidle' }); await p.waitForTimeout(600)
const prBuilder = await p.locator('#configurator .font-extrabold').first().boundingBox().catch(() => null)
ok(`pricing@${w}: сумма конструктора одной строкой`, oneLine(prBuilder, 48), prBuilder ? `h=${Math.round(prBuilder.height)}` : 'нет')
await p.screenshot({ path: resolve(OUT, `k4-pricing-${w}.png`), fullPage: true })
await noHScroll('pricing')
// ---- /signup?plan=start ----
await p.goto(`${BASE}/signup?plan=start`, { waitUntil: 'networkidle' }); await p.waitForTimeout(600)
const tileStart = await p.locator('label:has-text("Старт") .whitespace-nowrap').first().boundingBox().catch(() => null)
ok(`signup-start@${w}: плитка «Старт» цена одной строкой`, oneLine(tileStart, 24), tileStart ? `h=${Math.round(tileStart.height)}` : 'нет')
await p.screenshot({ path: resolve(OUT, `k4-signup-start-${w}.png`), fullPage: true })
await noHScroll('signup-start')
// ---- /signup после конфигуратора «Бизнес» (14 000 ₸/мес) ----
await p.goto(`${BASE}/signup?plan=business&shops=3&cashes=1&warehouses=1&employees=5`, { waitUntil: 'networkidle' }); await p.waitForTimeout(700)
const sumBiz = await p.locator('span.font-bold.whitespace-nowrap').first().boundingBox().catch(() => null)
ok(`signup-biz@${w}: итог «14 000 ₸/мес» одной строкой`, oneLine(sumBiz, 24), sumBiz ? `h=${Math.round(sumBiz.height)}` : 'нет')
const tileBiz = await p.locator('label:has-text("Бизнес") .whitespace-nowrap').first().boundingBox().catch(() => null)
ok(`signup-biz@${w}: плитка «Бизнес» цена одной строкой`, oneLine(tileBiz, 24), tileBiz ? `h=${Math.round(tileBiz.height)}` : 'нет')
// проверим, что текст действительно «14 000 ₸/мес»
const bizTxt = await p.locator('span.font-bold.whitespace-nowrap').first().innerText().catch(() => '')
ok(`signup-biz@${w}: сумма = 14 000 ₸/мес`, /14[\s ]000[\s ]₸.?\/.?мес/.test(bizTxt), JSON.stringify(bizTxt))
await p.screenshot({ path: resolve(OUT, `k4-signup-biz-${w}.png`), fullPage: true })
await noHScroll('signup-biz')
await ctx.close()
}
await b.close()
const pass = results.filter(r => r.c).length
console.log(`\n[site-verify-4] ${pass}/${results.length} passed → ${OUT}`)
process.exit(pass === results.length ? 0 : 1)