Skip to content

Commit

Permalink
Merge pull request #224 from FluentLenium/feature/element-navigation
Browse files Browse the repository at this point in the history
Add methods to navigate through document hierarchy
  • Loading branch information
filipcynarski committed Mar 31, 2016
2 parents f00b256 + e9668b4 commit 5add6a0
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
11 changes: 11 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,17 @@ findFirst(myCssSelector).isEnabled()
findFirst(myCssSelector).isSelected()
```

If you need to retrieve other elements from a selected one, you
can use [XPath axes](http://www.w3schools.com/xsl/xpath_axes.asp).

find(myCssSelector()).axes().parent()
find(myCssSelector()).axes().descendants()
find(myCssSelector()).axes().ancestors()
find(myCssSelector()).axes().followings()
find(myCssSelector()).axes().followingSiblings()
find(myCssSelector()).axes().precedings()
find(myCssSelector()).axes().precedingSiblings()

## Form Actions
Clicking, filling, submitting and cleaning an element or list of elements is simple and intuitive.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.fluentlenium.core.axes;

import org.fluentlenium.core.domain.FluentList;
import org.fluentlenium.core.domain.FluentListImpl;
import org.fluentlenium.core.domain.FluentWebElement;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import java.util.ArrayList;
import java.util.List;

/**
* Handles XPath axes for an element (http://www.w3schools.com/xsl/xpath_axes.asp)
*/
public class Axes {

private final WebElement webElement;

public Axes(WebElement element) {
this.webElement = element;
}


/**
* Find parent element.
*
* @return fluent web element
*/
public FluentWebElement parent() {
WebElement parentRaw = this.webElement.findElement(By.xpath("parent::*"));
FluentWebElement parent = new FluentWebElement(parentRaw);
return parent;
}

protected FluentList<FluentWebElement> handleAxe(String axe) {
List<WebElement> ancestorsRaw = this.webElement.findElements(By.xpath(axe + "::*"));
List<FluentWebElement> elements = new ArrayList<FluentWebElement>();
for (WebElement ancestor : ancestorsRaw) {
elements.add(new FluentWebElement(ancestor));
}
return new FluentListImpl<>(elements);
}

/**
* Find ancestor elements.
*
* @return list of Fluent web elements
*/
public FluentList<FluentWebElement> ancestors() {
return handleAxe("ancestor");
}

/**
* Find descendants elements (children, grandchildren, etc.).
*
* @return list of Fluent web elements
*/
public FluentList<FluentWebElement> descendants() {
return handleAxe("descendant");
}

/**
* Find following elements.
*
* @return list of Fluent web elements
*/
public FluentList<FluentWebElement> followings() {
return handleAxe("following");
}

/**
* Find following sibling elements.
*
* @return list of Fluent web elements
*/
public FluentList<FluentWebElement> followingSiblings() {
return handleAxe("following-sibling");
}

/**
* Find preceding elements. (Ancestors are NOT included)
*
* @return list of Fluent web elements
*/
public FluentList<FluentWebElement> precedings() {
return handleAxe("preceding");
}

/**
* Find preceding sibling elements.
*
* @return list of Fluent web elements
*/
public FluentList<FluentWebElement> precedingSiblings() {
return handleAxe("preceding-sibling");
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.fluentlenium.core.action.FillConstructor;
import org.fluentlenium.core.action.FillSelectConstructor;
import org.fluentlenium.core.action.FluentDefaultActions;
import org.fluentlenium.core.axes.Axes;
import org.fluentlenium.core.filter.Filter;
import org.fluentlenium.core.search.Search;
import org.fluentlenium.core.search.SearchActions;
Expand All @@ -18,10 +19,12 @@
public class FluentWebElement implements FluentDefaultActions<FluentWebElement>, SearchActions<FluentWebElement> {
private final WebElement webElement;
private final Search search;
private final Axes axes;

public FluentWebElement(WebElement webElement) {
this.webElement = webElement;
this.search = new Search(webElement);
this.axes = new Axes(webElement);
}

/**
Expand All @@ -34,6 +37,15 @@ public FluentWebElement click() {
return this;
}

/**
* XPath Axes accessor (parent, ancestors, preceding, following, ...).
*
* @return object to perform XPath Axes transformations.
*/
public Axes axes() {
return this.axes;
}

/**
* Double Click on the element
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.fluentlenium.integration;

import org.fluentlenium.adapter.FluentTest;
import org.fluentlenium.core.domain.FluentList;
import org.fluentlenium.core.domain.FluentWebElement;
import org.fluentlenium.integration.localtest.LocalFluentCase;
import org.junit.Test;

import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;

public class AxesTest extends LocalFluentCase {

@Test
public void checkSearchParentWorks() {
goTo(DEFAULT_URL);
FluentWebElement element = findFirst(".parent > .child");
assertThat(element.axes().parent().getAttribute("class")).isEqualTo("parent");
}

@Test
public void checkSearchAncestorsWorks() {
goTo(DEFAULT_URL);
FluentWebElement element = findFirst("html > body > .parent > .child");
FluentList<FluentWebElement> ancestors = element.axes().ancestors();
assertThat(ancestors).hasSize(3);

assertThat(ancestors.get(0).getTagName()).isEqualTo("html");
assertThat(ancestors.get(1).getTagName()).isEqualTo("body");
assertThat(ancestors.get(2).getTagName()).isEqualTo("span");
assertThat(ancestors.get(2).getAttribute("class")).isEqualTo("parent");
}

@Test
public void checkSearchDescendantsWorks() {
goTo(DEFAULT_URL);
FluentWebElement element = findFirst("html");
FluentList<FluentWebElement> descendants = element.axes().descendants();
assertThat(descendants.size()).isGreaterThan(10);
}

@Test
public void checkSearchPrecedingWorks() {
goTo(DEFAULT_URL);
FluentWebElement element = findFirst("#select > option[value='value-2']");
FluentList<FluentWebElement> precedings = element.axes().precedings();
assertThat(precedings.size()).isGreaterThan(2);

Collections.reverse(precedings);

assertThat(precedings.get(0).getTagName()).isEqualTo("option");
assertThat(precedings.get(1).getTagName()).isEqualTo("span");
}

@Test
public void checkSearchPrecedingSiblingWorks() {
goTo(DEFAULT_URL);
FluentWebElement element = findFirst("#select > option[value='value-2']");
FluentList<FluentWebElement> precedings = element.axes().precedingSiblings();
assertThat(precedings).hasSize(1);

assertThat(precedings.get(0).getTagName()).isEqualTo("option");
assertThat(precedings.get(0).getAttribute("value")).isEqualTo("value-1");
}

@Test
public void checkSearchFollowingWorks() {
goTo(DEFAULT_URL);
FluentWebElement element = findFirst("#select > option[value='value-2']");
FluentList<FluentWebElement> followings = element.axes().followings();
assertThat(followings.size()).isGreaterThan(2);

assertThat(followings.get(0).getTagName()).isEqualTo("option");
assertThat(followings.get(1).getTagName()).isEqualTo("input");
}

@Test
public void checkSearchFollowingSiblingWorks() {
goTo(DEFAULT_URL);
FluentWebElement element = findFirst("#select > option[value='value-2']");
FluentList<FluentWebElement> followings = element.axes().followingSiblings();
assertThat(followings).hasSize(1);

assertThat(followings.get(0).getTagName()).isEqualTo("option");
assertThat(followings.get(0).getAttribute("value")).isEqualTo("value-3");
}
}

0 comments on commit 5add6a0

Please sign in to comment.