Таблица расхождений 2026:
Vitest + jsdom против Safari/WebKit на удалённом Mac
Vitest с окружением jsdom — быстрый и дешёвый слой для логики и изолированных компонентов, но это не Safari и не WebKit. Для решений по совместимости в 2026 году командам нужна явная матрица расхождений (события, шрифты, медиа, хранилища, композитинг) и повторяемая приёмка в три шага перед релизом — особенно когда «истинный» браузер доступен только через удалённый Mac. Ниже — сжатый чек-лист и исполняемый процесс; навигация: блог, помощь, материалы про Safari Web Inspector и Playwright WebKit.
01 Обзор дифференциальных рисков
Ошибка стратегии звучит просто: «юниты зелёные — значит, Safari тоже ок». jsdom эмулирует подмножество веб-платформы в Node.js и не гоняет ваш код через движок WebKit. В итоге вы проверяете согласованность с моделью DOM/JS в памяти, а не с реальным рендерингом, политиками приватности, жизненным циклом медиа и тонкостями событий. Для фронтенда и fullstack это означает разделение уровней: Vitest — контракты логики, границы модулей, чистые хуки; WebKit — визуальное поведение, скролл, фокус, жесты, шрифты и сеть на уровне браузера.
На удалённом Mac выравнивается среда: тот же darwin, нативный Safari и установленный через Playwright WebKit совпадают с тем, что ожидает продакшен для пользователей Apple. Это снижает расхождение «Linux CI + только Chromium» vs «реальные клиенты в Safari». Связка с чек-листом Node/npm и Safari описана в отдельной статье; здесь фокус — именно граница Vitest/jsdom и WebKit.
При выборе инструментов закрепите простое правило для код-ревью: если изменение затрагивает визуальный контракт, жизненный цикл фокуса, политики хранения или цепочку событий ввода, оно не может считаться «закрытым» только зелёным прогоном в jsdom. Альтернативные окружения вроде happy-dom иногда быстрее, но не отменяют необходимости WebKit-слоя — они лишь меняют набор заглушек. Для fullstack-команд полезно явно назвать в Definition of Done пункт «проверено в Safari или в проекте Playwright webkit на macOS», иначе дефекты откладываются до пост-релизного саппорта.
- Ложный проход: тест прошёл в jsdom, в Safari падает вёрстка, фокус или Storage.
- Ложный провал: тест ломается из‑за неполной эмуляции API в jsdom, хотя в браузере всё корректно — без моков с контрактом тратите время на шум.
- Непереносимость CI: тяжёлые E2E на WebKit без выделенного macOS-узла откладываются «на потом» и выходят в релиз необстрелянными.
02 Сравнительная таблица: ключевые API и рендеринг
Используйте таблицу как рабочую матрицу при планировании тест-пирамиды: где достаточно Vitest, а где обязателен слой Safari/WebKit на Mac (локально или на удалённом Mac в CI).
| Область | Vitest + jsdom (ожидание) | Safari / WebKit (риск) | Практика |
|---|---|---|---|
| События (click, pointer, wheel) | Синтетические события, упрощённая модель всплытия/compose | Pointer Events, жесты трека, задержки тач/scroll chaining, особенности focusin | Критичный UI — смоук в Safari или Playwright webkit; в Vitest проверяйте только обработчики данных |
| Шрифты и текст | Нет реального рендера глифов, метрики layout приближены | Core Text, сглаживание, kerning, fallback при FOUT/FOIT | Визуальные регрессии и переносы — вне jsdom; на Mac сверяйте эталонные экраны |
| Медиа (video/audio) | Часто заглушки или минимальная реализация | Кодеки, autoplay-политики, fullscreen, PiP, батарейные ограничения iOS | E2E или ручной прогон на целевых устройствах; не опирайтесь на jsdom для «canplay» |
| Storage (localStorage, IndexedDB, cookies) | Память процесса, нет квот и ITP | ITP, приватные вкладки, эвикция, различия SameSite в краевых кейсах | Тестируйте ветвление «Storage недоступен»; приёмка в реальном Safari |
| Layout / CSS | Нет полного движка стилей (container queries, subgrid — по версии jsdom) | Реальный каскад, скролл-снапы, sticky, env(safe-area-inset-*) | Слой визуальных тестов или ручной чек на iPhone/Mac |
| Таймеры и микрозадачи | fake timers, быстрый цикл | Реальные задержки RAF, троттлинг вкладки, энергосбережение | Интеграционные сценарии с реальными таймерами на WebKit |
| Fetch / CORS / cookies | Часто моки fetch; нет настоящего сетевого стека |
Строгие заголовки, preflight, различия кэша | Контракт API в Vitest; сквозной путь — стенд + Safari или WebKit E2E |
Итог: jsdom — про изоляцию и скорость; WebKit — про доверие к релизу для аудитории Apple. Таблица помогает не смешивать эти уровни в одном «зелёном» статусе.
03 Процесс проверки: локально vs нативный WebKit
Чтобы превратить матрицу в решение, зафиксируйте короткий поток без пропусков. Локально (или на общем runner) Vitest остаётся первым фильтром. Удалённый Mac подключается тогда, когда нужен нативный WebKit без покупки железа каждому разработчику.
- Шаг A — Контракт слоя Vitest: запустите
vitest runс зафиксированнымpool/threadsкак в CI; пометьте тесты, которые сознательно мокают браузерные API, тегом или каталогом (*.unit.*). - Шаг B — Нативный WebKit: на macOS-узле выполните узкий набор Playwright с
project: webkitпо критическим пользовательским потокам (логин, оплата, медиа, формы). Сохраняйте trace при падении. Ручная отладка — через Web Inspector. - Шаг C — Смоук в реальном Safari: на стейджинге пройдите чек-лист из 10–15 пунктов: холодный старт, перезагрузка, приватная вкладка, мобильный User-Agent или реальное устройство, одна критическая форма и один медиасценарий. Сверка с выбором среды — устройство vs симулятор vs облако.
Деплой и проверка сборки на удалённом Mac с упором на Safari — в гайде Vite/Webpack и три шага верификации.
04 Рекомендации по параметрам CI
На Apple Silicon узле Vitest любит съедать CPU; WebKit E2E добавляет диск и I/O. Держите разделение job’ов: быстрый vitest отдельно от playwright test --project=webkit, чтобы кэшировать зависимости независимо и не убивать очередь.
- Vitest: задайте
VITEST_MAX_THREADSилиpoolOptions.threads.maxThreadsс запасом под sshd и ОС; избегайте «все ядра» на малой аренде. - Playwright WebKit: один раз на образ выполните
npx playwright install webkit; закрепите версию в lockfile и документации runner’а. - Артефакты: при падении E2E прикладывайте trace, скриншот и последние строки лога — шаблон разбора совместим с гайдом по триажу логов.
- Повторы: ретраи только для сетевых флапов; стабильные падения WebKit не маскируйте — иначе матрица теряет смысл.
05 FAQ
В: Достаточно ли зелёного Vitest с jsdom для релиза в Safari?
О: Нет как единственного критерия. Добавьте слой WebKit (авто или ручной смоук), иначе типичные расхождения по событиям, шрифтам и Storage останутся невидимыми до продакшена.
В: Чем Playwright WebKit на удалённом Mac отличается от ручного Safari?
О: Регрессия и артефакты в пайплайне против UX и редких железячных багов. Оптимально сочетать узкий E2E и короткий ручной проход перед релизом.
В: Стоит ли мокать matchMedia и ResizeObserver в Vitest?
О: Для логики — да, с явными значениями; для утверждений о вёрстке — переносите в WebKit или визуальные тесты на Mac.
В: Почему localStorage в тестах «работает», а в Safari при приватном режиме ломается?
О: jsdom не моделирует политики Safari. Пишите защитный код и проверяйте сценарии без Storage на реальном браузере.
В: Как ускорить Vitest в CI без ложных проходов?
О: Ограничьте потоки, разделите job’ы, кэшируйте зависимости и браузеры, зафиксируйте версии Node и macOS на узле.
Vitest и jsdom остаются лучшим ускорителем для логики, но не заменяют Safari и WebKit. Матрица API/рендера, три шага приёмки и отдельный macOS-job для WebKit превращают совместимость из надежды в процесс — особенно когда команда опирается на удалённый Mac как на единый эталон Apple.
Ещё по теме Safari, Node и автоматизации:
Нативный macOS для Safari, Playwright и приёмки без своего железа
Оформите аренду Mac Mini M4 без обязательного входа в личный кабинет: тарифы на странице тарифов, быстрый старт и доступ — в центре помощи, заказ — на странице покупки. Вернитесь в блог или на главную, чтобы собрать полный контур тестов Safari.