seleniumjsfprimefacesjsf-2graphene2

How to check a p:selectBooleanCheckbox and wait for it to be checked in Graphene/Selenium?


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

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:


Solution

  • 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.