В Protractor ошибка, условно называемая ElementNotFound, возникает в ситуациях, когда тест пытается взаимодействовать с элементом, который отсутствует в DOM, ещё не появился, был удалён или не соответствует заданному локатору. Чаще всего это проявляется в виде исключений:
NoSuchElementErrorStaleElementReferenceErrorTimeoutError)Причины можно разделить на несколько категорий:
Понимание этих причин — основа корректной обработки подобных ошибок.
Protractor изначально создавался как инструмент для
Angular-приложений и использует механизм автоматической синхронизации с
$digest и $http. Однако этот механизм не
гарантирует, что конкретный элемент уже появился в DOM.
Пример проблемной ситуации:
element(by.css('.save-button')).click();
Если кнопка появляется после завершения HTTP-запроса или анимации, тест может завершиться ошибкой.
Ключевой момент: автоматическая синхронизация не заменяет явных ожиданий элементов.
Основной способ обработки ElementNotFound — использование
ExpectedConditions.
const EC = protractor.ExpectedConditions;
const saveButton = element(by.css('.save-button'));
browser.wait(EC.presenceOf(saveButton), 5000);
saveButton.click();
Наиболее часто используемые условия:
presenceOf — элемент присутствует в DOM;visibilityOf — элемент видим;elementToBeClickable — элемент доступен для клика;stalenessOf — элемент удалён из DOM.Выбор неправильного условия — частая причина нестабильных тестов.
Например, presenceOf не гарантирует, что элемент видим или
доступен для взаимодействия.
Ошибка ElementNotFound часто возникает при работе с
element.all, особенно при обращении по индексу.
element.all(by.css('.item')).get(0).click();
Если список пуст, get(0) приведёт к ошибке.
Безопасный подход:
const items = element.all(by.css('.item'));
browser.wait(() =>
items.count().then(count => count > 0),
5000
);
items.first().click();
Здесь ожидание строится на реальном состоянии коллекции, а не на предположении о наличии элементов.
Метод isPresent() позволяет проверить наличие элемента
без генерации ошибки.
const modal = element(by.css('.modal'));
modal.isPresent().then(isPresent => {
if (isPresent) {
modal.element(by.css('.close')).click();
}
});
Важно различать:
isPresent() — элемент есть в DOM;isDisplayed() — элемент видим (вызывает ошибку, если
элемента нет).Неправильное использование isDisplayed() —
распространённый источник ElementNotFound.
В Angular-приложениях элементы часто удаляются и создаются заново,
что приводит к StaleElementReferenceError.
Небезопасный код:
const row = element(by.css('.row'));
row.click();
// DOM обновился
row.element(by.css('.delete')).click();
Корректный подход — повторный поиск элемента:
element(by.css('.row')).click();
browser.wait(EC.presenceOf(element(by.css('.delete'))), 3000);
element(by.css('.delete')).click();
Правило: не хранить ссылки на элементы дольше одного логического шага.
Слишком сложные или зависящие от структуры DOM локаторы повышают риск ошибок.
Плохой пример:
by.css('div.container > div:nth-child(3) > button')
Лучшие практики:
data-testid, data-qa,
data-e2e;by.id и by.css с устойчивыми
атрибутами.Пример устойчивого локатора:
by.css('[data-qa="submit-button"]')
Иногда ElementNotFound возникает из-за слишком малого времени ожидания.
Глобальная настройка:
exports.config = {
allScriptsTimeout: 11000,
getPageTimeout: 10000
};
Однако увеличение тайм-аутов — не универсальное решение. Приоритет всегда у локальных явных ожиданий, привязанных к конкретным элементам и состояниям.
Для крупных проектов характерно создание утилитарных методов:
function safeClick(el, timeout = 5000) {
const EC = protractor.ExpectedConditions;
browser.wait(EC.elementToBeClickable(el), timeout);
return el.click();
}
Использование:
safeClick(element(by.id('submit')));
Такой подход:
При отладке ошибок ElementNotFound важно фиксировать состояние страницы:
Пример получения HTML:
browser.getPageSource().then(source => {
console.log(source);
});
Это особенно полезно при падениях в CI, где воспроизведение локально затруднено.
Частые ошибки ElementNotFound — симптом проблем в архитектуре:
Page Object позволяет инкапсулировать ожидания:
this.save = function () {
const btn = element(by.id('save'));
browser.wait(EC.elementToBeClickable(btn), 5000);
btn.click();
};
В результате тесты становятся декларативными и устойчивыми к изменениям интерфейса.