Similar to Why can I click an input with type=radio of a h:selectOneRadio, but not one of a p:selectOneRadio with Graphene/Selenium? I'd like to know how I can check a p:selectBooleanCheckbox
in a functional tests with Arquillian and Selenium/Graphene and wait for it to be checked.
I already figured out
checked
attribute, but by its presence and that in XHTML (where the attribute needs to be always present) by the value checked
(not true
or false
, but why make a new standard intuitive...), see https://www.w3schools.com/tags/att_input_checked.asp for details.""
and null
for example in some cases, like ExpectedConditions.attributeToBe(WebElement, String, String)
and doesn't mention it - and then the request to improve it get closed with a hint that the Javadoc is accepted to be wrong because everything is well documented in the W3C web driver spec.WebElement.click
(where the WebElement
instance refers to the id of p:selectBooleanCheckbox
works in a minimal environment, but not in a complex one.The checkbox is checked when I investigate visually with Firefox after the checkbox is clicked with WebElement.click
on the @FindBy(id="mainForm:mainCheckbox")
element declared in JSF:
<h:form id="mainForm">
<p:selectBooleanCheckbox id="mainCheckbox"
value="#{managedBeanView.property0}"/>
</h:form>
I tried:
checked
attribute of the generated div
which simply always is present (or maybe not, see trouble with Selenium documentation above)checked
attribute of the nested HTML _input
which never the attribute checked
apparently (or maybe not, see above).There's the pretty awesome arquillian-primefaces project now which provides a one-liner. In case you can't use this:
/**
* Checks a {@code p:selectBooleanCheckbox} and waits for it to be checked.
* The method works for XHTML elements only and does not check whether the
* passed {@code checkbox} is declared in XHTML.
*
* The handling of Primefaces in functional testing with Selenium/Graphene
* is extremely difficult because the behaviour of elements is not
* documented in a usable way (either extremely complex for Primefaces
* developers or not at all), so that the following behaviour has been
* extracted:<ul>
* <li>The checked state of HTML input element which is a checkbox is
* determined by the presence of the {@code checked} attribute and not its
* value. In XHTML where the attribute has to be always present its the
* value {@code checked} of the attribute .See
* {@link https://www.w3schools.com/tags/att_input_checked.asp} for details.
* </li>
* <li>The {@code checked} attribute on {@code checkbox} is always
* {@code null} and
* {@code ExpectedConditions.attributeToBe(checkbox, "checked", "")} is
* always {@code true} (Selenium doesn't document whether it distinguishes
* between {@code ""} and {@code null} and refused all suggestions on
* improving the documentation (see e.g.
* https://github.com/SeleniumHQ/selenium/issues/5649)).</li>
* <li>{@link WebElement#click() } works in a minimal environment (the
* checked state appears visually) whereas it doesn't work in most real
* world conditions.</li>
* </ul>
*
* Asked
* https://stackoverflow.com/questions/46765542/how-to-access-primefaces-components-through-graphene-in-the-most-portable-way
* for a general solution.
*
* @param browser the web driver to use
* @param checkbox the web element representing the {@code p:selectBooleanCheckbox}
* @param checked whether to check or uncheck the checkbox
*/
public void checkCheckbox(WebDriver browser,
WebElement checkbox,
boolean checked) {
assert checkbox.getAttribute("id") != null
&& !checkbox.getAttribute("id").isEmpty();
String checkboxInputId = String.format("%s_input",
checkbox.getAttribute("id"));
final WebElement checkboxInputElement = checkbox.findElements(By.xpath(String.format("id('%s')/div[2]/span",
checkbox.getAttribute("id"))))
.stream().collect(MoreCollectors.onlyElement());
if(checked && !isCheckboxChecked(browser, checkboxInputId)
|| !checked && isCheckboxChecked(browser, checkboxInputId)) {
LOGGER.trace("checkCheckbox: need to click");
LOGGER.trace(String.format("checkboxInputElement.selected: %s",
isCheckboxChecked(browser, checkboxInputId)));
checkboxInputElement.click();
LOGGER.trace(String.format("checkboxInputElement.selected: %s",
isCheckboxChecked(browser, checkboxInputId)));
//- cannot invoke `checkboxInputElement.click` because the element
//is not visible
//- cannot invoke `checkboxInputElement.isSelected` or
//`checkbox.isSelected` (causes
//org.openqa.selenium.ElementNotSelectableException)
}
new WebDriverWait(browser, 5).until(
(WebDriver t) -> {
boolean retValue0 = isCheckboxChecked(browser,
checkboxInputId) == checked;
return retValue0;
});
//waiting for the checked attribute is no guarantee that the check
//will be visible on the screenshot
}
private boolean isCheckboxChecked(WebDriver browser,
String checkboxInputId) {
boolean retValue = (Boolean) ((JavascriptExecutor)browser).executeScript(String.format("return document.getElementById('%s').checked;",
checkboxInputId));
return retValue;
}
I figured out the right elements using the Selenium IDE which will probably help in all other similar situations.