🐛 Типичные ошибки

← К оглавлению урока

⚡ Топ-5 ошибок урока

  • NoAlertPresentException: не ждали появления alert — используйте EC.alert_is_present()
  • Iframe-ловушка: забыли switch_to.default_content() — все следующие поиски элементов будут внутри iframe
  • Вкладка не переключилась: вызвали driver.close() без последующего switch_to.window()
  • Drag&drop не работает: HTML5 DnD API не реагирует на стандартный drag_and_drop() — нужен JS-inject
  • Загрузка файла: передали относительный путь вместо абсолютного — файл не найден

1. NoAlertPresentException — не ждали Alert

Симптом

selenium.common.exceptions.NoAlertPresentException: Message: no such alert

Причина

Обращение к Alert до того, как он появился:

# НЕПРАВИЛЬНО — alert может ещё не появиться
browser.find_element(By.ID, "submit").click()
alert = browser.switch_to.alert  # Ошибка: alert ещё не на экране

Исправление

# ПРАВИЛЬНО — ждём через EC
browser.find_element(By.ID, "submit").click()
wait = WebDriverWait(browser, 10)
alert = wait.until(EC.alert_is_present())  # Ждём до 10 секунд
alert.accept()

2. Iframe-ловушка — забыли вернуться в основной контекст

Симптом

selenium.common.exceptions.NoSuchElementException: no such element
# Элемент существует на странице, но Selenium его «не видит»

Причина

После работы с iframe контекст Selenium остался внутри фрейма:

# НЕПРАВИЛЬНО
driver.switch_to.frame(iframe)
content = driver.find_element(By.ID, "inside")
# Забыли default_content() — следующий поиск будет в iframe контексте!
header = driver.find_element(By.TAG_NAME, "h1")  # NoSuchElementException

Исправление

# ПРАВИЛЬНО
driver.switch_to.frame(iframe)
content = driver.find_element(By.ID, "inside")
driver.switch_to.default_content()  # Обязательно возвращаемся!
header = driver.find_element(By.TAG_NAME, "h1")  # Работает

3. Вкладка закрыта — не переключились обратно

Симптом

selenium.common.exceptions.NoSuchWindowException: no such window: target window already closed

Причина

После driver.close() Selenium всё ещё «смотрит» на закрытое окно:

# НЕПРАВИЛЬНО
tabs = driver.window_handles
driver.switch_to.window(tabs[1])
driver.close()
# Забыли переключиться! Любое действие упадёт
driver.find_element(By.ID, "something")  # NoSuchWindowException

Исправление

# ПРАВИЛЬНО
tabs = driver.window_handles
driver.switch_to.window(tabs[1])
driver.close()
driver.switch_to.window(tabs[0])  # Обязательно возвращаемся
driver.find_element(By.ID, "something")  # Работает

4. Drag and Drop не работает с HTML5 DnD

Симптом

Метод выполняется без ошибки, но элемент визуально не перетаскивается. Тест падает при проверке результата.

Причина

Некоторые современные drag&drop реализации используют HTML5 Drag and Drop API, которая не обрабатывает синтетические события Selenium стандартным образом. Пример: globalsqa.com/demo-site/draganddrop/.

# РАБОТАЕТ на jQuery UI drag-and-drop (crossbrowsertesting.github.io)
ActionChains(browser).drag_and_drop(source, target).perform()

# НЕ РАБОТАЕТ на некоторых HTML5 DnD реализациях

Исправление (JS-inject)

# test_drag_drop_js.py
# Альтернативный подход через JavaScript для HTML5 DnD:
js_drag_drop = """
function simulateDragDrop(sourceNode, destinationNode) {
    var EVENT_TYPES = ['dragstart','dragenter','dragover','drop','dragend'];
    function createEvent(type) {
        var event = new DragEvent(type, {bubbles: true, cancelable: true});
        return event;
    }
    sourceNode.dispatchEvent(createEvent('dragstart'));
    destinationNode.dispatchEvent(createEvent('dragenter'));
    destinationNode.dispatchEvent(createEvent('dragover'));
    destinationNode.dispatchEvent(createEvent('drop'));
    sourceNode.dispatchEvent(createEvent('dragend'));
}
simulateDragDrop(arguments[0], arguments[1]);
"""
browser.execute_script(js_drag_drop, source, target)
⚠️ Проверить по документации: поведение зависит от конкретного фреймворка на сайте. Для ДЗ 5 (globalsqa) сайт использует jQuery UI внутри iframe — рекомендуется сначала попробовать стандартный drag_and_drop, и только при неудаче применять JS-inject.

5. Загрузка файла: относительный путь

Симптом

Файл не загружается, форма отправляется пустой или тест падает с ошибкой валидации.

Причина

# НЕПРАВИЛЬНО — относительный путь зависит от cwd при запуске pytest
file_input.send_keys("test.txt")

# НЕПРАВИЛЬНО на Windows — слэши без экранирования или raw-строки
file_input.send_keys("C:\Users\user\test.txt")  # \U, \t — escape-последовательности Python!

Исправление

# ПРАВИЛЬНО — всегда абсолютный путь через os.path.abspath
import os
file_path = os.path.abspath("test.txt")
file_input.send_keys(file_path)

# Или через __file__ для независимости от cwd
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, "test.txt")
file_input.send_keys(file_path)

6. StaleElementReferenceException после switch_to

Симптом

selenium.common.exceptions.StaleElementReferenceException: stale element reference

Причина

Элемент был найден до переключения контекста (в iframe или на другой вкладке). После switch_to контекст меняется — элемент устаревает:

# НЕПРАВИЛЬНО — нашли элемент до переключения во фрейм
element = driver.find_element(By.ID, "inside-iframe")  # Находим до switch
driver.switch_to.frame(iframe)
element.click()  # StaleElementReferenceException

Исправление

# ПРАВИЛЬНО — находим элементы ПОСЛЕ переключения контекста
driver.switch_to.frame(iframe)
element = driver.find_element(By.ID, "inside-iframe")  # Находим после switch
element.click()  # Работает

7. ActionChains: reset_actions не вызван — старые действия накапливаются

Симптом

Тест ведёт себя нестабильно: иногда выполняет лишние клики или hover.

Причина

# ПОТЕНЦИАЛЬНАЯ ОШИБКА — объект actions переиспользуется без сброса
actions = ActionChains(browser)
actions.move_to_element(menu1).perform()

# В следующем тесте/шаге:
actions.move_to_element(menu2).perform()
# Выполнится: move_to_element(menu1) + move_to_element(menu2)!
# Потому что actions накопил предыдущее действие

Исправление

# ПРАВИЛЬНО — создавайте новый объект или вызывайте reset_actions()
actions = ActionChains(browser)    # Новый объект для каждого действия
actions.move_to_element(menu1).perform()

actions = ActionChains(browser)    # Или: новый объект
actions.move_to_element(menu2).perform()