# Vitest: девять практических сценариев Проект связывает настоящий тестовый runner и живой интерфейс. `public/model.js` используется и посетителем мастерской, и тестами. `fixed` — исправленная реализация, `broken` — намеренные учебные ошибки. На публичной странице отображаются **записи реальных запусков Vitest**, а не исполняется тестовый runner. Версии: Node 24.15+ в ветке 24 LTS (при подготовке 24.19.0), Vitest / browser providers / coverage-v8 5.1.0, JSDOM 31.0.0. Требование Node обусловлено в том числе JSDOM. Полное дерево фиксируется `package-lock.json`; проверяйте `engines` при обновлении. ## Windows / PowerShell Установите Node 24 LTS и распакуйте проект в отдельную папку. Используйте `npm.cmd`, если PowerShell запрещает запуск `npm.ps1` политикой выполнения. Менять системную политику ради проекта не требуется. ```powershell node --version npm.cmd --version npm.cmd ci npm.cmd test npm.cmd run capture npm.cmd run verify npm.cmd run build-public npm.cmd run serve ``` Откройте http://127.0.0.1:8525/ . Остановка только своего сервера — Ctrl+C. Не открывайте index.html через file://: JSON и ES-модули требуют HTTP. Обычный `npm test` использует fixed. Для ручного просмотра намеренно падающей версии: ```powershell $env:LAB_VARIANT = 'broken' npm.cmd test # Ожидается exit code 1: это учебные ошибки. Remove-Item Env:LAB_VARIANT npm.cmd test ``` Не оставляйте `LAB_VARIANT=broken` в окружении обычного CI. Скрипт capture сам задаёт вариант дочерним процессам и не меняет окружение родительского терминала. ## Linux / Bash Установите поддерживаемый Node 24.15+ из принятого в вашей системе источника. Дистрибутивный пакет с именем nodejs может быть старее: сначала проверьте версию. Для npm ci нужны доступ к registry и доверенные CA; не отключайте TLS. ```bash node --version npm --version npm ci npm test npm run capture npm run verify npm run build-public npm run serve ``` ```bash LAB_VARIANT=broken npm test # намеренно exit 1 npm test # fixed, exit 0 npm run coverage npm run watch ``` Watch удобен локально; `vitest run` завершает процесс после одного запуска и подходит CI. Не запускайте публичный watch-сервер на VPS. Для знакомства по SSH используйте туннель к 127.0.0.1:8525. ## Девять сценариев и границы доказательств 1. `total`: цена в копейках, количество, скидка; граничный ввод и лишняя потерянная копейка. 2. `validateEmail`: test.each, допустимые и недопустимые значения. Это учебная проверка формы, не полный RFC-парсер и не подтверждение владения почтой. 3. `loadCustomer`: await, успешный JSON, HTTP 503, неверные данные. Тест поднимает **настоящий** Node HTTP-сервер на свободном loopback-порту и останавливает его afterAll. На странице fetch загружает настоящий статический JSON, ошибка — отсутствующий файл; это не отдельный backend с базой. 4. `saveOrder`: vi.fn проверяет путь и аргументы API, единицы измерения и нормализацию. Живой пример использует явно обозначенный адаптер без сети. Он не доказывает работоспособность внешнего API; реальный HTTP отдельно проверяет сценарий 3. 5. `debounceSearch`: fake timers проверяют последний запрос и cancel; живой UI использует настоящие таймеры. afterEach чистит таймеры и восстанавливает окружение даже при падающем assertion. 6. `mountContactForm`: JSDOM проверяет сообщение, aria-invalid и фокус. В браузере посетитель может сам заполнить форму. Сохранение учебное, никуда не отправляет контакт. 7. `mountDisclosure`: JSDOM preflight проверяет aria-expanded/Escape/focus. **Настоящий Browser Mode вынесен отдельно**; в исходной записи `browser.status=not-run`, пока он фактически не выполнен. JSDOM не доказывает CSS-геометрию, touch или физическое устройство. 8. `summary`: inline snapshot фиксирует небольшой формат заказа; coverage получен настоящим V8. Измерение процента не доказывает полноту требований. В `deliveryFee` намеренно проверен лишь самовывоз. 9. `canPublish`: any/all и непустой набор. Живой UI проверяет учебное условие; он не выполняет CI/деплой. Скрипт capture отдельно проверяет реальные exit 0 и exit 1 тестового runner. ## Что запускают команды - `npm test`: обычная проверка `tests/scenarios.test.js`, environment JSDOM, по умолчанию fixed. Намеренно failing реализация не делает обычный run красным. - `npm run test:ui`: 15 предметных проверок интерфейса в JSDOM, включая поздний HTTP-ответ, отмену таймера и восстановление из bfcache. Они не доказывают геометрию настоящего браузера или CSP. - `npm run capture`: запускает fixed и broken отдельными настоящими процессами Vitest, требует соответственно exit 0 и 1, затем запускает coverage fixed. Нормализует абсолютный путь проекта в сохранённых отчётах и пишет `public/reports.json`. Никаких придуманных assertion/duration/процентов нет. - `npm run verify`: проверяет SHA-256 исходников, статус зелёного запуска и наличие намеренной ошибки в каждом из девяти сценариев. После изменения входных файлов нужно переснять capture; вручную редактировать зелёные результаты нельзя. - `npm run coverage`: V8 coverage и HTML в `reports/coverage/`. Пороги учебного проекта — минимальный барьер, не обещание качества продукта. Исходник fixed/broken общий, поэтому таблица ветвей включает учебные альтернативы. - `npm run build-public`: формирует сценарии и читаемые `.txt` копии исходников. Не запускает браузер и не подменяет capture. - `npm run serve`: локальный статический Node-сервер public на 127.0.0.1:8525. Выдаёт только файлы внутри public, принимает GET/HEAD. В артефакте capture фиксируются версии runtime и hashes `package.json`, lock, общего модуля, тестов, конфигурации и capture-скрипта. Повторный запуск на другой платформе может иметь другие длительности. Сравнивать следует свойства результата и ожидаемые статусы, а не точное совпадение времён. ## Browser Mode: ручной preview ```powershell npm.cmd run browser:preview ``` ```bash npm run browser:preview ``` Конфигурация `vitest.browser.config.js` использует `@vitest/browser-preview`. Она задаёт `BROWSER=none`: новый браузер автоматически не открывается. Откройте в своём браузере полный URL runner, который напечатает Vitest (API порт 8526, адрес loopback). Provider preview выполняет тесты в настоящем браузерном DOM, но симулирует события; для CI используйте Playwright provider. В Vitest 5 импортируйте `page` и `userEvent` из `vitest/browser`. Старый путь `@vitest/browser/context` для этого проекта не подходит. В обеих browser-конфигурациях установлено `publicDir:false`: тест импортирует общий модуль и CSS из папки `public`, поэтому они должны проходить обработку Vite, а не отдаваться как необработанная статика. Статическую мастерскую по-прежнему запускает отдельная команда `npm run serve`. В закреплённой версии preview 5.1.0 обработчик `advanceTimers` вызывается и при обычных таймерах. Поэтому тест создаёт собственный `userEvent.setup`: для preview он продвигает время только при `vi.isFakeTimers()`, а для Playwright использует стандартную настройку. Это сохраняет настоящие таймеры и проверки клика, фокуса и Escape; ошибка таймеров не скрывается пропуском assertions. После каждого теста очищаются события пользователя и DOM. `tests/disclosure.browser.test.js` проверяет раскрытие панели, aria-expanded, фокус, Escape и отсутствие горизонтального переполнения на 1280/390/296. Это целевая проверка компонента, а не автоматическая приёмка всей публикации. Мастерскую и встроенный iframe нужно проверять отдельно. По окончании остановите только этот процесс Ctrl+C. Отдельный preview-прогон 12 ноября 2026 года прошёл все три проверки. Результат и его границы описаны в [BROWSER-CHECK.md](BROWSER-CHECK.md). Исторический Node/jsdom-отчёт `public/reports.json` сохранён без изменений; его отметка `browser.status=not-run` относится к тому, более раннему запуску. ## Browser Mode в CI / Playwright Для скачивания браузера нужен интернет. Установка browser runtime отличается от установки npm-пакета. На Linux Playwright также нужны системные библиотеки. ```powershell npx.cmd playwright install chromium npm.cmd run browser:ci ``` ```bash npx playwright install --with-deps chromium npm run browser:ci ``` Команда с `--with-deps` может потребовать права для системных пакетов; запускайте её в подготовленном CI-контейнере или выделенном runner, а не обновляйте чужой production-сервер автоматически. `vitest.browser-ci.config.js` использует headless Chromium и такой же тест компонента. В комплекте исходников эта конфигурация подготовлена отдельно; наличие файла не означает, что браузерный прогон уже выполнялся. Смотрите реальные evidence соответствующего запуска. ## CI и Linux/VPS Минимальные шаги pipeline: ```bash npm ci npm test npm run coverage npm run capture npm run verify npm run build-public ``` Намеренно broken run находится внутри capture: script ожидает ненулевой exit и завершится ошибкой, если плохая версия неожиданно стала зелёной. Это не команда `|| true`, скрывающая произвольные сбои. После capture проверяется существование свежего JSON-отчёта; старый raw удаляется перед запуском. Для Browser Mode добавьте установку Chromium и отдельный `npm run browser:ci`. Запускайте CI под отдельным пользователем с ограниченными правами. Тесты — исполняемый код проекта: не запускайте неизвестные pull request с production-секретами. Этот учебный проект не нуждается в токенах, паролях или внешней базе. Логи и HTML coverage храните как CI artifacts, доступные нужной команде. На публичный сайт выкладывается **только public**, не node_modules, runner API и не весь репозиторий. Статический хост должен отдавать `.js` как JavaScript, `.json` как JSON. Веб-сервером может быть Nginx или Caddy; PHP для этой публичной мастерской не требуется, потому что Node/Vitest выполняются на этапе подготовки отчёта. Готовые файлы можно разместить рядом с PHP-сайтом как статические ресурсы. ```nginx server { listen 80; server_name tests.example.com; root /srv/vitest-workshop/public; index index.html; location / { try_files $uri $uri/ =404; } location ~ /\. { deny all; } } ``` Для своего домена настройте HTTPS принятым на сервере способом. `nginx -t` перед reload обязателен; не заменяйте конфигурацию соседних сайтов. Архив `vitest-project.zip` публикуется отдельно; ссылка мастерской ожидает его на один каталог выше public. На чисто локальном `npm run serve` этот внешний ZIP не обслуживается — исходники уже находятся у вас. ## Snapshot и тестовые данные Не обновляйте snapshot только ради зелёного статуса. Сначала выясните, почему изменился контракт: валюта, поле, сортировка, дата или ошибка реализации. В примере RUB→USD является намеренной ошибкой; новый snapshot с USD скрыл бы её. Все данные синтетические, никаких реальных клиентов в fixtures нет. После каждого DOM-теста снимаются обработчики и очищается документ. Fake timers не остаются активными для следующего сценария. Настоящий HTTP-сервер закрывается afterAll. Эти детали важны именно для failing run: cleanup должен работать и после исключения assertion, а не только в последней строке зелёного теста. ## Лицензии и исходники Авторский проект — MIT (`LICENSE`). Полные лицензии установленных npm-зависимостей и Bootstrap находятся в `licenses/` и `THIRD-PARTY-LICENSES.txt`. `node_modules` воспроизводится через npm ci и не требуется в публичной упаковке. `reports/initial.json`, временные raw-отчёты и coverage-файлы с абсолютными локальными путями не следует включать в публичный архив; нормализованные fixed/broken/capture и сводка доступны отдельно. Официальные источники: https://vitest.dev/guide/ , https://vitest.dev/guide/browser/ , https://vitest.dev/guide/coverage , https://vitest.dev/guide/mocking .