5. Selene #1 - qa-guru/knowledge-base GitHub Wiki
Особенность Selene
Задача Selene говорить на языке пользователя, поэтому обращаться с его методами просто, и многие из них читаются практически также, как на естественном языке.
Документация Selene:
https://yashaka.github.io/selene/
https://autotest.how/selenides-quick-start-docs-md/
https://autotest.how/selenides-in-action-docs-md/
Установка Selene
pip install selene --pre
Пример использования Selene
from selene import browser, have
browser.open('https://demoqa.com/automation-practice-form')
browser.should(have.title('DEMOQA')) # проверка, что вкладка содержит текст «DEMOQA»
# Элемент по селектору «main-header» должен содержать текст «Box»
browser.element('[class="main-header"]').should(have.text('Box'))
Важно отметить, что Selene работает с CSS-селекторами и XPath.
CSS-селекторы
Важно: краткая запись класса начинается с символа
.
, а ID с#
.
CSS-селекторы позволяют обращаться к различным элементам страницы и выполнять с ними различные действия. В примере выше CSS-селектор main-header
указан после слова element
: browser.element('[class="main-header"]')
. В данном случае мы обращаемся к элементу по атрибуту и его значению, где class
— атрибут, а main-header
— значение.
Можно не указывать значение. Но тогда найдутся все элементы с указанным атрибутом. К примеру, browser.element('[class]')
.
Можно указать элемент ещё более точно и обратиться не просто к атрибуту и значению, но и к его тегу, К примеру, browser.element('[div[class="main-header"]')
. Но важно помнить, чтобы селекторы были стабильными и не стоит использовать очень длинные и точные описания. Достаточно указать уникальные атрибуты.
В наилучшем варианте стоит договориться с разработчиками, чтобы они сами указывали в коде проекта уникальные атрибуты для тестирования, которые никогда не будут меняться и использоваться только для целей тестирования.
Если значение атрибута состоит из нескольких слов, то мы можем обратиться только по основному. Для этого понадобится символ тильды (~
). К примеру, в коде мы имеем селектор cursive main-header bold
. Обратиться к нему можно browser.element('[class~=main-header]')
. Можно ещё сократить и написать browser.element('.main-header')
.
Ещё можно обращаться следующим образом: browser.element('[id=userName]')
. Если сократить, то выйдет вот так: browser.element('#userName')
.
Что делать с дублирующими ID
Бывает такое, что разные элементы имеют в коде одинаковые ID. Поэтому если обращаться к этому элементу, то система всегда будет брать самый первый, который найдёт. Для решения этой проблемы есть несколько способов:
- Указать номер элемента
browser.all('#currentAddress')[1]
илиbrowser.all("#currentAddress").first
. Если нужен второй элементbrowser.all("#currentAddress").second
- Найти элемент в два подхода
browser.element('#output #currentAddress')
илиbrowser.element('#output').element('#currentAddress')
XPath-селекторы
Примеры отличия XPath-селекторов от CSS-селекторов:
CSS | XPath |
---|---|
browser.element('#userName') | browser.element('//*[@id="userName"]') |
browse.all('#output p') | browser.all('//*[@id="output"]//p') |
Если необходимо обратиться к элементу по атрибуту for="gender-radio-1"
, то запись будет выглядеть так:
browser.element('[for="gender-radio-1"]')
Все атрибуты записываются в квадратных скобках.
Base URL
Чтобы не писать полностью путь к странице можно ее основную часть вынести в base_url:
from selene import browser
browser.config.base_url = 'https://demoqa.com' # базовый URL. Данные конфигурации нужно выносить в отдельный файл conftest.py в виде фикстуры
browser.open('/automation-practice-form') # откроется https://demoqa.com/automation-practice-form
Выносить в base_url нужно только те части, которые не будут меняться. Выносить урл в base_url нужно когда у вас много тестов на один и тот же сайт, и чтобы не дублировать код.
Работа с разными браузерами
По умолчанию Selene использует браузер Chrome. Если нужно использовать другой браузер, то нужно указать его в конфигурации:
Пример для Firefox:
from selene import browser
browser.config.driver_name = 'firefox'
или
from selene import browser
from selenium import webdriver
browser.config.driver = webdriver.Firefox() # или если нужно добавлять специальныи опции то используют `FirefoxOptions()`
Пример для Edge:
from selene import browser
`browser.config.driver_name = 'edge'`
или
browser.config.driver = webdriver.Edge() # или если нужно добавлять специальныи опции то используют `EdgeOptions()`
Поиск одного элемента
from selene import browser
browser.element('.main-header')
Команда browser.element('селектор')
находит только один элемент. Если элементов несколько, то будет найден только первый.
Если нужно обратиться к определенному элементу, что находится внутри другого, то нужно использовать следующую запись:
browser.element('.main-header').element('.sub-header')
Таким образом мы ищем элемент с классом main-header
и внутри него ищем элемент с классом sub-header
.
Поиск несколько элементов
from selene import browser
browser.all('.main-header')
Команда browser.all('селектор')
находит все элементы, которые соответствуют селектору. Возвращает список элементов. Чтобы обратиться к определенному элементу, нужно добавлять уточнение.
Пример:
from selene import browser
browser.all('.main-header').first.should(have.text('Box')) # обращение к первому элементу и проверяется, что он содержит текст «Box»
Ввод текста в поле
В Selene ввод текста в поле осуществляется методом type
. В этом методе вводится текст, по одной букве(симуляция нажатия клавиш пользователем).
from selene import browser
browser.element('#firstName').type('Иванов Иван')
Если нужно ввести текст сразу, то нужно использовать ввод через js, а именно использовать метод set_value
:
from selene import browser
browser.element('#firstName').set_value('Иванов Иван')
Проверки элементов
Все проверки в Selene начинаются с should
. Например, проверка на наличие текста:
from selene import browser, have
browser.element('#firstName').should(have.value('Иванов Иван'))
Опции для браузера
Пример headless-режима(без отображения окна браузера):
from selene import browser
from selenium import webdriver
driver_options = webdriver.ChromeOptions()
driver_options.add_argument('--headless') # вместо этой строки можно добавить другие опции
browser.config.driver_options = driver_options
Время ожидания
По умолчанию глобальное время ожидания элементов 4 секунды. Если нужно увеличить время ожидания, то нужно указать его в конфигурации:
from selene import browser
browser.config.timeout = 20.0
Если необходимо увеличить время ожидания только для конкретного элемента, то нужно указать его перед командой проверки should
:
from selene import browser, have
browser.element('#firstName').with_(timeout=15).should(have.value('Иванов Ива'))
Данное время ожидания будет действовать только для этого элемента и оно главнее глобального.
Команды для работы с элементами
from selene import browser, command
browser.element('element').perform(command.js.click) # клик по элементу
browser.element('element').perform(command.js.scroll_into_view) # прокрутка до элемента
Изменение масштаба страницы
from selene import browser
browser.driver.execute_script("document.querySelector('.body-height').style.transform='scale(.65)'") # уменьшение масштаба страницы до 65%
Загрузка файла
Ниже приведен пример загрузки файла если есть тег input
с атрибутом type="file"
:
from selene import browser
import os
browser.element('element').send_keys(os.path.abspath('picture.png')) # загрузка файла. Вместо 'picture.png' указать путь к файлу и наименование файла.
Если нет тега input
с атрибутом type="file"
, то нужно использовать метод drop_file
из `command.js:
from selene import browser, command
import os
browser.element('element').perform(command.js.drop_file(os.path.abspath('img.png'))) # загрузка файла. Вместо 'img.png' указать путь к файлу и наименование файла.
Путь os.path.abspath('picture.png') указывает на файл, который находится в той же директории, что и тест.
Если нужно более универсальное решение, которое будет находить файл в любой директории запуска теста, то можно использовать следующую запись:
from pathlib import Path
def path(file_name):
return str(Path(__file__).parent.parent.joinpath(f'resources/{file_name}'))
В данном случае файл picture.png находится в директории resources.
Path(file) - это путь к файлу в котором лежит код.
parent.parent - это путь который мы получаем, чтобы добраться от этого файла до корня(до папки, где лежит проект). Количество parent зависит от того, насколько глубоко лежит файл с кодом.
joinpath- это как '+' -добавляем к тому что есть уже путь.
(f'resources/{file_name}') -указываем папку, где лежит файл и переменную для подстановки названия файла. Если файл лежит в корне проекта, то можно убрать 'resources/'.
Работа со слайдером
Если нужно переместить ползунок в слайдере и нет значение на странице куда именно его переместить, то можно использовать метод drag_and_drop_by_offset
из command
:
from selene import browser, command
browser.element('element').perform(command.drag_and_drop_by_offset(x=277, y=0)) # перемещение слайдера на 277 пикселей вправо(ось x), если нужно влево, то значение y должно быть отрицательным.
Если на странице есть значение, куда нужно переместить слайдер, то можно использовать метод drag_and_drop_to
из command
:
from selene import browser, command
browser.element('element').perform(command.drag_and_drop_to('element2'))
Где element
- это элемент, который нужно переместить(к примеру это точка на слайдере), а element2
- это элемент, куда нужно переместить.