Привет! Я инженер по контролю качества продукта Amplicode в компании Haulmont. Одним из направлений деятельности нашей компании является разработка плагинов для IntelliJ IDEA и расширений для VSCode. Передо мной встала задача протестировать расширение Amplicode Frontend для VS Code. Задача оказалась не самой тривиальной и в процессе мы столкнулись с немалым количеством проблем и нюансов, о которых я и хочу рассказать в этой статье.

Как мы тестировали раньше?

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

Есть два популярных фреймворка для тестирования расширений VS Code: Extester и WebdriverIO. Extester построен на базе Selenium WebDriver и практически “наследует” его принципы, добавляя API, которые упрощают работу с VS Code. Кроме того, Extester создавался исключительно для тестирования расширений VS Code. WebdriverIO реализует два протокола: WebDriver Protocol и Chrome DevTools Protocol, и в целом является более универсальным иснтрументом для автоматизации тестирования. Мы попробовали оба фреймворка, однако не будем проводить подробное их сравнение в этой статье. Напишем отдельную, если тема вызовет интерес у читателя. Скажем лишь, что знакомство с WebdriverIO проходило более гладко и не без удовольствия благодаря хорошей документации и активному комьюнити, чем Extester похвастаться не может. 

Далее я опишу некоторые, на мой взгляд, важные проблемы и нюансы при использовании WebdriverIO

Фреймы

VS Code был разработан на платформе Electron, а для создания инструментов расширения было решено использовать WebView. Эта технология создает фреймы в DOM, что является важным аспектом, поскольку мы используем WebView для отображения контента, но для поиска элементов по-прежнему полагаемся на x-path локаторы.

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

const addAuth = await browser.$(this.locators.addAuth); 
await addAuth.isDisplayed(); 
await addAuth.click();

const frame1 = await browser.findElement( 
  "css selector", 
  "iframe.webview.ready" 
); 
await browser.switchToFrame(frame1); 

const frame2 = await browser.findElement( 
  "css selector", 
  "iframe#active-frame" 
); 
await browser.switchToFrame(frame2); 

const configure = await browser.$(this.locators.configureButton); 
await configure.isDisplayed(); 
await configure.click(); 

await browser.switchToParentFrame(); 
await browser.switchToParentFrame(); 

const basicAuth = await browser.$(this.locators.basicFile); 
await basicAuth.waitForDisplayed({ timeout: 5000 }); 
await expect(basicAuth).toBeDisplayed(); 

Чтобы переключиться между фреймами, нужно найти именно элемент, а не только его ID. На просторах Google можно найти информацию, о том, что переключение возможно и просто по ID, но это не так. Метод switchToFrame должен получить элемент, который мы находим с помощью метода findElement, в который необходимо передать тип (CSS-селектор) и непосредственно сам селектор. Таким образом, мы сможем переключиться на нужный нам фрейм. 

Важно помнить, что после того, как мы выполним все необходимые действия в окне WebView, нужно вернуться обратно. Для этого необходимо выполнить browser.switchToParentFrame(), и в моем случае я делаю это дважды (помним, что при открытии WebView создается 2 фрейма, и переключаюсь я 2 раза). 

Расширение и проект 

Так как мы будем тестировать расширение, необходимо, чтобы оно было установлено при запуске инстанса VS Code, когда мы прогоняем тесты. Расширения в VSCode устанавливаются как VSIX-архивы, но фреймворк не сможет прочитать такой архив. Я взял расширение, изменил его формат на ZIP и распаковал содержимое. Чтобы установить расширение в тестах, нужно указать путь в конфигурации (wdio.conf.ts) следующим образом: 

const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); 
const __dirnameExtension = path.join(__dirname, '.extension', 'extension'); 
extensionPath: __dirnameExtension, 

Также необходимо создать папку с проектом внутри директории с тестами, который будет открываться в тестах, и указать путь к нему: 

const __dirnameWorkspace = path.join(__dirname, 'test', 'test-app'); 
workspacePath: __dirnameWorkspace, 

Хочу обратить внимание на то, что запускается настоящая версия VSCode с вашим расширением, поэтому если в проекте (на котором будут крутиться тесты) будут происходить изменения, их нужно откатывать, чтобы в следующий раз тесты работали корректно. И забегая вперед — это момент для автоматизации. 

Reporters (Отчеты) 

Важной частью автоматизированных тестов является reporter, который позволяет отлаживать код, отслеживать ошибки и красиво структурировать пройденные и не пройденные тесты. Можно записывать видео, делать скриншоты, подключать Allure со всеми графиками, но я остановился на HTML, это мне ближе и больше нравится (в Playwright есть подобный репортер, который мне также нравится). Это не проблема, а важная часть тестирования. Репортер указывается также в конфигурации: 

['html-nice', { 
  outputDir: './reports/html-nice/', 
  filename: 'report.html', 
  override: true, 
  reportTitle: 'Test Report', 
}]], 

Также в package.json указываем скрипт: 

"report": "open reports/html-nice/report-0-1.html" 

Если мы хотим запустить отчет, выполняем команду npm run report. Но каждый раз вручную запускать отчет мы, конечно же, не будем. Мы допишем запуск отчета в скрипт запуска тестов, чтобы отчет открывался автоматически после прохождения тестов. 

Вспомогательные скрипты и команды 

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

import fs from'fs'; 
import * as path from "node:path"; 
import * as url from "node:url"; 
import unzipper from 'unzipper'; 

const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) 
const vsixFilePath = path.join(__dirname, '.extension.vsix'); 

if (!fs.existsSync(vsixFilePath)) { 
  console.error('Файл .vsix не найден, продолжаем выполнение тестов'); 
  process.exit(0); 
} 

const zipFilePath = vsixFilePath.replace('.vsix', '.zip'); 
fs.renameSync(vsixFilePath, zipFilePath); 

const extractPath = path.join(__dirname, '.extension'); 
if (!fs.existsSync(extractPath)) { 
  fs.mkdirSync(extractPath); 
} 

fs.createReadStream(zipFilePath) 
  .pipe(unzipper.Extract({ path: extractPath })) 
  .on('close', () => { 
    console.log('Файл успешно распакован в директорию .extension'); 
  }) 
  .on('error', (err) => { 
    console.error('Ошибка при распаковке файла:', err); 
  }); 

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

"report": "open reports/html-nice/report-0-1.html" 

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

"cleanup": "git checkout . && git clean -fd" 

Скрипты, прописанные в package.json: 

"scripts": { 
  "unzip": "node unzipper.js", 
  "test": "npm-run-all --continue-on-error unzip test:run report cleanup", 
  "test:run": "wdio run ./wdio.conf.ts", 
  "report": "open reports/html-nice/report-0-1.html", 
  "cleanup": "git checkout . && git clean -fd" 
} 

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

unzip — для распаковки 

npm-run-all — для запуска команд 

Заключение 

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

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

Комментарии (0)