Современный frontend давно перестал быть страницей, которую можно объяснить через DOM, сетевые запросы и консоль браузера. Значительная часть поведения интерфейса рождается уже после загрузки: внутри состояния приложения, реактивных зависимостей, цепочек computed-логики, store, runtime-обновлений и внутренних связей между компонентами. При этом основной QA-инструментарий во многих сценариях остаётся почти тем же, каким был в эпоху, когда большая часть логики ещё находилась снаружи.

  • Network показывает транспорт.

  • DOM показывает результат.

  • Console показывает ошибки.

Но пространство между ними остаётся черным ящиком — а именно там сегодня живёт значительная часть frontend-логики. Именно там тестировщик чаще всего вынужден либо догадываться, либо идти к разработчику с классическим: «посмотри у себя локально, что туда реально пришло».

Проблема не в том, что инструментов мало. Проблема в том, что большинство из них построены вокруг браузера прошлого поколения, тогда как frontend уже давно живёт внутри runtime. Именно из этой практической боли появился собственный runtime-инспектор — сначала как консольный скрипт для одной конкретной задачи, а затем как полноценный инструмент, который неожиданно нашел отклик у QA и разработчиков.

Когда привычный дебаг перестал работать

Проект постепенно усложнялся, и в постановках всё чаще начали появляться задачи формата:

  • "подключить ещё один prop"

  • "передать дополнительный параметр в виджет"

  • "проверить реализацию контракта компонента на новые входные данные"

и тому подобное.

Сначала всё выглядело привычно: запросы есть, backend отвечает стабильно, данные одинаковые — но frontend реагирует не так, как должен. Подменяешь ответ через proxy — реакции нет. Смотришь Network — payload тот же. DOM отрисован. А причина где-то исчезает между запросом и компонентом.

Так началось первое серьёзное знакомство с внутренним устройством Vue-приложений и взаимодействием микрофронтов между собой.

Первая попытка: официальное решение, которое не решает задачу QA

Первая логичная мысль была очевидной: если разработчики как-то видят внутреннее состояние приложения, значит инструмент уже существует. Так состоялось знакомство с Vue Devtools — если через него видно props, состояние компонентов и store, значит проблема решена.

Но вместо ожидаемого инструмента появилось короткое сообщение:

Vue.js is detected on this page.
Devtools inspection is not available because it's in production mode or explicitly disabled by the author.

После короткого разговора с frontend-разработчиками выяснилось: стандартный инструмент полноценно работает только в localhost, все остальное воспринимается как production-сборка, и изменить это мы не можем. Тестировщик снова оказывается в знакомой ситуации: данные существуют, приложение их использует, доступа нет.

Главный вывод того вечера

В какой-то момент пришла простая мысль: если данные уже живут внутри frontend runtime, значит они физически существуют. Остаётся только понять, где именно они лежат.

Несколько вечеров ушло просто на исследование: как Vue хранит ссылки на компоненты, где живут props, как получить доступ к экземплярам и как не сломать страницу в процессе. Так появился первый console script — очень простой, нестабильный, но он уже умел главное: находить props, показывать их и менять значения прямо во время работы страницы!

Это изменило саму механику тестирования. Впервые появилась возможность не просто смотреть на результат, а работать с внутренним состоянием компонента напрямую. В этот момент стало понятно: я больше не тестирую вслепую.

Почему script быстро перестал быть временным решением

Сначала это выглядело как локальный инструмент под одну-две задачи. Но Vue в проекте никуда не исчезнет, сценариев становится больше, а console script на несколько тысяч строк уже упирается в свои ограничения:

  • неудобный запуск;

  • ручная вставка;

  • тонна багов;

  • неудобный интерфейс;

  • постоянная борьба с обновлением страницы.

А значит что скрипт уже переходит в формат инструментов и его необходимо поддерживать. Так появилась следующая очевидная точка роста — browser extension.

Когда выяснилось, что props — это только половина картины

Во время переноса в расширение один из frontend-разработчиков вскользь упомянул: «props — это полезно, но часть поведения вообще определяется store». Так произошло знакомство с Pinia — снова без готовой QA-модели использования, только короткий локальный пример и понимание, что есть ещё один слой данных, который тестировщик снова не видит.

