Использование усовершенствованных By локаторов в Page Object

Уважаемое сообщество, доброго времени суток.

В своем проекте использую следующий подход к описанию элементов - в классах Page Object храню исключительно By локаторы элементов, которые при необходимости использую в коде методов в $ и $$.

private static final By SOME_LOCATOR = By.id("123");
// ...
public void somePageObjectMethod() {
    $(SOME_LOCATOR).click();
}

Возникла идея расширить класс локатора элемента (By), чтобы получить следующие возможности:

  1. Сразу указывать user-friendly описание элемента, которое затем используется в toString() (в случае ошибки будет отображаться не только локатор, но и описание)
private static final By SOME_FIELD = By.id("123").as("Элемент Элементович");
  1. Сразу указывать в каком frame живет элемент, чтобы в теле findElement / findElements сначала происходило переключение на нужный фрейм.
private static final By SOME_FIELD = By.id("123").inFrame("parentFrame", "childFrame");
  1. Указывать необходимость ожидания, скажем jQuery.active == 0 в теле findElement / findElements . Сам класс с методом ожидания указывать Селениду единожды
// Где-то в коде автотестов
static {
        // SomeAjaxExpectant реализует нужны интерфейс всего с одним методом 
        // В реализуемом методе описываются некие действия ожидания, например jQuery.active == 0
	Selenide.addExpectant(new SomeAjaxExpectant());
}
// В пейдж обжекте
private static final By SOME_FIELD = By.id("123").withWaiting();
  1. Иметь возможность работать с параметризованными локаторами. Например в классе PageObject объявить локатор xpath, который в себе содержит некоторые значение известные лишь в ходе выполнения теста.
private static final By SOME_FIELD = By.xpath("//div[@someAttr='{0}']");

public void someMethod() {
	$(SOME_FIELD.withParams("some attribute value")).click();
}

Что думаете? Кому-нибудь это было бы полезным? Плюсы и минусы ?

1 лайк

Первый пункт был бы сразу полезен мне на проектах, т.к. описание элемента в той же строке что и сам элемент упрощает и уменьшает код, не надо использовать большие конструкции комментариев.
Второй пункт полезен будет не всем и не сразу, но для некоторых частей будет полезно, но я так понимаю, что после отработки метода, переключаться на родительский фрейм надо будет вручную? Где фокус остаётся после отработки этого метода?
Третий пункт полезен, т.к. все мы сталкиваемся с аяксами, и у всех есть методы ожидания, надо это всё собрать в одно место, когда мы все используем Селенид.
Четвёртый пункт тоже полезен, много видел вопросов на форумах про такую штуку, здесь всё понятно и просто.
Если это всё реализовано, то почему бы не состыковаться с уважаемым Андреем и обговорить коммиты. Дмитрий, а тесты для этих изменений есть?

Спасибо за комментарий!
Немного проясню по пунктам:
2) Фокус останется в том фрейме, на который произошло переключение перед поиском элемента.
Если фрейм изначально у By не задан, то никакого переключения не будет. Если же элемент живет в корне, то можно объявить локатор так:

private static final By SOME_FIELD = By.id("123").inRoot();  // или для наглядности .inDefaultContent();

Насчет тестов - если все эти пункты действительно стоит включить в Selenide, то я конечно же сделаю PR в т.ч. и с тестами.

Критика самого подхода к хранению локаторов также приветствуется!

Поддерживаю всеми руками за! Главное чтобы Simple Report у селенида обрабатывал такую штуку: если есть метод “.as” то в колонку “Element” писать параметр метода. Или же добавить колонку для подобных описывающих методов

А мне кажется, суть и вся прелесть Selenide - это его простота и что в нем нет ничего лишнего.

А по поводу

  1. Так сейчас и выводится очень удобное описание элемента - особенно когда там какие то атрибуты то есть то нету.
  2. Фреймы - ну такое, далеко не в каждом проекте есть необходимость в них.
  3. Такая ожидалка это по моему прошлый век.
  4. Это не проблема, это НЕ знание как пользоваться xpath-ом.
    Например $(SOME_FIELD).$(By.xpath("./self::*[@disabled=‘disabled’]")) будет искать элементы SOME_FIELD с атрибутом disabled=‘disabled’.
1 лайк
  1. Читабельное текстовое описание всяко приятнее
  2. Однако такие проекты есть
  3. Если проект стабилен как часы, не перегружен полукривым ajax’ом, то такая ожидалка не нужна. Но жизнь богаче
  4. Знание как раз есть, как и желание не городить конструкции вида “./self::*[@disabled=‘disabled’]”

@dimand58 Привет!
Я ещё раз сел и внимательно перечитал и понял, что я категорически согласен с @kasheylm

Если ещё первый пункт хоть как-то ок (ну может, и правда у кого-то что-то читаемее станет), то остальные вещи надо просто по-другому решать. Второе, может, стоит ещё обдумать, но третий и четвёртый - это точно оверкил. Это можно и нужно решать проще.

Например,

  1. можно сделать публичный метод типа

    public SelenideElement divByAttr(String attributeValue) {
    return $(String.format("//div[@someAttr=’%s’]", attributeValue));
    }

  2. метод “.withWaiting();” точно не нужен хотя бы потому, что в Selenide все методы и так умеют ждать.

Предложенные примеры могут зайти как кастомная реализация для одного проекта, можете сделать свой форк и его продвигать, но в selenide - это точно не нужно.