📖 Теория — Расширенные практики Selenium (часть 2)

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

⚡ Кратко

Alert вне DOM — работа только через switch_to.alert. Вкладки управляются через window_handles + switch_to.window(). Сложные действия мыши — ActionChains. Содержимое iframe недоступно без switch_to.frame(). Файл загружается через send_keys(abs_path) на input[type=file].

1. Alert — всплывающие диалоговые окна

Alert — это браузерное диалоговое окно, которое не является частью DOM-дерева. Обычные методы поиска элементов (find_element) его не видят. Чтобы взаимодействовать с Alert, нужно переключиться на него через switch_to.alert.

В браузере существует три типа Alert-диалогов:

  • alert() — информационное окно с кнопкой «OK»
  • confirm() — окно с кнопками «OK» и «Cancel»
  • prompt() — окно с полем ввода текста и кнопками «OK» / «Cancel»

Методы Alert в Selenium 4

Получение текста из Alert

# test_alert.py
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://example.com/page-with-alert")

# Дожидаемся появления alert (надёжнее, чем сразу switch_to)
wait = WebDriverWait(driver, 10)
alert = wait.until(EC.alert_is_present())

text = alert.text      # Получаем текст
print(text)

Принятие (OK) — alert.accept()

Нажимает «OK» в любом типе диалога:

# test_alert.py
alert.accept()

Отмена (Cancel) — alert.dismiss()

Работает только для confirm и prompt. Аналог кнопки «Отмена»:

# test_alert.py
alert.dismiss()

Ввод текста в prompt — alert.send_keys()

Доступен только для prompt()-диалогов, где есть поле ввода:

# test_alert.py
alert.send_keys("Мой ответ")
alert.accept()    # Подтверждаем ввод
Важно: всегда используйте WebDriverWait + EC.alert_is_present() перед обращением к alert. Без ожидания тест упадёт с NoAlertPresentException, если диалог ещё не появился.

2. Переключение между вкладками и окнами

Когда страница открывает новую вкладку или окно, Selenium не переключается на неё автоматически. Нужно самостоятельно получить идентификатор новой вкладки и переключиться на неё.

Ключевые свойства и методы

driver.window_handles

Возвращает список идентификаторов всех открытых вкладок. Каждый идентификатор — уникальная строка вида 'CDwindow-XXXXXXXX':

# test_tabs.py
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://example.com")

# Открываем новую вкладку через JavaScript
driver.execute_script("window.open('https://google.com', '_blank');")

# Получаем список всех вкладок
tabs = driver.window_handles
print(tabs)
# ['CDwindow-123456', 'CDwindow-789012']

driver.current_window_handle

Возвращает идентификатор текущей активной вкладки:

# test_tabs.py
print(driver.current_window_handle)
# 'CDwindow-123456'

driver.switch_to.window(handle)

Переключает Selenium на указанную вкладку:

# test_tabs.py
# Переключаемся на вторую вкладку
driver.switch_to.window(tabs[1])

# Работаем на второй вкладке...

# Закрываем вторую вкладку
driver.close()

# Обязательно переключаемся обратно!
driver.switch_to.window(tabs[0])
Осторожно: после driver.close() активная вкладка закрыта, но Selenium всё ещё «смотрит» на неё. Любое действие вызовет ошибку. Всегда вызывайте switch_to.window() после закрытия вкладки.

3. ActionChains — сложные взаимодействия мышью

Класс ActionChains эмулирует действия пользователя, которые нельзя выполнить простым .click(): наведение курсора, двойной клик, drag&drop, контекстное меню и цепочки действий.

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

# test_actions.py
from selenium.webdriver.common.action_chains import ActionChains

actions = ActionChains(driver)
# Добавляем действия в цепочку...
actions.move_to_element(element).perform()
# perform() — выполнить всю цепочку

Hover (наведение курсора)

Используется для открытия выпадающих меню, tooltip-ов, скрытых элементов, которые появляются только при наведении:

# test_hover.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

# Наводим курсор на элемент меню
menu_item = driver.find_element(By.ID, "menu")
actions = ActionChains(driver)
actions.move_to_element(menu_item).perform()

# После hover кликаем по появившемуся подменю
submenu = driver.find_element(By.ID, "submenu")
submenu.click()

Drag and Drop (перетаскивание)

Метод drag_and_drop(source, target) перемещает элемент source на позицию target:

# test_drag_drop.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com/drag")

source = driver.find_element(By.ID, "draggable")
target = driver.find_element(By.ID, "droppable")

actions = ActionChains(driver)
actions.drag_and_drop(source, target).perform()
⚠️ Проверить по документации: некоторые современные drag&drop реализации на JavaScript (например, HTML5 DnD API) могут не реагировать на стандартный drag_and_drop(). В таких случаях используют drag_and_drop_by_offset() или JS-inject.

4. Работа с фреймами (iframe)

Фрейм (iframe) — это HTML-элемент, который встраивает одну страницу в другую. Элементы внутри iframe находятся в отдельном контексте DOM и недоступны напрямую. Для работы с ними нужно сначала переключиться в этот контекст.

Переключение в iframe

# test_iframe.py
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com/page-with-iframe")

# Найти iframe-элемент и переключиться в него
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)

# Теперь можно работать с элементами внутри iframe
content = driver.find_element(By.ID, "inside-iframe")
print(content.text)

# Вернуться в основной контекст страницы
driver.switch_to.default_content()

Можно переключаться по имени, индексу или WebElement:

# test_iframe.py
driver.switch_to.frame("frame-name")   # по атрибуту name
driver.switch_to.frame(0)              # по индексу (первый фрейм)
driver.switch_to.frame(iframe_element) # по WebElement (рекомендуется)
Правило: всегда вызывайте driver.switch_to.default_content() после работы с iframe, чтобы вернуться к основной странице. Иначе все последующие поиски элементов будут ограничены контекстом последнего фрейма.

5. Загрузка файлов

Для загрузки файла в тесте не нужно открывать системный диалог выбора файла. Selenium позволяет передать абсолютный путь к файлу напрямую в элемент <input type="file"> через send_keys().

# test_upload.py
import os
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("http://example.com/upload")

# Получаем абсолютный путь к файлу
file_path = os.path.abspath("test_file.txt")

# Передаём путь в input[type=file]
file_input = driver.find_element(By.ID, "file-upload")
file_input.send_keys(file_path)

# Отправляем форму
driver.find_element(By.ID, "submit").click()
Важно: send_keys() работает только с нативным <input type="file">. Если кнопка загрузки кастомная (div, button, styled-input), потребуется сделать элемент видимым через JavaScript или использовать другой подход.
На Windows путь должен использовать обратные слэши или raw-строку:
file_path = r"C:\Users\user\test_file.txt"
Удобнее использовать os.path.abspath() — он автоматически строит корректный путь для текущей ОС.