惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Troy Hunt's Blog
Scott Helme
Scott Helme
T
Threat Research - Cisco Blogs
T
Tenable Blog
L
LINUX DO - 热门话题
V
Visual Studio Blog
I
Intezer
Blog — PlanetScale
Blog — PlanetScale
Cisco Talos Blog
Cisco Talos Blog
A
Arctic Wolf
C
Cyber Attacks, Cyber Crime and Cyber Security
F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
N
Netflix TechBlog - Medium
SecWiki News
SecWiki News
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Project Zero
Project Zero
W
WeLiveSecurity
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
Recorded Future
Recorded Future
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
S
Securelist
Spread Privacy
Spread Privacy
L
LangChain Blog
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
MongoDB | Blog
MongoDB | Blog
Google DeepMind News
Google DeepMind News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
罗磊的独立博客
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Last Watchdog
The Last Watchdog
Attack and Defense Labs
Attack and Defense Labs
博客园 - 司徒正美
Help Net Security
Help Net Security
L
Lohrmann on Cybersecurity
人人都是产品经理
人人都是产品经理
Forbes - Security
Forbes - Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
PCI Perspectives
PCI Perspectives
博客园 - 【当耐特】
T
Tor Project blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как ручное тестирование вскрывает дефекты в логике интерфейса: 5 кейсов Modus BI
Alek_Che (Mo · 2026-05-09 · via Все публикации подряд на Хабре

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

В сложном веб-интерфейсе пользовательское действие редко заканчивается на одном контроле. Клик меняет состояние. Состояние формирует параметры. Параметры уходят в связанный компонент или в запрос. Компонент пересчитывает данные, обновляет разметку, перерисовывает таблицу, диаграмму или модальное окно. Если один шаг в этой цепочке выпадает, интерфейс может выглядеть рабочим, но функция уже выполняется неправильно.

На примере Modus BI разберём пять багов, которые нашли с помощью ручного тестирования. Это не история про «кликнуть по кнопке и сверить текст». Каждый кейс показывает конкретный тип логической проблемы: неверный выбор источника данных для отображения, разрыв передачи состояния, ошибка на границе выборки, проблема рендера при длинном контенте и отсутствие сетевого запроса после действия пользователя.

Что важно понимать перед кейсами

В Modus BI пользователь собирает отчёт из связанных элементов. Данные приходят из источника, попадают в набор записей, затем отображаются в таблицах, фильтрах, диаграммах и модальных окнах. Для статьи важен не сам BI-контекст, а программная цепочка: откуда компонент берёт значение, как хранит состояние, куда передаёт параметры и какой результат должен появиться на экране.

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

SELECT * FROM table;

Но интерфейс не всегда должен показывать пользователю физические имена полей из базы. В БД поле может называться client_id, created_at, order_sum или region_code. Для разработчика это нормальные технические имена. Для пользователя отчёта чаще нужны понятные подписи: «Клиент», «Дата создания», «Сумма заказа», «Регион».

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

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

Поэтому каждый кейс ниже разбирается не как «ошибка в BI», а как сбой в программной цепочке: данные → состояние → событие → запрос или рендер → результат.

Кейс 1. Модальное окно берёт не тот источник имени поля

Первый баг выглядел почти незаметно. Модальное окно открывалось, данные не пропадали, ошибок на экране не было. Но компонент показывал не то значение, которое ожидал пользователь.

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

Здесь важно не смешивать три уровня:

  1. Физическое имя в базе данных. Техническое имя, с которым работает хранилище.

  2. Имя поля в наборе данных. Значение, которое система получила из источника.

  3. Пользовательский заголовок. Название, которое задано в интерфейсе для отображения.

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

Что должно было работать

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

Логика выглядит так:

если задан пользовательский заголовок → показываем его

иначе → показываем наименование поля из набора данных

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

Что было замечено

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

В настройках таблицы всё выглядело корректно: пользователь ввёл название, интерфейс его принял, визуально настройка сохранилась. Баг проявился дальше. После открытия модального окна система показала не заданный заголовок, а исходное наименование поля из набора данных.

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

Почему это важно для разработки

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

Условный приоритет может выглядеть так:

→ пользовательский заголовок

→ отображаемое имя из набора данных

→ физическое имя поля

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

Такой дефект особенно неприятен тем, что он не ломает интерфейс явно. Нет исключения, нет пустого состояния, нет падения страницы. Компонент просто берёт валидное, но неверное значение.

Как это было найдено и исправлено

Баг нашёлся при прохождении полного сценария. Сначала проверили, что заголовок задаётся и отображается в настройках таблицы. Затем проверили, как это же поле ведёт себя в модальном окне.

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

Вывод

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

Кейс 2. Date picker работает, но состояние не доходит до таблицы

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

Именно здесь появился баг. Компонент фильтра выглядел исправным, но таблица, которая зависела от выбранного диапазона, не обновляла данные.

Что должно было работать

В системе Modus BI был фильтр по диапазону дат. Он состоял из двух полей: «От» и «До». Пользователь нажимал на иконку календаря, выбирал год, месяц и конкретную дату. Такой сценарий важен, когда ручной ввод заблокирован и дату можно задать только через календарь.

После выбора даты должна сработать цепочка:

→ пользователь выбирает дату

→ date picker обновляет значение

→ фильтр обновляет состояние

→ формируются параметры фильтрации

→ связанная таблица получает новые параметры

→ данные в таблице обновляются

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

Что было замечено

Проверка шла в два этапа.

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

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

На этом этапе появилась проблема. Фильтр продолжал выглядеть корректно, но таблица не фильтровала данные согласно выбранным значениям. Пользователь задавал диапазон, интерфейс принимал выбор, но связанный компонент оставался в прежнем состоянии.

Почему это важно для разработки

Date picker может корректно менять значение внутри себя, но для интерфейса этого недостаточно. Нужно проверить, дошло ли изменение до слоя, который отвечает за данные таблицы.

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

→ значение date picker

→ состояние фильтра

→ объект параметров

→ событие изменения

→ связанный компонент

→ запрос или пересчёт данных

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

В такой ситуации тестировщик проверяет не «красивый календарь», а поток данных между компонентами. Это уже ближе к инженерной диагностике: где значение появилось, в каком виде оно должно уйти дальше и какой компонент обязан на него отреагировать.

Как это было найдено

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

Если бы проверка ограничилась date picker, баг остался бы незамеченным. Формально доработка фильтра прошла бы тест, но пользовательский сценарий всё равно был бы сломан.

Вывод

Этот кейс показывает, почему атомарного тестирования недостаточно. Компонент может работать правильно внутри своих границ, но ломаться в момент, когда его значение должно повлиять на другой компонент.

Для таких сценариев важно проверять не только контрол, но и весь путь состояния: от действия пользователя до обновления данных.

Кейс 3. Таблица теряет строку на границе выборки

Третий кейс Modus BI связан с таблицей, лимитом строк, итогами и фильтрацией. Снаружи это выглядит как обычные настройки отображения. Но с точки зрения реализации здесь сходятся несколько операций над выборкой: фильтр меняет набор данных, лимит ограничивает вывод, итоги считаются по отдельному правилу.

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

Что должно было работать

В контейнере «Таблица» есть настройка «Лимит при показе (строки)». Она задаёт максимальное количество строк, которое пользователь видит на экране, даже если в источнике данных записей больше. Например, если в источнике 1000 записей, а лимит установлен на 10, таблица должна показать 10 строк. Данные в источнике при этом не исчезают.

Вторая настройка — «Итоги». По требованиям нужно было проверить, как таблица работает при одновременном включении лимита строк и итогов. Логика такая: даже если пользователь ограничивает количество строк на экране, итоги должны считаться по всем данным таблицы, а не только по строкам, которые попали в отображаемый лимит.

Схема выглядит так:

→ данные таблицы

→ расчёт общих итогов по всему набору

→ применение лимита к строкам на экране

→ вывод ограниченного списка строк и общих итогов

Лимит отвечает за отображение. Итоги отвечают за расчёт. Если смешать эти задачи, пользователь получит неверный результат.

Что было замечено

Сначала был создан дашборд с двумя контейнерами: фильтром и таблицей. В таблице включили «Лимит при показе (строки)» и «Итоги».

При проверке этой связки всё работало согласно требованиям. Независимо от количества строк на экране таблица показывала общие итоги по всем данным. Основная доработка выглядела корректной.

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

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

Таблица не падала. Фильтр применялся. Данные были на месте. Но количество строк не совпадало с ожидаемым результатом. Это неприятный тип ошибки: строка не выглядит сломанной, она просто не появляется.

Почему это важно для разработки

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

В разработке похожие случаи часто относят к классу off-by-one, то есть ошибкам «на единицу». Они возникают, когда код неверно считает границы диапазона: включает или исключает последнюю запись, начинает отсчёт не с того индекса или неправильно сравнивает количество элементов с лимитом.

Но здесь не нужно утверждать конкретную причину без разбора кода. Важно другое: симптом указывает на проблемный участок — обработку границы после фильтрации и перед отображением.

Например:

показать строки с 0 по 3 включительно → 4 строки

показать строки с 0 по 3 не включительно → 3 строки

Для пользователя разница простая: он ждёт 4 записи, а получает 3. Для разработчика это повод проверить порядок применения фильтра, лимита и расчёта итогов. Особенно если часть операций выполняется на клиенте, часть на сервере, а интерфейс отдельно считает количество строк для отображения.

Почему проверка только основной доработки не спасает

В исходном требовании акцент был на связке «лимит строк + итоги». Эта часть работала корректно. Но баг нашёлся в соседнем сценарии: лимит строк + фильтр.

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

Фильтр здесь добавили не случайно. Это типовой пользовательский сценарий: в больших таблицах пользователь ограничивает вывод и фильтрует данные, чтобы быстрее найти нужные записи.

Как это было найдено

Баг нашли во время отдельной проверки лимита и его поведения в связке с фильтром. Сначала проверили основную доработку: лимит вместе с итогами. Затем стали тестировать настройки независимо друг от друга.

После добавления фильтра стало видно, что таблица не выводит последнюю ожидаемую строку. Если должно было появиться 4 записи, фактически отображались 3.

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

Вывод

Этот кейс показывает, почему нужно проверять не только наличие данных, но и точное количество строк. Таблица может выглядеть корректно, но всё равно терять запись на границе выборки.

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

Кейс 4. Tooltip появляется, но ломает читаемость слоя

Четвёртый кейс связан с рендером. Данные не пропали, диаграмма построилась, всплывающая подсказка появилась. Но интерфейс всё равно работал некорректно: подсказка перекрыла легенду, и пользователь не мог прочитать ни одно, ни другое.

Это не ошибка расчёта. Это ошибка поведения визуального компонента при нестандартном содержимом.

Что должно было работать

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

Tooltip работает как дополнительный слой. Он не должен ломать читаемость основного графика и соседних элементов. Сценарий выглядит так:

→ пользователь наводит курсор на элемент диаграммы

→ система показывает подсказку

→ пользователь читает содержимое подсказки

→ легенда остаётся читаемой

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

Что было замечено

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

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

То есть компонент формально выполнил действие, но не учёл контекст, в котором отрисовывается.

Почему это важно для разработки

Такие дефекты лежат в зоне layout-логики: позиционирования, расчёта размеров, слоёв и поведения при длинном содержимом. Tooltip не может рассчитываться только на короткие строки и свободное место рядом.

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

С инженерной точки зрения здесь нужно проверить:

  1. как компонент определяет ширину и высоту подсказки;

  2. переносит ли длинный текст;

  3. учитывает ли границы области диаграммы;

  4. не перекрывает ли легенду;

  5. есть ли у подсказки непрозрачный фон;

  6. корректно ли работает слой отображения поверх диаграммы.

Если подсказка появляется поверх легенды без корректного фона или позиции, пользователь видит наложение текста на текст. Данные есть, но работать с ними нельзя.

Как это было найдено и исправлено

Баг нашли намеренно: для проверки использовались большие данные и длинный текст. Нужно было понять, как компонент поведёт себя на нестандартных данных: обрежет подсказку, покажет её полностью или сломает читаемость соседних элементов.

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

Вывод

Этот кейс показывает, что отрисовать данные недостаточно. Нужно проверить, сможет ли пользователь их прочитать.

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

Кейс 5. Кнопка нажимается, но запрос не уходит

Пятый кейс Modus BI связан с импортом Excel. Интерфейс внешне почти не давал признаков поломки. Пользователь выбирал файл, видел его содержимое, заполнял нужные поля и нажимал кнопку импорта. Ошибки на экране не было. Форма не падала. Предпросмотр работал. Но ключевое действие не выполнялось: файл не импортировался.

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

Что должно было работать

В разделе загрузки данных пользователь выбирает файл .xlsx, система показывает содержимое, затем пользователь задаёт имя для отображения после импорта и нажимает «Импортировать данные».

После этого должна запуститься цепочка:

→ выбор файла

→ чтение и предпросмотр содержимого

→ заполнение параметров импорта

→ нажатие кнопки

→ отправка запроса

→ обработка файла на сервере

→ создание таблицы в выбранной схеме БД

→ создание набора данных в системе

→ сообщение об успешной загрузке

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

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

Что было замечено

Был выбран файл .xlsx. Система отобразила его содержимое. Затем было заполнено имя файла для отображения внутри системы. После нажатия на кнопку «Импортировать данные» ожидалось сообщение об успешной загрузке, создание таблицы в выбранной схеме БД и формирование набора данных.

Но этого не произошло. Файл не импортировался. Таблица в БД не создавалась. Набор данных в системе не появлялся.

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

Значит, нужно было смотреть не на содержимое Excel, а на то, что происходит после клика.

Почему это важно для разработки

Ключевой сигнал — отсутствие сетевого запроса. Через DevTools стало видно, что при нажатии на кнопку «Импортировать данные» запрос не отправляется.

Это сразу сужает область поиска. Если запрос ушёл, можно проверять серверную обработку, валидацию, права на создание таблицы, работу с выбранной схемой БД. Но если запроса нет, серверная часть ещё не участвует в сценарии.

С инженерной точки зрения проблема находится до API:

→ клик по кнопке

→ обработчик события

→ проверка состояния формы

→ подготовка данных для запроса

→ отправка запроса

Сам факт отсутствия запроса не говорит, где именно ошибка в коде. Но он показывает направление диагностики: обработчик клика, состояние формы, фронтенд-валидация, сбор параметров или логика отправки.

Как это было найдено

Баг нашли через DevTools. Сначала проверили пользовательские версии: корректность файла, отсутствие спецсимволов, повтор на другом .xlsx. После этого открыли панель сетевых запросов и повторили сценарий.

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

Это отделило ошибку интерфейса от ошибки обработки данных. Если бы сервер вернул ошибку, её можно было бы смотреть по статусу ответа, телу ответа или сообщению в интерфейсе. Здесь система даже не начала операцию импорта.

Вывод

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

Когда интерфейс молчит, DevTools помогает понять, где остановилась цепочка: до запроса, на запросе или после ответа сервера.

Что объединяет эти кейсы

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

Модальное окно открылось, но взяло не тот источник имени поля. Date picker работал, но значение не дошло до таблицы. Таблица обновилась, но потеряла строку на границе выборки. Tooltip появился, но сломал читаемость легенды. Кнопка импорта нажалась, но запрос не ушёл.

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

Поэтому проверка отдельного компонента даёт только часть картины. Нужно смотреть, что происходит после действия:

  1. какое состояние изменилось;

  2. какие параметры сформировались;

  3. какой компонент должен получить обновление;

  4. ушёл ли запрос;

  5. какие данные вернулись;

  6. как компонент отрисовал результат.

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

Во всех пяти кейсах интерфейс не выглядел полностью сломанным. Именно поэтому такие баги легко пропустить. Здесь помогает простое правило: проверять не только визуальный результат, но и техническое действие за ним.

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

Заключение

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

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

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

P.S. Присоединяйтесь к нашему BI-сообществу в Telegram и будьте в курсе последних новостей Modus!