Дальше снова началось исследование: как Pinia хранит state, как читаются getters, как вызываются actions, как безопасно вмешиваться в store. Через несколько недель появился второй рабочий слой инструмента: читать state, изменять state, проверять getters, вмешиваться в actions. И стало понятно ещё кое-что: frontend-диагностика без store сегодня уже почти неполноценна.

Когда инструмент вышел за пределы личного использования

На этом этапе расширение впервые ушло в тестовый оборот внутри QA-команды. Изначально ожидания были скромными: возможно, кому-то пригодится несколько функций, которые уже помогают лично мне. Но реакция оказалась гораздо сильнее — это была не локальная история одного человека, а накопившаяся боль всего тестового chapter.

Практически у каждого были свои обходные способы: чаще всего просто принимали поведение интерфейса как есть, если внешне всё выглядело корректно, либо подключали frontend-разработчиков, чтобы локально проверить состояние приложения у них.

Даже задачи, которые в нормальной ситуации можно решить через инструмент и просмотр Pinia store, обычно закрывались гораздо тяжелее: через подмену трафика или вообще через обращение к DevOps с просьбой временно выдать дополнительные права. Сам инструмент появился скорее как попытка убрать этот накопившийся операционный шум и дать QA прямой доступ к runtime-состоянию приложения.

После первых недель использования расширение начали встраивать в реальный flow тестирования. Вместе с этим пришла самая полезная часть любой внутренней разработки — эксплуатационная обратная связь. Она быстро показала слабые места: нет смысла вычитывать статичные сайты, если runtime отсутствует — постоянный обход создаёт лишнюю нагрузку. Пришлось серьёзно заниматься оптимизацией: уменьшать CPU-нагрузку, пересматривать циклы обхода, убирать лишнюю активность в фоне.

Почему версия 2.0.0 началась с нового пространства, а не с новой функции

Переход к версии 2.0.0 начался не с добавления новых возможностей, а с накопившихся ограничений первой архитектуры. Изначально расширение работало в стандартном popup-формате браузера: этого хватало, пока сценарии оставались простыми. Но в реальном использовании довольно быстро проявились системные проблемы.

Popup всегда был жёстко привязан к верхнему правому углу браузера и имел фиксированный размер. Пока речь шла о коротких данных, это ещё оставалось терпимым, но как только в работе появились большие JSON-структуры, store-состояния, props и вложенные данные, читать и редактировать их стало неудобно. Часть интерфейса постоянно упиралась в ограничения самого контейнера, а полезное пространство расходовалось неэффективно.

Дополнительно сама popup-архитектура начала создавать технические проблемы: именно этот режим оказался одной из причин лишней CPU-нагрузки и нестабильного поведения при постоянной работе с runtime-данными.

Поэтому в 2.0.0 инструмент переехал в iframe внутри страницы. Это решение сразу сняло несколько ограничений: появилось нормальное рабочее пространство, исчезла жёсткая привязка к размеру popup, стало возможно строить уже не вспомогательную overlay-панель, а полноценное runtime-приложение внутри страницы.

Когда установка сама стала техническим ограничением

После пересборки интерфейса быстро проявилось ещё одно ограничение — уже не архитектурное, а инфраструктурное. Формально расширение оставалось обычным browser extension: установил и работаешь. Но в реальной корпоративной среде это оказалось не универсальным сценарием.

Во многих рабочих окружениях установка расширений ограничена политиками безопасности, и для части QA это автоматически делало инструмент недоступным независимо от его полезности.

Именно поэтому в 2.0.0 появился отдельный режим запуска через bookmark — без установки расширения. По сути это тот же runtime-инструмент, но запускаемый напрямую в контексте страницы.

Неожиданно именно этот режим стал одним из самых востребованных: значительная часть пользователей работала на корпоративных машинах, где extension установить было нельзя. В итоге bookmark-режим решил проблему доступа для той аудитории, которая раньше вообще выпадала из использования инструмента.

Это хорошо показало практическую вещь: иногда ограничения инфраструктуры влияют на распространение инструмента сильнее, чем новые функции.

Когда внутри появился Network

