Учебник веб-разработки
Разделы учебника
На этой странице

X. Инструменты и проверки

Проверки, TypeScript, Oxlint, Oxfmt и Jest

Для введения начните с инструментов разработки и лаборатории проверок. Здесь перечислены команды и покрытие проекта.

Оглавление

Что выполняет pnpm check

Порядок закреплён в root package.json:

pnpm build
  → pnpm typecheck
  → pnpm lint
  → pnpm format:check
  → pnpm test

Команды связаны &&: при ошибке следующие не выполняются. Успешный check означает успех этих конкретных проверок, а не готовность всей production-инфраструктуры.

КомандаСмыслЧто не входит
buildРекурсивная сборка contracts, web, docsStrapi
typecheckПроверка типов workspace, Next typegenHTTP runtime-данные
lintOxlint с ошибкой на warning в apps packagesПолный lint CMS/Python/Bash
format:checkOxfmt в корне с ignore patternsПроверка смысла программы
formatИзменяет форматированиеНе readonly-проверка

Strapi проверяется npm ci, schemas, tsc и Node tests в CI check; admin/runtime image дополнительно собирается в job images для main. Root pnpm check остаётся workspace-проверкой.

Конфигурации TypeScript

Workspace использует typescript@7.0.2 и команду tsc; отдельного пакета @typescript/native-preview в manifests нет. Вопрос «ts-go» здесь не означает замену Node.js на Go: проверка типов, генерация JavaScript и runtime — разные этапы.

ПриложениеConfigВывод
ContractsBase + declarationESM JavaScript и .d.ts в dist
WebNext config, bundler resolution, noEmitNext сборка, TypeScript проверяет
UIKitJSX, bundler resolution, noEmitПроверка React-пакета; Storybook отдельно
DocsNext typegen, noEmit, .ts importsПроверка рендерера и Node-тестов
StrapiСобственный TS 5 и configСамостоятельная сборка CMS

Web использует alias @/* и generated route types Next. Нативный Node-запуск учебного скрипта не использует этот alias автоматически. skipLibCheck уменьшает проверку деклараций зависимостей; он не выключает strict для нашего кода.

Node 24 dev-запуск .ts не заменяет tsc --noEmit. Не переносите TypeScript-код, требующий преобразований/путевых aliases, в native runtime без проверки обоих режимов.

Почему Jest

Jest подключён через next/jest.js: Next настраивает трансформацию тестового кода. Environment — node, match — src/**/*.test.ts. Отдельного Vite-конфига нет. В проекте сознательно не установлены Vitest и автоматические UI-тесты Playwright.

У apps/standby свой Vite build для страницы ожидания, без Vitest. pnpm --filter @atmanki/standby test через Node test runner проверяет опрос: ожидание → готовность, некорректный HTTP 200, удаление/TTL, сбои сети и отмену. HTTP-тест диспетчера проверяет независимый от контейнеров HTML, JSON-статус, запрет кеширования и автоматического повтора POST/PUT на независимой HTML-фикстуре. Сборка самой SPA проверяется отдельно: pnpm --filter @atmanki/standby build. Новая конфигурация node-checks выполняет обе проверки; прежний main может запускать Python-тесты до сборки workspace.

Alias @/ отображается на src. Contracts в тестах отображается на исходный .ts, а в production импортируется из dist. Поэтому тест не гарантирует правильность упаковки production-пакета; это дополнительно проверяет build/Docker.

Какие тесты есть

Серверные Jest suites проверяют router, webhook, CMS pagination/contracts и media proxy. Нет live CMS в unit tests: repository/fetch заменяются контролируемым transport fixture. Тесты проверяют полную pagination, отказ второй страницы, безопасные URL, nullable endsAt, null → NOT_FOUND, отказ CMS → INTERNAL_SERVER_ERROR, фиксированный Garage Host, запрет redirect/неверного MIME/traversal и отсутствие credentials в static uploads.

Изолированный Strapi запускает Node tests после собственного tsc: преобразование HTML, исходные даты, повтор расписания, сохранение правок, explicit update, dry-run без базы, повтор после сбоя media-каталога и ожидание post-commit callbacks. Эти проверки выполняет CI check отдельным npm ci/test:import; сборку admin/runtime проверяет Docker job images.

npm --prefix infra/strapi ci --no-audit --no-fund
npm --prefix infra/strapi run check:models
npm --prefix infra/strapi run test:import
pnpm check

Unit green не заменяет настоящий upload/read/delete, import/rerun, restore drill или обычную публикацию в Content Manager. Для UI используются ручные сценарии.

Команды для разработки

pnpm test
pnpm --filter @atmanki/web test --watch
pnpm --filter @atmanki/web exec jest src/server/router.test.ts --runInBand
pnpm lint
pnpm format:check

