Работа с асинхронным JavaScript

WebdriverIO работает поверх WebDriver-протокола и сильно зависит от асинхронного выполнения операций. Каждое взаимодействие с браузером, элементами страницы или ожидание состояния требует асинхронного подхода. Основная причина — коммуникация с браузером осуществляется через удалённый сервер (например, Selenium Server или ChromeDriver), и операции могут занимать непредсказуемое время.

В современном WebdriverIO используется синтаксис async/await, который делает асинхронный код более читаемым и последовательным, по сравнению с использованием промисов напрямую.

it('должен открыть страницу и проверить заголовок', async () => {
    await browser.url('https://example.com');
    const title = await browser.getTitle();
    console.log(title);
});

В этом примере метод browser.url выполняется асинхронно и возвращает промис. Ключевое слово await заставляет выполнение теста ждать завершения операции перед переходом к следующей строке.


Асинхронные команды WebdriverIO

Все команды WebdriverIO, связанные с браузером и элементами страницы, являются асинхронными. Основные категории:

  • Навигация: browser.url(), browser.back(), browser.forward(), browser.refresh()
  • Работа с элементами: $(selector), $$(), element.click(), element.setValue()
  • Ожидания: element.waitForDisplayed(), browser.waitUntil()
  • Взаимодействие с окнами и фреймами: browser.switchToFrame(), browser.switchToWindow()
  • Скриншоты и сохранение состояния: browser.saveScreenshot(), element.takeScreenshot()

Каждая из этих команд возвращает промис, и игнорирование await может привести к непредсказуемому поведению тестов.


Работа с элементами

Поиск элементов через $ и $$ всегда возвращает объект Element или массив элементов, обернутых в прокси WebdriverIO. Важно помнить, что сам элемент может не быть сразу доступен на странице, поэтому часто требуется ожидание:

const button = await $('#submit');
await button.waitForClickable({ timeout: 5000 });
await button.click();

Метод waitForClickable позволяет дождаться, пока элемент не станет кликабельным, предотвращая ошибки типа element not interactable.


Ожидания и синхронизация

Асинхронность в тестах требует контроля времени ожидания состояния элементов. WebdriverIO предоставляет гибкие методы:

waitUntil

Позволяет ждать выполнение произвольного условия:

await browser.waitUntil(async () => {
    const text = await $('#status').getText();
    return text === 'Готово';
}, {
    timeout: 10000,
    timeoutMsg: 'Статус не обновился за 10 секунд'
});

waitForDisplayed / waitForExist

Специализированные методы для проверки появления элементов:

const alert = await $('#alert');
await alert.waitForDisplayed({ timeout: 5000 });

Использование этих методов гарантирует стабильность тестов, даже если страница загружается медленно.


Асинхронные хуки

WebdriverIO позволяет использовать хуки для асинхронной подготовки и очистки тестовой среды:

  • before: выполняется перед всеми тестами в файле
  • beforeEach: выполняется перед каждым тестом
  • afterEach: выполняется после каждого теста
  • after: выполняется после всех тестов

Пример использования:

beforeEach(async () => {
    await browser.url('https://example.com');
    await $('#login').setValue('user');
});

Все хуки поддерживают async/await, что делает последовательное выполнение действий надежным.


Обработка ошибок и таймаутов

Асинхронный код в WebdriverIO должен корректно обрабатывать исключения. Обычная практика — использование try/catch:

try {
    const modal = await $('#modal');
    await modal.waitForDisplayed({ timeout: 3000 });
    await modal.close();
} catch (error) {
    console.log('Модальное окно не появилось:', error.message);
}

Это предотвращает падение всего теста из-за непредвиденного отсутствия элемента или задержек в загрузке страницы.


Асинхронные тестовые сценарии

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

it('регистрация нового пользователя', async () => {
    await browser.url('/register');
    const username = await $('#username');
    const password = await $('#password');
    const submit = await $('#submit');

    await username.setValue('testuser');
    await password.setValue('securepass');
    await submit.click();

    const successMsg = await $('#success');
    await successMsg.waitForDisplayed({ timeout: 5000 });
});

Правильное использование await обеспечивает корректный порядок действий и стабильность тестов.


Асинхронные утилиты и пользовательские команды

WebdriverIO поддерживает создание пользовательских команд через browser.addCommand или element.addCommand, которые также могут быть асинхронными:

browser.addCommand('login', async (username, password) => {
    await $('#username').setValue(username);
    await $('#password').setValue(password);
    await $('#submit').click();
});

await browser.login('admin', 'adminpass');

Это позволяет инкапсулировать повторяющиеся действия и сохранять чистоту тестового кода.


Асинхронность в WebdriverIO — ключевой элемент архитектуры, обеспечивающий надежное взаимодействие с браузером и элементами страницы. Понимание принципов работы async/await, ожиданий и обработки ошибок формирует основу стабильных и масштабируемых тестов.