Отдельной постоянной болью долго оставалась работа с сетевыми запросами. Формально решение в индустрии существует давно: Charles Proxy, Fiddler и другие proxy-инструменты закрывают большую часть задач. Но в ежедневной практике всё выглядело заметно сложнее.

Сначала основная проблема была даже не в самих возможностях, а в пороге входа: установка, настройка сертификатов, отладка окружения, борьба с локальными ограничениями. Если потратить несколько вечеров, инструмент действительно можно было довести до стабильного состояния и использовать в работе.

Но со временем фронтовая архитектура изменилась, и proxy стал вести себя всё менее предсказуемо. В какой-то момент лучший сценарий использования Charles Proxy выглядел почти абсурдно: запустить, быстро нажать одну кнопку, получить нужную подмену и сразу выключить, потому что в противном случае легко словить каскад CORS-ошибок и сломать тестовый сценарий.

Из-за этого proxy постепенно перестал восприниматься как рабочий инструмент команды. Новые QA регулярно спрашивали у Lead QA, используется ли он вообще внутри компании, и обычно слышали ответ, что инструмент не прижился. При этом сам функционал по-прежнему оставался необходимым: запросы всё равно приходилось перехватывать для подмены и воспроизведения редких сценариев.

Именно здесь идея оказалась почти очевидной: если расширение уже работает внутри runtime приложения, понимает frontend и поддерживает внутреннее состояние, то логично закрыть и эту часть работы внутри того же инструмента.

Так появился собственный Network-блок: сначала как базовый просмотр fetch и XMLHttpRequest, затем появились breakpoint перед отправкой, редактирование payload, mock response, form-data и генерация коллекций для Postman.

В этот момент инструмент перестал быть просто runtime-инспектором: в одной точке начали собираться сценарии, которые раньше были размазаны между несколькими внешними программами.

Когда инструмент увидели frontend-разработчики

До появления Network это воспринималось скорее как QA-инструмент. После появления полноценного traffic interception реакция изменилась. Если раньше были шутки уровня «не знаю, сможем ли мы тебя оставить в живых, ты слишком много знаешь», то дальше появились уже другие вопросы: «Подожди… как ты вообще это сделал?» и «Это как внутри работает?»

В нескольких локальных сценариях часть frontend-разработчиков просто переставала открывать Vue Devtools и переключалась на расширение со словами: «У тебя удобнее». Это был довольно неожиданный тип обратной связи — инструмент рождался как попытка закрыть blind zone тестирования, а не как альтернатива developer tooling. Но удобство работы с runtime в production-сценарии начало выигрывать даже там, где у разработчика уже есть привычный стек. Дальнейшие отзывы всё больше показывали, что инструмент перестал быть просто пет-проектом, а стал полноценным рабочим средством, выигрывая удобством входа и повседневного использования.

Что умеет инструмент сейчас

Props: читать и редактировать props, фильтровать компоненты, работать с favorites и blacklist, обновлять данные без перезагрузки.

Pinia Store: читать и менять state и getters.

Network: видеть fetch/XHR, ставить breakpoint, подменять response, сохранять правила, генерировать Postman collections.

Режимы запуска: browser extension, iframe, DevTools, bookmark без установки.

Главный вывод

Если данные уже живут в браузере, вопрос обычно не в том, доступны ли они. Вопрос в том, насколько глубоко можно зайти внутрь runtime, чтобы перестать тестировать поведение интерфейса только через внешний результат.

Frontend давно перестал ограничиваться DOM, Network и консолью: значительная часть логики живёт внутри состояния приложения, реактивных связей и внутренних переходов между компонентами. И пока QA работает только вокруг payload и отрисовки, часть поведения неизбежно остаётся скрытой.

Во многом этот инструмент появился из довольно простого внутреннего отказа мириться с ситуацией, где тестировщик вынужден принимать часть поведения системы на веру только потому, что стандартные инструменты туда не дотягиваются. Такой формат тестирования мне всегда казался скорее вынужденным компромиссом, чем нормальной рабочей моделью.

В какой-то момент один из frontend-разработчиков в команде сформулировал это даже точнее: «Похоже, в какой-то момент ты просто забыл, что ты не разработчик» 😄

Если интересно посмотреть, как это выглядит вживую или попробовать в работе, инструмент доступен здесь.