Пример 4
В прошлом примере разобрались, как работать со списками простых элементов. Однако чаще всего при работе с интерфейсом встречаются сгруппированные в блоки части интерфейса, т.е. виджеты.
Это могут быть сколь угодно разные сущности: строки таблиц, разбитые по столбцам на ячейки; блоки изображений с описаниями и метаданными; строки списков, состоящие из нескольких блоков со значениями и т.д.
Для наглядного примера возьмём такой составной виджет, как Accordion.
Это отличный вариант для примера, так как данный комплексный виджет состоит из блоков, включающих всего по два элемента: заголовок и текст внутри блока. Логика проста – при нажатии на заголовок соответствующий блок разворачивается, а предыдущий сворачивается.
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:
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");
}
Таким образом, нам не пришлось описывать поиск через фильтры и ручное логирование шагов – всё происходит автоматически.