Example 4
In the previous example, we learned how to work with lists of simple elements. However, most often when working with an interface you encounter sections grouped into blocks, i.e. widgets.
These can be any kind of entities: table rows divided into cells by columns; image blocks with descriptions and metadata; list rows composed of several blocks with values; etc.
For a clear example, let’s take a composite widget such as an Accordion.
This is an excellent example because this complex widget consists of blocks that include only two elements: a header and the text inside the block. The logic is simple – when you click on a header, the corresponding block expands while the previous one collapses.
1. Define the 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. Create a test scenario in which you expand the accordion blocks in a different order and verify that the text blocks contain specific excerpts:
@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");
}
Let's view the Selenide Allure report:
1. Define the 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();
}
}
}
In this case our class becomes more structured. The list of blocks is created using ListWC
, and to describe the structure of an accordion block we use the class AccordionSection
. Note the use of the @LocatorChain
annotation to build a locator within the widget. By overriding the getId()
method, we can extract widgets from the list by the header text, for example:
accordionPage.accordionSections().get("Chapter 3").title().click();
2. Define the test scenario
@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");
}
Thus, we did not have to describe search via filters and manual logging of steps – everything happens automatically.