docs(site): отчёт и скриншоты проверки исправлений test.food-market.kz (10/10)
docs/site-fixes-2026-10-10/{index.html,REPORT.md,screenshots}; tools/site-fixes-verify.mjs
(Playwright-проверка: signup plan/конфигурация/цена, мобильное меню, без промисов/REPLACE_ME).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75
docs/site-fixes-2026-10-10/REPORT.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Исправления публичного сайта test.food-market.kz — этап 1 «Сайт и выбор тарифа»
|
||||||
|
|
||||||
|
Дата: 2026-10-10/11. Источник: src/food-market.public (Astro). Деплой — только на ТЕСТ
|
||||||
|
(`~/.fm-watchdog/deploy-public-test.sh`). Откат: образ `food-market-public:test-pre-site-fixes`
|
||||||
|
(создан перед деплоем). Прод-сайт, gateway nginx, прод-БД — НЕ трогались.
|
||||||
|
|
||||||
|
Проверка после деплоя (Playwright, десктоп 1440×900@2x и моб. 390×844@3x): **10/10** —
|
||||||
|
см. `index.html` в этой папке (скриншоты + автопроверки).
|
||||||
|
|
||||||
|
## Что исправлено (по номерам 1–10)
|
||||||
|
|
||||||
|
1. **/signup читает `?plan=` и конфигурацию.** Форма на клиенте разбирает `plan`
|
||||||
|
(start|business|network; неизвестный → «Старт») и `shops/cashes/warehouses/employees`.
|
||||||
|
Выбранный тариф предвыбран; для «Бизнес» показывается конфигурация и итоговая цена.
|
||||||
|
Расчёт вынесен в общий модуль `src/lib/pricing.ts` (БД 10 000 + 2 000/магазин +
|
||||||
|
500/касса + 500/склад) — одни и те же числа в конструкторе, форме и на /pricing.
|
||||||
|
Проверено: `?plan=business&shops=3&cashes=4&warehouses=1&employees=5` → «Бизнес» выбран,
|
||||||
|
конфигурация и **15 500 ₸** видны; `?plan=start` → «Старт»; `?plan=xyz` → «Старт».
|
||||||
|
Параметры конфигурации уходят в тело запроса регистрации (вперёд-совместимо). **Текущий
|
||||||
|
контракт `/api/auth/signup` принимает только `plan` (SignupInput.Plan), остальные поля
|
||||||
|
игнорирует** — см. раздел «нужно решение владельца».
|
||||||
|
2. **Мобильная шапка (≤ lg).** Добавлены гамбургер + выезжающая панель со ВСЕМИ пунктами
|
||||||
|
десктоп-меню и кнопками «Войти»/«Начать бесплатно». `aria-expanded`/`aria-controls`,
|
||||||
|
закрытие по тапу вне и по Esc, доступно с клавиатуры; зоны нажатия ≥ 44 px. Проверено на
|
||||||
|
390 px: меню открывается, тап по «Тарифы» ведёт на /pricing, горизонтальной прокрутки нет.
|
||||||
|
3. **og/twitter-картинки** перегенерированы (`scripts/og.mjs` → `public/og/*`): из home убрано
|
||||||
|
«ОФД РК», в import «REST API» → «Excel, CSV, 1С, МойСклад», из for-grocery убран «ABC-анализ».
|
||||||
|
4. **База знаний и блог — убраны обещания несуществующего** (сверено с «В разработке» на главной
|
||||||
|
и /integrations/): способы оплаты подписки «Kaspi Pay/Halyk/Jusan/Forte/Visa/Mastercard»
|
||||||
|
→ «способ уточняется, свяжитесь с нами»; «стандартный REST API» в импорте → «Excel/CSV,
|
||||||
|
1С, МойСклад»; «Скачать установщик/.msi/Скачать кассу для Windows» → «скоро»; «тип оплаты
|
||||||
|
Наличные/Kaspi/Карта» → «наличные или карта»; «Kaspi Magazin (Q3 2026)» → без даты;
|
||||||
|
«архитектура для будущей маркировки» — убрано; «Триал» как тариф → «Старт/Бизнес/Сеть,
|
||||||
|
90 дней бесплатно»; платные консультации «5 000 тг/час» и скидки 3/6/12 мес — убраны
|
||||||
|
(нет на /pricing, не подтверждены). Ссылки на несуществующие /kb/users, /kb/loyalty убраны.
|
||||||
|
5. **/import**: чужой английский HR-скриншот (`/photos/import-hero.jpg`) заменён на реальный
|
||||||
|
скриншот каталога продукта (`/screenshots/catalog.webp`).
|
||||||
|
6. **Рендер /kb/***: тело статей было завёрнуто в ```` ```markdown ```` с дублирующим front-matter
|
||||||
|
и вторым `#`-заголовком → показывалось сырым моноширинным текстом. Развёрнуто: front-matter
|
||||||
|
не виден, markdown рендерится, заголовок один. Проверены все /kb/* и /blog/* (блог был чист).
|
||||||
|
7. **Аналитика**: убраны заглушки `data-id="REPLACE_ME"` (Google/Яндекс.Метрика) — скрипты
|
||||||
|
подключаются только при реальных `PUBLIC_GA_ID`/`PUBLIC_YM_ID`, иначе в HTML ничего нет.
|
||||||
|
8. **robots.txt**: `Sitemap:` → рабочий `/sitemap.xml` (был 404 `/sitemap-index.xml`).
|
||||||
|
9. **Противоречия сведены** к оферте (она главнее): удаление данных — 30 дней (было 30/60 в KB);
|
||||||
|
возврат — по письменному заявлению в случаях по закону РК (убрано «14 дней без обоснования»);
|
||||||
|
«Старт без лимита пользователей» → «до 2 сотрудников» (как на /pricing). Дубль в футере
|
||||||
|
«Дорожная карта»/«Changelog» (один URL) — убран, остался «Changelog». /status «Касса
|
||||||
|
(Windows) — в разработке» → «Скоро» (согласовано с «Скачать · скоро»).
|
||||||
|
10. **Мелочи**: зоны нажатия — ссылки футера `block py-2` (≈40 px), CTA в шапке `min-h-[44px]`;
|
||||||
|
у lazy-картинок (главная, /pos) проставлены `width`/`height` (catalog 1440×900, pos 1440×810)
|
||||||
|
против сдвига вёрстки.
|
||||||
|
|
||||||
|
## Что НЕ сделано / частично и почему
|
||||||
|
- **Зоны нажатия инлайновых ссылок** в предложениях (например «офертой»/«политикой» в форме,
|
||||||
|
отдельные «Подробнее →») не раздувал до 44 px — это текст внутри абзаца, увеличение ломает
|
||||||
|
поток; основные интерактивные элементы (меню, кнопки, ссылки футера) подняты до ≥ 40–44 px.
|
||||||
|
- **«Популярный» на /pricing у «Бизнес»** не добавлял (есть на главной) — косметическая
|
||||||
|
рассинхронизация, не влияет на факты.
|
||||||
|
- **Формулировка «до 10 сотрудников» (главная) vs минимум 5 в конструкторе** — оставлена
|
||||||
|
(«до 10» — верхняя граница, не ложь).
|
||||||
|
|
||||||
|
## Нужно решение владельца
|
||||||
|
- **Способы оплаты подписки** — реальные методы не определены в продукте; в KB поставил
|
||||||
|
«уточняется / по счёту — свяжитесь с нами». Нужно задать фактические способы.
|
||||||
|
- **Приём платежей конфигурации при регистрации** — сервер `/api/auth/signup` не принимает
|
||||||
|
shops/cashes/warehouses/employees. Нужно решение: расширять ли контракт, чтобы тариф/объём
|
||||||
|
фиксировались при регистрации (сейчас параметры показываются и уходят в запрос, но игнорируются).
|
||||||
|
- **Юридические документы /legal/* и контакты** — НЕ трогались (решение владельца): видны
|
||||||
|
плейсхолдеры «[НАЗВАНИЕ ТОО]», «[БИН]», телефон «+7 (___)», пометка «подлежит правовой
|
||||||
|
экспертизе». Из формы регистрации пользователь соглашается с офертой и политикой ПДн —
|
||||||
|
т.е. эти документы с плейсхолдерами ему предъявляются. Нужны реквизиты/экспертиза.
|
||||||
|
- **Налоги**: оферта 6.3 «с учётом всех налогов»; в FAQ оставлена фраза про счёт-фактуру «с НДС
|
||||||
|
или без» (режим налогообложения клиента) — формально не противоречит, но при желании свести.
|
||||||
|
- **Скидки за предоплату / платные консультации** — убраны как неподтверждённые; вернуть, если
|
||||||
|
такие условия реально есть.
|
||||||
BIN
docs/site-fixes-2026-10-10/d-blog-quickstart.png
Normal file
|
After Width: | Height: | Size: 2 MiB |
BIN
docs/site-fixes-2026-10-10/d-home.png
Normal file
|
After Width: | Height: | Size: 753 KiB |
BIN
docs/site-fixes-2026-10-10/d-import.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
docs/site-fixes-2026-10-10/d-kb-billing.png
Normal file
|
After Width: | Height: | Size: 271 KiB |
BIN
docs/site-fixes-2026-10-10/d-pricing.png
Normal file
|
After Width: | Height: | Size: 596 KiB |
BIN
docs/site-fixes-2026-10-10/d1-signup-business.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
28
docs/site-fixes-2026-10-10/index.html
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!doctype html><meta charset="utf-8"><title>Исправления сайта 2026-10-10</title>
|
||||||
|
<style>body{font:15px system-ui;margin:0;padding:24px;background:#f8fafc;color:#0f172a;max-width:1100px;margin:auto}
|
||||||
|
h1{font-size:22px} figure{margin:0 0 24px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}
|
||||||
|
figcaption{padding:8px 12px;font-size:13px;color:#475569;background:#f1f5f9} img{display:block;width:100%}
|
||||||
|
ul{background:#fff;border:1px solid #e2e8f0;border-radius:10px;padding:14px 32px} li{margin:4px 0}
|
||||||
|
.ok{color:#16a34a} .no{color:#dc2626;font-weight:700}</style>
|
||||||
|
<h1>Исправления публичного сайта test.food-market.kz — этап 1 «Сайт и выбор тарифа»</h1>
|
||||||
|
<p>Проверка после деплоя (2026-10-10T19:01:19.232Z). Пройдено: 10/10.</p>
|
||||||
|
<h2>Автопроверки</h2><ul><li class="ok">✓ signup ?plan=business: тариф «Бизнес» выбран</li>
|
||||||
|
<li class="ok">✓ signup business: цена 15 500 видна</li>
|
||||||
|
<li class="ok">✓ signup business: конфигурация (3 магазина) видна</li>
|
||||||
|
<li class="ok">✓ signup ?plan=start: «Старт» выбран</li>
|
||||||
|
<li class="ok">✓ signup ?plan=xyz: откат на «Старт»</li>
|
||||||
|
<li class="ok">✓ моб. главная: нет горизонтальной прокрутки</li>
|
||||||
|
<li class="ok">✓ моб. шапка: есть кнопка-гамбургер</li>
|
||||||
|
<li class="ok">✓ моб. меню открывается по гамбургеру</li>
|
||||||
|
<li class="ok">✓ моб. меню: пункт «Тарифы» доступен</li>
|
||||||
|
<li class="ok">✓ моб. тап по «Тарифы» → /pricing</li></ul>
|
||||||
|
<h2>Скриншоты</h2><figure><figcaption>Десктоп 1440: /signup?plan=business&shops=3&cashes=4&warehouses=1 — выбран «Бизнес», конфигурация и цена 15 500 ₸</figcaption><img src="d1-signup-business.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Десктоп 1440: Главная</figcaption><img src="d-home.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Десктоп 1440: Тарифы (конструктор «Бизнес»)</figcaption><img src="d-pricing.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Десктоп 1440: Импорт — скриншот каталога продукта (без чужого HR-дашборда)</figcaption><img src="d-import.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Десктоп 1440: KB: Тарифы и оплата (рендер markdown, без front-matter)</figcaption><img src="d-kb-billing.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Десктоп 1440: Блог: быстрый старт</figcaption><img src="d-blog-quickstart.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Моб. 390: Моб. главная 390px — в шапке гамбургер</figcaption><img src="m-home-closed.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Моб. 390: Моб. меню открыто — все пункты + «Войти»/«Начать бесплатно»</figcaption><img src="m-menu-open.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Моб. 390: Моб. /pricing после тапа по «Тарифы» из меню</figcaption><img src="m-pricing.png" loading="lazy"></figure>
|
||||||
|
<figure><figcaption>Моб. 390: Моб. /signup «Бизнес» с конфигурацией и ценой</figcaption><img src="m-signup-business.png" loading="lazy"></figure>
|
||||||
BIN
docs/site-fixes-2026-10-10/m-home-closed.png
Normal file
|
After Width: | Height: | Size: 683 KiB |
BIN
docs/site-fixes-2026-10-10/m-menu-open.png
Normal file
|
After Width: | Height: | Size: 438 KiB |
BIN
docs/site-fixes-2026-10-10/m-pricing.png
Normal file
|
After Width: | Height: | Size: 677 KiB |
BIN
docs/site-fixes-2026-10-10/m-signup-business.png
Normal file
|
After Width: | Height: | Size: 299 KiB |
106
docs/site-fixes-2026-10-10/verify.json
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"name": "signup ?plan=business: тариф «Бизнес» выбран",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "signup business: цена 15 500 видна",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "signup business: конфигурация (3 магазина) видна",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "signup ?plan=start: «Старт» выбран",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "signup ?plan=xyz: откат на «Старт»",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "моб. главная: нет горизонтальной прокрутки",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "моб. шапка: есть кнопка-гамбургер",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "моб. меню открывается по гамбургеру",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "моб. меню: пункт «Тарифы» доступен",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "моб. тап по «Тарифы» → /pricing",
|
||||||
|
"ok": true,
|
||||||
|
"detail": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shots": [
|
||||||
|
{
|
||||||
|
"file": "d1-signup-business.png",
|
||||||
|
"caption": "/signup?plan=business&shops=3&cashes=4&warehouses=1 — выбран «Бизнес», конфигурация и цена 15 500 ₸",
|
||||||
|
"kind": "Десктоп 1440"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "d-home.png",
|
||||||
|
"caption": "Главная",
|
||||||
|
"kind": "Десктоп 1440"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "d-pricing.png",
|
||||||
|
"caption": "Тарифы (конструктор «Бизнес»)",
|
||||||
|
"kind": "Десктоп 1440"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "d-import.png",
|
||||||
|
"caption": "Импорт — скриншот каталога продукта (без чужого HR-дашборда)",
|
||||||
|
"kind": "Десктоп 1440"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "d-kb-billing.png",
|
||||||
|
"caption": "KB: Тарифы и оплата (рендер markdown, без front-matter)",
|
||||||
|
"kind": "Десктоп 1440"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "d-blog-quickstart.png",
|
||||||
|
"caption": "Блог: быстрый старт",
|
||||||
|
"kind": "Десктоп 1440"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "m-home-closed.png",
|
||||||
|
"caption": "Моб. главная 390px — в шапке гамбургер",
|
||||||
|
"kind": "Моб. 390"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "m-menu-open.png",
|
||||||
|
"caption": "Моб. меню открыто — все пункты + «Войти»/«Начать бесплатно»",
|
||||||
|
"kind": "Моб. 390"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "m-pricing.png",
|
||||||
|
"caption": "Моб. /pricing после тапа по «Тарифы» из меню",
|
||||||
|
"kind": "Моб. 390"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "m-signup-business.png",
|
||||||
|
"caption": "Моб. /signup «Бизнес» с конфигурацией и ценой",
|
||||||
|
"kind": "Моб. 390"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
81
tests/e2e/tools/site-fixes-verify.mjs
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { chromium } from '@playwright/test'
|
||||||
|
import { mkdirSync, writeFileSync } 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-10')
|
||||||
|
mkdirSync(OUT, { recursive: true })
|
||||||
|
const results = []
|
||||||
|
const shots = []
|
||||||
|
const ok = (name, cond, detail='') => { results.push({ name, ok: !!cond, detail }); console.log(`${cond?'✓':'✗'} ${name} ${detail}`) }
|
||||||
|
|
||||||
|
const browser = await chromium.launch({ args: ['--ignore-certificate-errors'] })
|
||||||
|
|
||||||
|
// ---------- DESKTOP 1440x900 @2x ----------
|
||||||
|
const dctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2, ignoreHTTPSErrors: true })
|
||||||
|
const d = await dctx.newPage()
|
||||||
|
async function shotD(path, file, caption) {
|
||||||
|
await d.goto(BASE + path, { waitUntil: 'networkidle' }).catch(()=>{})
|
||||||
|
await d.waitForTimeout(600)
|
||||||
|
await d.screenshot({ path: resolve(OUT, file) })
|
||||||
|
shots.push({ file, caption, kind: 'Десктоп 1440' })
|
||||||
|
}
|
||||||
|
// 1. signup business config + price 15 500
|
||||||
|
await d.goto(`${BASE}/signup?plan=business&shops=3&cashes=4&warehouses=1&employees=5`, { waitUntil:'networkidle' }).catch(()=>{})
|
||||||
|
await d.waitForTimeout(1000)
|
||||||
|
ok('signup ?plan=business: тариф «Бизнес» выбран', await d.locator('label:has(input[name="plan"]:checked):has-text("Бизнес")').count() > 0)
|
||||||
|
const cfgText = (await d.locator('div:has-text("Ваша конфигурация"):has-text("Магазинов")').last().innerText().catch(() => '')) || ''
|
||||||
|
const normCfg = cfgText.replace(/[\s\u00a0\u202f\u2009\u2007]/g, '')
|
||||||
|
ok('signup business: цена 15 500 видна', normCfg.includes('15500'))
|
||||||
|
ok('signup business: конфигурация (3 магазина) видна', /Магазинов:\s*3/.test(cfgText))
|
||||||
|
await d.screenshot({ path: resolve(OUT, 'd1-signup-business.png') }); shots.push({ file:'d1-signup-business.png', caption:'/signup?plan=business&shops=3&cashes=4&warehouses=1 — выбран «Бизнес», конфигурация и цена 15 500 ₸', kind:'Десктоп 1440' })
|
||||||
|
// 2. plan=start
|
||||||
|
await d.goto(`${BASE}/signup?plan=start`, { waitUntil:'networkidle' }).catch(()=>{}); await d.waitForTimeout(500)
|
||||||
|
ok('signup ?plan=start: «Старт» выбран', /border-brand[\s\S]{0,120}Старт/.test(await d.content()))
|
||||||
|
// 3. plan=xyz → start
|
||||||
|
await d.goto(`${BASE}/signup?plan=xyz`, { waitUntil:'networkidle' }).catch(()=>{}); await d.waitForTimeout(500)
|
||||||
|
ok('signup ?plan=xyz: откат на «Старт»', /border-brand[\s\S]{0,120}Старт/.test(await d.content()))
|
||||||
|
// desktop screenshots of key pages
|
||||||
|
for (const [p,f,c] of [['/','d-home.png','Главная'],['/pricing','d-pricing.png','Тарифы (конструктор «Бизнес»)'],['/import','d-import.png','Импорт — скриншот каталога продукта (без чужого HR-дашборда)'],['/kb/billing','d-kb-billing.png','KB: Тарифы и оплата (рендер markdown, без front-matter)'],['/blog/quick-start','d-blog-quickstart.png','Блог: быстрый старт']]) await shotD(p,f,c)
|
||||||
|
|
||||||
|
// ---------- MOBILE 390x844 @3x ----------
|
||||||
|
const mctx = await browser.newContext({ viewport: { width: 390, height: 844 }, deviceScaleFactor: 3, ignoreHTTPSErrors: true, isMobile: true, hasTouch: true })
|
||||||
|
const m = await mctx.newPage()
|
||||||
|
await m.goto(BASE + '/', { waitUntil:'networkidle' }).catch(()=>{}); await m.waitForTimeout(700)
|
||||||
|
// no horizontal scroll
|
||||||
|
const noHScroll = await m.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1)
|
||||||
|
ok('моб. главная: нет горизонтальной прокрутки', noHScroll, '')
|
||||||
|
const burger = m.locator('#mobile-menu-btn')
|
||||||
|
ok('моб. шапка: есть кнопка-гамбургер', await burger.count() > 0)
|
||||||
|
await m.screenshot({ path: resolve(OUT, 'm-home-closed.png') }); shots.push({ file:'m-home-closed.png', caption:'Моб. главная 390px — в шапке гамбургер', kind:'Моб. 390' })
|
||||||
|
await burger.click().catch(()=>{}); await m.waitForTimeout(400)
|
||||||
|
const menuVisible = await m.locator('#mobile-menu:not(.hidden)').count() > 0
|
||||||
|
ok('моб. меню открывается по гамбургеру', menuVisible)
|
||||||
|
ok('моб. меню: пункт «Тарифы» доступен', await m.locator('#mobile-menu a[href="/pricing"]').count() > 0)
|
||||||
|
await m.screenshot({ path: resolve(OUT, 'm-menu-open.png') }); shots.push({ file:'m-menu-open.png', caption:'Моб. меню открыто — все пункты + «Войти»/«Начать бесплатно»', kind:'Моб. 390' })
|
||||||
|
await m.locator('#mobile-menu a[href="/pricing"]').first().click().catch(()=>{}); await m.waitForTimeout(800)
|
||||||
|
ok('моб. тап по «Тарифы» → /pricing', m.url().includes('/pricing'))
|
||||||
|
await m.screenshot({ path: resolve(OUT, 'm-pricing.png') }); shots.push({ file:'m-pricing.png', caption:'Моб. /pricing после тапа по «Тарифы» из меню', kind:'Моб. 390' })
|
||||||
|
await m.goto(`${BASE}/signup?plan=business&shops=3&cashes=4&warehouses=1&employees=5`, { waitUntil:'networkidle' }).catch(()=>{}); await m.waitForTimeout(600)
|
||||||
|
await m.screenshot({ path: resolve(OUT, 'm-signup-business.png'), fullPage: true }); shots.push({ file:'m-signup-business.png', caption:'Моб. /signup «Бизнес» с конфигурацией и ценой', kind:'Моб. 390' })
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
|
||||||
|
// index.html
|
||||||
|
const rows = shots.map(s => `<figure><figcaption>${s.kind}: ${s.caption}</figcaption><img src="${s.file}" loading="lazy"></figure>`).join('\n')
|
||||||
|
const checks = results.map(r => `<li class="${r.ok?'ok':'no'}">${r.ok?'✓':'✗'} ${r.name}${r.detail?` — ${r.detail}`:''}</li>`).join('\n')
|
||||||
|
const html = `<!doctype html><meta charset="utf-8"><title>Исправления сайта 2026-10-10</title>
|
||||||
|
<style>body{font:15px system-ui;margin:0;padding:24px;background:#f8fafc;color:#0f172a;max-width:1100px;margin:auto}
|
||||||
|
h1{font-size:22px} figure{margin:0 0 24px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}
|
||||||
|
figcaption{padding:8px 12px;font-size:13px;color:#475569;background:#f1f5f9} img{display:block;width:100%}
|
||||||
|
ul{background:#fff;border:1px solid #e2e8f0;border-radius:10px;padding:14px 32px} li{margin:4px 0}
|
||||||
|
.ok{color:#16a34a} .no{color:#dc2626;font-weight:700}</style>
|
||||||
|
<h1>Исправления публичного сайта test.food-market.kz — этап 1 «Сайт и выбор тарифа»</h1>
|
||||||
|
<p>Проверка после деплоя (${new Date().toISOString()}). Пройдено: ${results.filter(r=>r.ok).length}/${results.length}.</p>
|
||||||
|
<h2>Автопроверки</h2><ul>${checks}</ul>
|
||||||
|
<h2>Скриншоты</h2>${rows}`
|
||||||
|
writeFileSync(resolve(OUT, 'index.html'), html)
|
||||||
|
const failed = results.filter(r=>!r.ok)
|
||||||
|
writeFileSync(resolve(OUT, 'verify.json'), JSON.stringify({ results, shots }, null, 2))
|
||||||
|
console.log(`\n[site-verify] ${results.filter(r=>r.ok).length}/${results.length} passed → ${OUT}/index.html`)
|
||||||
|
if (failed.length) { console.log('FAILED:', failed.map(f=>f.name).join('; ')); process.exit(1) }
|
||||||