﻿# Ваши тесты упали по причине JavaScript

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

![1309_test_fail_because_js_ru/image1.png](https://import.viva64.com/docx/blog/1309_test_fail_because_js_ru/image1.png)

Для нашего статического анализатора мы поддерживаем довольно большое количество интеграций в различные инструменты, в том числе в IDE, чтобы разработчики могли без проблем пользоваться инструментом в процессе разработки\. Одна из таких интеграций — расширение для Visual Studio Code, написанное на JavaScript и TypeScript\.


> \*\*Примечание\\\.\*\* О том, как пользоваться расширением PVS\\\-Studio для Visual Studio Code, можно прочитать \[в соответствующем разделе\]\(https://pvs\-studio\.ru/ru/docs/manual/6646/\) нашей документации\\\.

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

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

## Расследование ведут единороги

Итак, начнём наш небольшой детектив с того, что именно сломалось\.

Для запуска различных действий в плагине используется панель команд Visual Studio Code\. Она открывается по сочетанию клавиш `Ctrl+Shift+P`\. Соответственно, во [фреймворке для тестирования](https://github.com/redhat-developer/vscode-extension-tester/tree/main), который мы используем, предусмотрен функционал для взаимодействия с этой панелью\.

Говорю я про неё, потому что на скриншотах, сохранившихся в CI/CD после падения тестов, было видно, что панель команд в Visual Studio Code открылась, но дальше ничего не происходило:

![1309_test_fail_because_js_ru/image2.png](https://import.viva64.com/docx/blog/1309_test_fail_because_js_ru/image2.png)

Стало понятно, в какую сторону копать, но всё ещё неясно, в чём конкретно заключается проблема\.

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

Итак, вот метод, отвечающий за взаимодействие с панелью команд:

```cpp
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` для версий постарше\.

Всё выглядит довольно прилично, но всего одна строка и привела к поломке:

```cpp
...
if (Workbench.versionInfo.version >= '1.44.0') {...}
...
```

Здесь мы, собственно, сравниваем версии, чтобы понять, какой объект для взаимодействия с панелью команд необходимо использовать\. Обратите внимание, что версия указана строкой\.

А теперь мы зайдём [на сайт Visual Studio Code](https://code.visualstudio.com/updates/v1_105) и посмотрим, какая версия является самой новой:

![1309_test_fail_because_js_ru/image3.png](https://import.viva64.com/docx/blog/1309_test_fail_because_js_ru/image3.png)

Давайте попробуем выполнить сравнение с этим значением:

```cpp
console.log("1.105.0" >= "1.44.0") // false
```

Поскольку версия хранится в строке, мы сравниваем числа лексикографически\. Т\. е\. мы посимвольно двигаемся слева направо, сравнивая коды символов в Unicode\.

Таким образом, во фрагменте выше TypeScript сравнил символы `1` и `4`, после чего сделал необходимые выводы, ведь он не знает, что `1` находится в разряде выше, чем `4`\.

В результате такого сравнения фреймворк выбрал неправильный способ передачи команд в панель команд Visual Studio Code, и тесты намертво зависли\.

Решением этой проблемы могло бы стать хранение версии в объекте, а не в строке:

```cpp
interface VSCodeVersion {
  major: Number,
  minor: Number
}
```

И данная проблема была пофикшена в новых версиях фреймворка использованием [отдельной библиотеки](https://www.npmjs.com/package/compare-versions) для проверки версий\.

## Динамическая типизация

Вы можете смело сказать: "В заголовке сказано, что тесты упали из\-за JavaScript, но ведь такое могло быть и в любом другом, даже статически типизированном языке\!"— и будете правы\. Однако конкретно в JavaScript мы можем найти причину довольно похожих происшествий\.

JavaScript — это язык с динамической типизацией, поэтому с типами в нём порой творится настоящая магия\. 

Например, если мы хотим максимально точно сравнить два значения, следует использовать оператор `===`\. При его использовании мы также проверяем, что типы объектов одинаковые\.

Более привычный же для других языков оператор `==` при сравнении выполняет приведение типов, что позволяет даже значениям разных типов при сравнении выдавать `true`\.

Например, мы можем сравнить `true` и `1`:

```cpp
console.log(true == 1);   // true 
console.log(true === 1);  // false
```

Поскольку `true` при приведении типов будет равно `1`, результатом сравнения с приведением типов будет `true`\. 

Таким же образом мы можем сравнить число и строку:

```cpp
console.log("5" == 5);   // true
console.log("5" === 5);  // false
```

Несмотря на то, что TypeScript — это расширение JavaScript, добавляющее статическую типизацию, компилятор TypeScript, в зависимости от настроек, может не ругаться на такие сравнения\.

Подобные проблемы в проекте может помочь выловить, например, [статический анализатор кода](https://pvs-studio.ru/ru/blog/terms/0046/)\. 

## Заключение

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

А статья про JavaScript, кстати, не просто же так попала в блог PVS\-Studio\. Кто знает, может, совсем скоро в PVS\-Studio появится анализатор для JavaScript и TypeScript\.\.\.

Чистого вам кода, друзья\!