Паттерн объектов страниц
Версия 5 WebdriverIO была разработана с учетом поддержки паттерна объектов страниц (Page Object Pattern). Благодаря введению принципа "элементы как первоклассные объекты", теперь возможно создавать крупные тестовые наборы, используя этот паттерн.
Для создания объектов страниц не требуются дополнительные пакеты. Оказывается, чистые современные классы предоставляют все необходимые функции:
- наследование между объектами страниц
- ленивая загрузка элементов
- инкапсуляция методов и действий
Цель использования объектов страниц — абстрагировать информацию о странице от самих тестов. В идеале, вы должны хранить все селекторы или специфические инструкции, уникальные для определенной страницы, в объекте страницы, чтобы вы могли запускать тесты даже после полного редизайна страницы.
Создание объекта страницы
Сначала нам нужен основной объект страницы, который мы назовем Page.js
. Он будет содержать общие селекторы или методы, которые будут наследовать все объекты страниц.
// Page.js
export default class Page {
constructor() {
this.title = 'My Page'
}
async open (path) {
await browser.url(path)
}
}
Мы всегда будем export
-ировать экземпляр объекта страницы и никогда не создавать этот экземпляр в тесте. Поскольку мы пишем end-to-end тесты, мы всегда рассматриваем страницу как конструкцию без состояния — так же, как каждый HTTP-запрос является конструкцией без состояния.
Конечно, браузер может хранить информацию о сессии и, следовательно, отображать разные страницы на основе разных сессий, но это не должно отражаться в объекте страницы. Такие изменения состояния должны быть в ваших фактических тестах.
Давайте начнем тестировать первую страницу. Для демонстрационных целей мы используем сайт The Internet от Elemental Selenium в качестве подопытного. Попробуем создать пример объекта страницы для страницы входа.
Получение селекторов с помощью Get
Первый шаг — написать все важные селекторы, которые нужны в нашем объекте login.page
, в виде функций-геттеров:
// login.page.js
import Page from './page'
class LoginPage extends Page {
get username () { return $('#username') }
get password () { return $('#password') }
get submitBtn () { return $('form button[type="submit"]') }
get flash () { return $('#flash') }
get headerLinks () { return $$('#header a') }
async open () {
await super.open('login')
}
async submit () {
await this.submitBtn.click()
}
}
export default new LoginPage()
Определение селекторов в функциях-геттерах может выглядеть немного странно, но это очень полезно. Эти функции вычисляются когда вы обращаетесь к свойству, а не когда вы создаете объект. Благодаря этому вы всегда запрашиваете элемент перед выполнением действия с ним.