После изменения contract выполняйте build/typecheck обоих потребителей. После изменения только Markdown достаточно проверить ссылки и примеры; сборка всех приложений не проверит качество объяснений.

Что проверять вручную

UI: мобильная ширина, keyboard navigation, Drawer, accordion, карусель, детальная страница, missing slug, изображения и шрифт. Датированные визуальные сравнения не подтверждают актуальный UI после последующих изменений.

Инфраструктура: config --quiet, сборка образов, healthchecks, доступ через Caddy, авторизованный вход в панели и обновление страниц через Strapi webhook. node scripts/check-static-site.mjs проверяет SSG/ISR на production standalone-сервере с изолированной CMS; pnpm check требует доступной CMS для статической сборки. Сделать mock-тест зелёным и сделать реальную задачу работающей — разные результаты.

Audit и качество

cd infra/strapi
npm audit --omit=dev

У корневого check нет автоматического audit gate. Предыдущая зафиксированная проверка CMS от 2026-10-02 сообщала 22 замечания, 4 high; текущий результат может меняться вместе с базой advisory. Не применяйте несовместимые major-upgrades транзитивных библиотек без проверки Strapi.

Где смотреть код

Router tests, webhook tests, Jest config, Oxlint, Oxfmt, base TS.

Garage и upload provider

При добавлении S3 отдельно проверены сборка CMS и upload/read/delete через @strapi/provider-upload-aws-s3@5.56.0 с Garage 2.3.0, включая multipart 6 MiB и чтение через Caddy. Это отдельная integration-проверка, не доказательство покрытия серверными Jest suites и не доказательство переноса редакционного контента. Детали.

Скрипт медиабиблиотеки

python3 -m unittest discover -s scripts/tests -p 'test_media_library.py' проверяет сохранение редакторских полей, конфликт плана, атомарный журнал, отказ от чужого журнала, продолжение после потерянного ответа и повторный запуск без записей. Эти проверки работают с поддельным клиентом и запускаются в GitHub CI без production-доступов. Jest проверяет ревалидацию по событиям записей и медиа.

Проверка документационного сайта

pnpm --filter @atmanki/docs test
pnpm --filter @atmanki/docs typecheck
pnpm docs:build
node apps/docs/scripts/check-export.mjs

Node-тесты проверяют реестр глав и преобразование ссылок после переноса файлов. Сборка docs — целевая локальная проверка документации; она не собирает web-образ и не требует CMS. Проверка экспорта покрывает внутренние ссылки, MDX и русскую 404. GitHub deploy.yaml и SourceCraft node-checks отдельно запускают тесты реестра до проверки сайта. В docs-образе тесты и экспорт проверяются при сборке в CI. Root pnpm test по-прежнему запускает только web-тесты; эти области не следует смешивать.

Проверка наблюдаемости

python3 -m unittest discover -s scripts/tests -p test_caddy_config.py
python3 -m unittest discover -s scripts/tests -p test_observability_provisioning.py
pnpm --filter @atmanki/web exec jest src/lib/observability.test.ts --runInBand

Тесты проверяют сохранение маршрутов и credentials, отказ от чужого владельца и privileged роли, фильтрацию чувствительных полей и сохранение связи с source map. bash scripts/check-observability.sh выполняется в CI: создаёт изолированные контейнеры/volumes, проверяет Compose, Prometheus, повторную подготовку баз, миграции приёмников и provisioning Grafana, затем удаляет только свои fixture-объекты. Он не должен запускаться против production Docker environment.

После выпуска отдельно проверяются реальные посещения, исходное место browser/server ошибки и свежие метрики. Source maps могут быть совместимы по типам API и при этом не соответствовать конкретному JS-файлу. Telegram не входит в критерии выпуска.

Контракт первого пилота SourceCraft проверяется без Docker и токенов:

python3 -m unittest discover -s scripts/tests -p test_sourcecraft_pilot.py

Тесты проверяют запрет локальной/PR-публикации, точный SHA и registry digest. Реальный CI, registry push/pull и HTTP на VPS проверяются по порядку пилота; локальные тесты их не заменяют.

Полные окружения

