diff --git a/docs/site-fixes-2026-10-11/REPORT.md b/docs/site-fixes-2026-10-11/REPORT.md index a4fd0ba..0b5b547 100644 --- a/docs/site-fixes-2026-10-11/REPORT.md +++ b/docs/site-fixes-2026-10-11/REPORT.md @@ -250,3 +250,35 @@ iPhone 17) нашёл ещё два места, где «сумма + ₸ + /м Остальное в прогоне владельца чисто (меню, «Назад», тарифы, выбор Старт/Бизнес/Сеть, нет горизонтальной прокрутки на 25 страницах) — не трогал. Юридические реквизиты не трогал. Прод-сайт не трогал. Весы — пауза в силе. + +--- + +## Круг 5 (прогон этапа 1 в Chrome 154 на dev-android, 360css — 12.10) + +Регрессия от Круга 4 (`a0f9e0b`): на /signup `whitespace-nowrap` делал цену +плитки тарифа **шире узкой колонки** (плитка ~87px, контента ~65px на 360px). +«14 000 ₸/мес» (scrollWidth 77 > 65) срезался — на экране «14 000 ₸/ме». + +Исправлено (commits `a5f885c`, `ee5c7ec`; деплой только на тест, статус `OK`): + +- **Плитки тарифа `/signup` — сумма и «/мес» на разных строках.** Сумма+«₸» — + одной неразрывной строкой (`whitespace-nowrap`, видна целиком), «/мес» — + отдельной строкой под суммой (меньшим кеглем). Плитки остаются в 3 колонки, + внутри суммы переноса нет, хвост не срезается. +- **Плитка «Сеть» — «по запросу» переносится по словам** (это не сумма, nowrap + ей не нужен; на 320px слово не влезало в колонку). +- Итог «Ваша конфигурация „Бизнес“» и карточки тарифов на главной — без + изменений (там полноширинные строки, запаса хватает). + +**Автопроверка (Playwright, Chrome-154/Android-UA):** ширины **320, 360, 375, +390, 402** × `/signup?plan=start` и `/signup?plan=business&shops=3&cashes=5& +warehouses=5&employees=10` (итог **18 000 ₸/мес**) + главная. Для каждой плитки +проверено, что `scrollWidth ≤ clientWidth` (нет переполнения/среза): **52/52 +passed**. Суммы «5 000 ₸», «10 000 ₸», «18 000 ₸» — `priceOver=0` на всех +ширинах. Итог «Бизнес» = `18 000 ₸⁠/⁠мес`, одна строка (20px). Горизонтальной +прокрутки нет. + +Скриншоты (`docs/site-fixes-2026-10-11/`): `k5-signup-start-{320,360,375,390,402}.png`, +`k5-signup-biz-*.png`, `k5-home-360.png`. Скрипт: `tests/e2e/tools/site-fixes-verify-5.mjs`. + +Весы — пауза в силе, не трогал. Прод-сайт и реквизиты не трогал. diff --git a/docs/site-fixes-2026-10-11/k5-home-360.png b/docs/site-fixes-2026-10-11/k5-home-360.png new file mode 100644 index 0000000..5cf8d16 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-home-360.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-biz-320.png b/docs/site-fixes-2026-10-11/k5-signup-biz-320.png new file mode 100644 index 0000000..b47cdfc Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-biz-320.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-biz-360.png b/docs/site-fixes-2026-10-11/k5-signup-biz-360.png new file mode 100644 index 0000000..796fe83 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-biz-360.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-biz-375.png b/docs/site-fixes-2026-10-11/k5-signup-biz-375.png new file mode 100644 index 0000000..3306a7a Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-biz-375.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-biz-390.png b/docs/site-fixes-2026-10-11/k5-signup-biz-390.png new file mode 100644 index 0000000..30ad7c2 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-biz-390.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-biz-402.png b/docs/site-fixes-2026-10-11/k5-signup-biz-402.png new file mode 100644 index 0000000..9430d41 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-biz-402.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-start-320.png b/docs/site-fixes-2026-10-11/k5-signup-start-320.png new file mode 100644 index 0000000..3821182 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-start-320.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-start-360.png b/docs/site-fixes-2026-10-11/k5-signup-start-360.png new file mode 100644 index 0000000..56bbf6b Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-start-360.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-start-375.png b/docs/site-fixes-2026-10-11/k5-signup-start-375.png new file mode 100644 index 0000000..804e5e2 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-start-375.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-start-390.png b/docs/site-fixes-2026-10-11/k5-signup-start-390.png new file mode 100644 index 0000000..8f37013 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-start-390.png differ diff --git a/docs/site-fixes-2026-10-11/k5-signup-start-402.png b/docs/site-fixes-2026-10-11/k5-signup-start-402.png new file mode 100644 index 0000000..1b4fc03 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k5-signup-start-402.png differ diff --git a/tests/e2e/tools/site-fixes-verify-5.mjs b/tests/e2e/tools/site-fixes-verify-5.mjs new file mode 100644 index 0000000..7026526 --- /dev/null +++ b/tests/e2e/tools/site-fixes-verify-5.mjs @@ -0,0 +1,90 @@ +// Круг 5: цена в плитках тарифа /signup не должна срезаться на узких экранах. +// Регрессия от a0f9e0b (Chrome 154, dev-android, 360css). Ширины: 320,360,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, 360, 375, 390, 402] +const results = [] +const ok = (n, c, d = '') => { results.push({ n, c: !!c, d }); console.log(`${c ? '✓' : '✗'} ${n} ${d}`) } + +const b = await chromium.launch({ args: ['--ignore-certificate-errors'] }) + +// проверка «плитки тарифа не срезают цену»: для каждой плитки — нет переполнения +// (scrollWidth<=clientWidth) ни на самой плитке, ни на контейнере цены. +async function checkTiles(p, w, label) { + const data = await p.evaluate(() => { + const labels = Array.from(document.querySelectorAll('form label')) + .filter(l => l.querySelector('input[name="plan"]')) + return labels.map(l => { + const name = l.querySelector('.font-semibold')?.textContent?.trim() ?? '?' + const priceBox = l.querySelector('.font-semibold')?.nextElementSibling + const sumSpan = priceBox?.querySelector('span') + const over = (el) => el ? el.scrollWidth - el.clientWidth : 0 + return { + name, + labelOver: over(l), + priceOver: over(priceBox), + sumText: sumSpan?.textContent ?? '', + sumOver: sumSpan ? Math.max(0, sumSpan.scrollWidth - l.clientWidth) : 0, + } + }) + }) + for (const t of data) { + ok(`${label}@${w}: плитка «${t.name}» цена не срезана`, + t.labelOver <= 1 && t.priceOver <= 1, + `labelOver=${t.labelOver} priceOver=${t.priceOver} sum="${t.sumText}"`) + } +} + +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 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/154.0.0.0 Mobile Safari/537.36', + }) + 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}`) + } + + // /signup?plan=start + await p.goto(`${BASE}/signup?plan=start`, { waitUntil: 'networkidle' }); await p.waitForTimeout(600) + await checkTiles(p, w, 'start') + await p.screenshot({ path: resolve(OUT, `k5-signup-start-${w}.png`), fullPage: true }) + await noHScroll('start') + + // /signup?plan=business&shops=3&cashes=5&warehouses=5&employees=10 → 18 000 ₸ + await p.goto(`${BASE}/signup?plan=business&shops=3&cashes=5&warehouses=5&employees=10`, { waitUntil: 'networkidle' }); await p.waitForTimeout(700) + await checkTiles(p, w, 'biz') + const sum = await p.locator('span.font-bold.whitespace-nowrap').first().innerText().catch(() => '') + ok(`biz@${w}: итог = 18 000 ₸/мес`, /18[\s ]000[\s ]₸.?\/.?мес/.test(sum), JSON.stringify(sum)) + const sumBox = await p.locator('span.font-bold.whitespace-nowrap').first().boundingBox().catch(() => null) + ok(`biz@${w}: итог одной строкой`, sumBox && sumBox.height <= 24, sumBox ? `h=${Math.round(sumBox.height)}` : 'нет') + await p.screenshot({ path: resolve(OUT, `k5-signup-biz-${w}.png`), fullPage: true }) + await noHScroll('biz') + + await ctx.close() +} + +// главная/тарифы не сломались (одна ширина — 360) +{ + const ctx = await b.newContext({ viewport: { width: 360, height: 900 }, deviceScaleFactor: 2, ignoreHTTPSErrors: true, isMobile: true, hasTouch: true }) + const p = await ctx.newPage() + await p.goto(`${BASE}/`, { waitUntil: 'networkidle' }); await p.waitForTimeout(400) + const hero = await p.locator('p:has-text("Подписка от") span').first().boundingBox().catch(() => null) + ok('home@360: hero цена одной строкой', hero && hero.height <= 24, hero ? `h=${Math.round(hero.height)}` : 'нет') + const over = await p.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth) + ok('home@360: нет горизонтальной прокрутки', over <= 1, `overflow=${over}`) + await p.screenshot({ path: resolve(OUT, `k5-home-360.png`), fullPage: true }) + await ctx.close() +} + +await b.close() +const pass = results.filter(r => r.c).length +console.log(`\n[site-verify-5] ${pass}/${results.length} passed → ${OUT}`) +process.exit(pass === results.length ? 0 : 1)