Привет! Я инженер по контролю качества продукта 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 репозиторий, все ссылки есть на сайте.
ссылка на оригинал статьи https://habr.com/ru/articles/879002/
Добавить комментарий