如何检查 p:selectBooleanCheckbox 并等待它在 Graphene/Selenium 中检查?

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

类似于 我想知道如何在 Arquillian 和 Selenium/Graphene 的功能测试中检查 p:selectBooleanCheckbox 并等待它被检查。

我已经想通了

在 JSF 中声明的 @FindBy(id="mainForm:mainCheckbox") 元素上用 WebElement.click 单击复选框后,当我用 Firefox 进行视觉调查时,复选框被选中:

<h:form id="mainForm">
    <p:selectBooleanCheckbox id="mainCheckbox"
                             value="#{managedBeanView.property0}"/>
</h:form>

我试过了:

现在有一个非常棒的 arquillian-primefaces project,它提供了 one-liner。如果你不能使用这个:

/**
 * 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
 * 
 * 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;
}

我使用 Selenium IDE 找到了正确的元素,这可能会在所有其他类似情况下提供帮助。