Список виджетов, поиск и использование

Пример 4

В прошлом примере разобрались, как работать со списками простых элементов. Однако чаще всего при работе с интерфейсом встречаются сгруппированные в блоки части интерфейса, т.е. виджеты.

Это могут быть сколь угодно разные сущности: строки таблиц, разбитые по столбцам на ячейки; блоки изображений с описаниями и метаданными; строки списков, состоящие из нескольких блоков со значениями и т.д.

Для наглядного примера возьмём такой составной виджет, как Accordion.



Это отличный вариант для примера, так как данный комплексный виджет состоит из блоков, включающих всего по два элемента: заголовок и текст внутри блока. Логика проста – при нажатии на заголовок соответствующий блок разворачивается, а предыдущий сворачивается.

Вариант на Selenide

1. Описываем Page Object

@Getter
@Accessors(fluent = true)
public class AccordionPage extends Page {

    protected ElementsCollection sections = $$(".card").as("Accordion sections");

    @Step("Click on {chapterName} in accordion widget")
    public SelenideElement clickOnSectionLabel(String chapterName) {
        SelenideElement chapterHead = sections.stream()
            .filter(section -> section.text().contains(chapterName))
            .findFirst().get()
            .$(".card-header button").as("Chapter head");
        chapterHead.click();
        return chapterHead;
    }

    @Step("Assert accordion's chapter {chapterName} has text {text}")
    public void assertTextHas(String chapterName, String text) {
        String targetText = sections.stream()
            .filter(section -> section.text().contains(chapterName))
            .findFirst().get()
            .$(".collapse .card-body").text();
        Assertions.assertThat(targetText).as("chapter content").contains(text);
    }
}

2. Создаём сценарий, в котором в разном порядке разворачиваем блоки аккордеона и проверяем, что текстовые блоки содержат конкретные отрывки:

@Test
@Feature("List of widgets")
@DisplayName("Walk through accordion widget, asserting text patterns. All passed.")
public void accordionPassedTest() {
    Selenide.open(accordionPageUrl);
    accordionPage.clickOnSectionLabel("Chapter 2");
    accordionPage.clickOnSectionLabel("Chapter 3");
    accordionPage.clickOnSectionLabel("Chapter 4");
    accordionPage.clickOnSectionLabel("Chapter 5");
    accordionPage.clickOnSectionLabel("Chapter 1");
    accordionPage.assertTextHas("Chapter 1", "Once upon a time");
    accordionPage.clickOnSectionLabel("Chapter 2");
    accordionPage.assertTextHas("Chapter 2", "resided in the structured");
    accordionPage.clickOnSectionLabel("Chapter 3");
    accordionPage.assertTextHas("Chapter 3", "As Ajax traveled");
    accordionPage.clickOnSectionLabel("Chapter 4");
    accordionPage.assertTextHas("Chapter 4", "the art of dynamic typing");
    accordionPage.clickOnSectionLabel("Chapter 5");
    accordionPage.assertTextHas("Chapter 5", "As Ajax returned to the City");
}

Смотрим отчёт Allure:

Отчёт Selenide: Accordion

Вариант на Allurium

1. Описываем Page Object

@PageObject
@Getter
@Accessors(fluent = true)
public class AccordionPage extends Page {

    @Name("Accordion categories")
    @ListLocator(css = "#accordion .card")
    protected ListWC<AccordionSection> accordionSections = new ListWC<>();

    @Widget
    @Getter
    @Accessors(fluent = true)
    public static class AccordionSection extends AbstractWidget {

        @Name("Chapter name")
        @LocatorChain(css = "h5 button")
        protected Button title;

        @Name("Chapter story")
        @LocatorChain(css = ".collapse")
        protected Text textContent;

        public AccordionSection(SelenideElement root) {
            super(root);
        }

        @Override
        public String getId() {
            return title.text();
        }
    }
}

В данном случае наш класс стал более структурированным. Для создания списка блоков используется ListWC, а для описания структуры блока аккордеона – класс AccordionSection. Обратите внимание на использование аннотации @LocatorChain для построения локатора внутри виджета. Благодаря переопределению метода getId() мы можем извлекать виджеты из списка по тексту заголовка, например:

accordionPage.accordionSections().get("Chapter 3").title().click();

2. Описываем сценарий

@Test
@Feature("List of widgets")
@DisplayName("Walk through accordion widget, asserting text patterns. All passed.")
public void accordionPassedTest() {
    UiSteps.openBrowser(accordionPageUrl);
    accordionPage.accordionSections().get("Chapter 2").title().click();
    accordionPage.accordionSections().get("Chapter 3").title().click();
    accordionPage.accordionSections().get("Chapter 4").title().click();
    accordionPage.accordionSections().get("Chapter 5").title().click();
    accordionPage.accordionSections().get("Chapter 1").title().click();
    accordionPage.accordionSections().get("Chapter 1").textContent().assertHasText("Once upon a time");
    accordionPage.accordionSections().get("Chapter 2").title().click();
    accordionPage.accordionSections().get("Chapter 2").textContent().assertHasText("resided in the structured");
    accordionPage.accordionSections().get("Chapter 3").title().click();
    accordionPage.accordionSections().get("Chapter 3").textContent().assertHasText("As Ajax traveled");
    accordionPage.accordionSections().get("Chapter 4").title().click();
    accordionPage.accordionSections().get("Chapter 4").textContent().assertHasText("the art of dynamic typing");
    accordionPage.accordionSections().get("Chapter 5").title().click();
    accordionPage.accordionSections().get("Chapter 5").textContent().assertHasText("As Ajax returned to the City");
}

Таким образом, нам не пришлось описывать поиск через фильтры и ручное логирование шагов – всё происходит автоматически.

Отчёт Allurium: Accordion