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 - это элемент, куда нужно переместить.