python3 -m unittest discover -s scripts/tests проверяет delivery manifest, свежесть SourceCraft control, provider/generation, TTL, cold/wake и LRU. node --test infra/strapi/scripts/tests/*.test.mjs включает published-only export/import, remapping media/relations, checksum/model fingerprint, отказ перезаписи редакторских данных и изолированную выдачу доступов. Локальные тесты не заменяют SourceCraft CI, экспорт production snapshot и проверки живых адресов всех сервисов.

Быстрые проверки обновления CMS заполненного собственного PR:

python3 -m unittest discover -s scripts/tests -p test_preview_upgrade.py
python3 -m unittest discover -s scripts/tests -p 'test_preview*.py'

Они проверяют сбой каждой фазы clone transaction, неопределённый результат commit, forward-only recovery, сохранение строк и кратности, binding для настоящего Runtime.wake, блокировку cold/wake/GC при pending intent и адресное удаление по owner labels. Docker boundary здесь заменён моделями; сохранность настоящей Strapi/PostgreSQL/Garage подтверждается отдельно контейнерным CI drill.

scripts/tests/integration_preview_upgrade.py --cms-image <local-ci-image> — отдельный сценарий сохранности populated PR. Его запускают только с SOURCECRAFT_CI=true на native Docker worker после сборки реального CMS image. Он использует отдельный Sandbox для начального наполнения, создаёт собственный pr-N, добавляет реальный content type upgrade-probe и endpoint /upgrade-proof, который читает новую таблицу upgrade_probes, и проверяет клонирование, сохранность строк/медиа/admin, прерывание после commit, forward recovery, новую редакторскую запись, cold wake и отказ с rollback при удалении draft на клоне. Registry digest/revision boundary заменена локальными CI tags; сами контракты извлекаются из образов. HTTP companion этого drill обслуживает остальные приложения и читает настоящую CMS. Он не подтверждает настоящий Next.js или Caddy.

Вызов этого сценария подключается внутри CI orchestration до удаления переданного CMS image; этот файл сам не публикует образы и не ходит на VPS. Локальные тесты test_preview_upgrade_drill.py проверяют запрет запуска вне CI и cleanup, но успешный реальный container run здесь ещё не подтверждён. При изменении модели или migration files конкретного PR дополнительно требуется сценарий сохранности именно этой схемы; добавление одного нового content type не доказывает произвольную автоматическую миграцию схемы. До миграции drill проверяет отсутствие новой таблицы, после неё — созданную строку, а после recovery — ответ API из этой таблицы. Старые модели и строгая проверка всех прежних строк, включая настройки редактора, сохраняются.

Временные Docker helpers регистрируются в journal до запуска, получают точное имя и owner labels. Rollback/recovery сначала останавливает и адресно удаляет эти контейнеры, затем подтверждает отсутствие остатка, и лишь после этого возобновляет прежних writers. Ошибка inventory/inspect/удаления сохраняет закрытый gate; cleanup в CI продолжает остальные категории и завершает сценарий ошибкой. Контрактные тесты моделируют timeout после принятого Docker create/start, падение до ответа, чужого владельца и недоступный inventory. Отдельный тест настоящего локального процесса подтверждает завершение всей группы Docker-клиента при timeout; Docker контейнеры он не запускает.

PR preservation drill читает серверный CMS-контракт из source/compiled деревьев, включая присутствующие migrations. Экспорт админки dist/build в этот контракт не входит, как и при production image inspection. При ошибке drill выводит фиксированный этап операции, тип исключения и путь/строку известного исходника. Сообщения исключений, строки кода, аргументы команд, env, SQL и закрытый вывод контейнеров не печатаются. Ошибка очистки не скрывает диагностические метки предыдущей ошибки. Эти сведения помогают выбрать следующую проверку, но сами по себе не подтверждают прохождение миграции или сохранность данных.

CLI drill устанавливает обработчик TERM до создания ресурсов: первая отмена запускает finally, повторная не прерывает очистку. Намерение создать временный image записывается до Docker build; cleanup сверяет точный тег и owner label, затем повторяет inventory. Незавершённая сборка без тега не считается утечкой. Локальный тест проверяет отмену между командами и повторный TERM во время очистки через настоящий дочерний процесс. Он не доказывает завершение Docker cleanup до принудительного SIGKILL внешнего runner: контейнерная проверка остаётся в CI.

Учебный backup/restore drill

Быстрая локальная проверка без Docker и секретов:

python3 -m unittest discover -s scripts/tests -p 'test*backup*.py'
python3 -m unittest discover -s scripts/tests -p 'test*refresh*.py'

Вторая команда включает test_refresh_drill_cleanup.py: сохранение чужих ресурсов, отказ при несовпадении ownership, продолжение очистки после ошибки удаления и свежую проверку нулевого остатка. Ошибка inventory не считается пустым списком. Эти тесты моделируют Docker boundary и не доказывают реальный restore.

Контейнерный сценарий scripts/tests/integration_test_refresh.py запускается только в SourceCraft checks → check-task/test-data-drill на точном SHA. Настоящие PG/Garage/Strapi проверяют backup/restore, reset, rollback после ошибки импорта и cold restart; HTTP companions заменяют остальные приложения. Критерий успеха включает exit 0, нулевой Docker остаток своего drill и удалённый временный каталог. Порядок и границы упражнения отделяют его от production backup и восстановления VPS. Локальная зелёная проверка не означает, что этот контейнерный CI run уже прошёл.