Рассказываем, как безобидная строка JavaScript-кода привела к нарушению стабильности тестов продукта, а также о том, как можно избежать подобных ошибок. Читать далее
Рассказываем, как безобидная строка JavaScript-кода привела к нарушению стабильности тестов продукта, а также о том, как можно избежать подобных ошибок.

Для нашего статического анализатора мы поддерживаем довольно большое количество интеграций в различные инструменты, в том числе в IDE, чтобы разработчики могли без проблем пользоваться инструментом в процессе разработки. Одна из таких интеграций — расширение для Visual Studio Code, написанное на JavaScript и TypeScript.
Примечание. О том, как пользоваться расширением PVS-Studio для Visual Studio Code, можно прочитать в соответствующем разделе нашей документации.
Как и для любой другой части продукта, необходимо отслеживать качество этого расширения, поэтому для него есть различные способы верификации и тестирования, один из них — UI тесты, проверяющие основные сценарии взаимодействия пользователя с плагином.
Но однажды эти тесты стали падать без причины. По крайней мере, мы воспринимали эти падения именно так, потому что никаких изменений с нашей стороны в этот промежуток времени не происходило.
Расследование ведут единорогиИтак, начнём наш небольшой детектив с того, что именно сломалось.
Для запуска различных действий в плагине используется панель команд Visual Studio Code. Она открывается по сочетанию клавиш Ctrl+Shift+P. Соответственно, во фреймворке для тестирования, который мы используем, предусмотрен функционал для взаимодействия с этой панелью.
Говорю я про неё, потому что на скриншотах, сохранившихся в CI/CD после падения тестов, было видно, что панель команд в Visual Studio Code открылась, но дальше ничего не происходило:

Стало понятно, в какую сторону копать, но всё ещё неясно, в чём конкретно заключается проблема.
В результате долгих поисков мы выяснили, что причина падения заключалась во фреймворке, который используется для тестирования.
Итак, вот метод, отвечающий за взаимодействие с панелью команд:
async openCommandPrompt(): Promise<QuickOpenBox | InputBox> {
const webview = await new EditorView()
.findElements(
EditorView.locators.EditorView.webView
);
if (webview.length > 0) {
const tab = await new EditorView().getActiveTab();
if (tab) {
await tab.sendKeys(Key.F1);
return await InputBox.create();
}
}
const driver = this.getDriver();
await driver.actions()
.keyDown(Workbench.ctlKey)
.keyDown(Key.SHIFT)
.sendKeys('p')
.perform();
if (Workbench.versionInfo.version >= '1.44.0') {
return await InputBox.create();
}
return await QuickOpenBox.create();
}
В этом методе фреймворк получает нужные объекты, эмулирует нажатие необходимого сочетания клавиш с помощью веб-движка, а после, в зависимости от используемой версии Visual Studio Code, создаёт объекты, в которые будут переданы необходимые команды: InputBox для более новых версий или QuickOpenBox для версий постарше.
Всё выглядит довольно прилично, но всего одна строка и привела к поломке:
...
if (Workbench.versionInfo.version >= '1.44.0') {...}
...
Здесь мы, собственно, сравниваем версии, чтобы понять, какой объект для взаимодействия с панелью команд необходимо использовать. Обратите внимание, что версия указана строкой.
А теперь мы зайдём на сайт Visual Studio Code и посмотрим, какая версия является самой новой:

Давайте попробуем выполнить сравнение с этим значением:
console.log("1.105.0" >= "1.44.0") // false
Поскольку версия хранится в строке, мы сравниваем числа лексикографически. Т. е. мы посимвольно двигаемся слева направо, сравнивая коды символов в Unicode.
Таким образом, во фрагменте выше TypeScript сравнил символы 1 и 4, после чего сделал необходимые выводы, ведь он не знает, что 1 находится в разряде выше, чем 4.
В результате такого сравнения фреймворк выбрал неправильный способ передачи команд в панель команд Visual Studio Code, и тесты намертво зависли.
Решением этой проблемы могло бы стать хранение версии в объекте, а не в строке:
interface VSCodeVersion {
major: Number,
minor: Number
}
И данная проблема была пофикшена в новых версиях фреймворка использованием отдельной библиотеки для проверки версий.
Динамическая типизацияВы можете смело сказать: "В заголовке сказано, что тесты упали из-за JavaScript, но ведь такое могло быть и в любом другом, даже статически типизированном языке!"— и будете правы. Однако конкретно в JavaScript мы можем найти причину довольно похожих происшествий.
JavaScript — это язык с динамической типизацией, поэтому с типами в нём порой творится настоящая магия.
Например, если мы хотим максимально точно сравнить два значения, следует использовать оператор ===. При его использовании мы также проверяем, что типы объектов одинаковые.
Более привычный же для других языков оператор == при сравнении выполняет приведение типов, что позволяет даже значениям разных типов при сравнении выдавать true.
Например, мы можем сравнить true и 1:
console.log(true == 1); // true
console.log(true === 1); // false
Поскольку true при приведении типов будет равно 1, результатом сравнения с приведением типов будет true.
Таким же образом мы можем сравнить число и строку:
console.log("5" == 5); // true
console.log("5" === 5); // false
Несмотря на то, что TypeScript — это расширение JavaScript, добавляющее статическую типизацию, компилятор TypeScript, в зависимости от настроек, может не ругаться на такие сравнения.
Подобные проблемы в проекте может помочь выловить, например, статический анализатор кода.
ЗаключениеЭта история — напоминание о том, как легко упустить из виду "очевидные" детали вроде сравнения строк, особенно в языке, где типы ведут себя не так, как кажется на первый взгляд. Даже в проектах с, казалось бы, строгой типизацией такие ловушки остаются возможными.
А статья про JavaScript, кстати, не просто же так попала в блог PVS-Studio. Кто знает, может, совсем скоро в PVS-Studio появится анализатор для JavaScript и TypeScript...
Чистого вам кода, друзья!
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Valerii Filatov. JavaScript failed your tests.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Дайджест JS/TS: новинки ES2026, гонка рантаймов и EAP | 0 | 12.18 | 29-06-2026 |
| 2 | История JavaScript: браузерные войны, ECMAScript, Node.js, TypeScript, React | 0 | 8.31 | 10-07-2026 |
| 3 | Строки кода должны помещаться на экране | 0 | 13.58 | 01-12-2025 |
| 4 | Ааа, всё пропало! AI создаёт дырявый код! Что же делать? | 0 | 11.43 | 16-06-2026 |
| 5 | Давайте заглянем в этот самый вайб-код | 0 | 8.44 | 20-03-2026 |
| 6 | Разработка нового статического анализатора: PVS-Studio JavaScript | 0 | 10.39 | 15-04-2026 |
| 7 | Баги на диком западе: топ-10 ошибок в C и C++ проектах за 2025 год | 0 | 8.06 | 30-12-2025 |
| 8 | Ошибка, которую никто никогда не совершал. Ведь так, да? | -1 | 11.32 | 17-07-2026 |
| 9 | OWASP Top 10 2025 — от кода к цепочке поставок: расширение границ безопасности | 0 | 16.98 | 16-02-2026 |