Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I click an input with type=radio of a h:selectOneRadio, but not one of a p:selectOneRadio with Graphene/Selenium?

A h:selectOneRadio results in <input type="radio"> in a table and p:selectOneRadio in <input type="radio"> in a table with some divs around the input. The id for both is [form id]:[selectOneRadio id]:[option number] which I can use successfully for the plain JSF in a Graphene functional test when accessing it with @FindBy(id="[...]") whereas the PrimeFaces variant fails due to org.openqa.selenium.ElementNotInteractableException. Investigating the generated HTML I don't see the difference

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <link type="text/css" rel="stylesheet" href="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/theme.css.xhtml?ln=primefaces-aristo">
  <link type="text/css" rel="stylesheet" href="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/primefaces.css.xhtml;jsessionid=48ca919d0b7e89661f92149ac321?ln=primefaces&amp;v=5.0">
  <script type="text/javascript" src="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/jquery/jquery.js.xhtml;jsessionid=48ca919d0b7e89661f92149ac321?ln=primefaces&amp;v=5.0"></script>
  <script type="text/javascript" src="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/primefaces.js.xhtml;jsessionid=48ca919d0b7e89661f92149ac321?ln=primefaces&amp;v=5.0"></script>
  <title>Facelet Title</title>
</head>

<body>
  <form id="mainForm" name="mainForm" method="post" action="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/index.xhtml;jsessionid=48ca919d0b7e89661f92149ac321" enctype="application/x-www-form-urlencoded">
    <input name="mainForm" value="mainForm" type="hidden">
    <table id="mainForm:mainSelectOneRadio">
      <tbody>
        <tr>
          <td>
            <input name="mainForm:mainSelectOneRadio" id="mainForm:mainSelectOneRadio:0" value="a" type="radio">
            <label for="mainForm:mainSelectOneRadio:0"> a</label>
          </td>
          <td>
            <input name="mainForm:mainSelectOneRadio" id="mainForm:mainSelectOneRadio:1" value="b" type="radio">
            <label for="mainForm:mainSelectOneRadio:1"> b</label>
          </td>
          <td>
            <input name="mainForm:mainSelectOneRadio" id="mainForm:mainSelectOneRadio:2" value="c" type="radio">
            <label for="mainForm:mainSelectOneRadio:2"> c</label>
          </td>
        </tr>
      </tbody>
    </table>
    <table id="mainForm:mainSelectOneRadioPrime" class="ui-selectoneradio ui-widget">
      <tbody>
        <tr>
          <td>
            <div class="ui-radiobutton ui-widget">
              <div class="ui-helper-hidden-accessible">
                <input id="mainForm:mainSelectOneRadioPrime:0" name="mainForm:mainSelectOneRadioPrime" value="aPrime" type="radio">
              </div>
              <div class="ui-radiobutton-box ui-widget ui-corner-all ui-state-default"><span class="ui-radiobutton-icon ui-icon ui-icon-blank"></span>
              </div>
            </div>
          </td>
          <td>
            <label for="mainForm:mainSelectOneRadioPrime:0">aPrime</label>
          </td>
          <td>
            <div class="ui-radiobutton ui-widget">
              <div class="ui-helper-hidden-accessible">
                <input id="mainForm:mainSelectOneRadioPrime:1" name="mainForm:mainSelectOneRadioPrime" value="bPrime" type="radio">
              </div>
              <div class="ui-radiobutton-box ui-widget ui-corner-all ui-state-default"><span class="ui-radiobutton-icon ui-icon ui-icon-blank"></span>
              </div>
            </div>
          </td>
          <td>
            <label for="mainForm:mainSelectOneRadioPrime:1">bPrime</label>
          </td>
          <td>
            <div class="ui-radiobutton ui-widget">
              <div class="ui-helper-hidden-accessible">
                <input id="mainForm:mainSelectOneRadioPrime:2" name="mainForm:mainSelectOneRadioPrime" value="cPrime" type="radio">
              </div>
              <div class="ui-radiobutton-box ui-widget ui-corner-all ui-state-default"><span class="ui-radiobutton-icon ui-icon ui-icon-blank"></span>
              </div>
            </div>
          </td>
          <td>
            <label for="mainForm:mainSelectOneRadioPrime:2">cPrime</label>
          </td>
        </tr>
      </tbody>
    </table>
    <input name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-485558793831512050:990657069126697889" autocomplete="off" type="hidden">
  </form>
</body>

</html>

nor do I if I deploy the application on Payara 4.1.2 or any other reason for the ElementNotInteractableException.

The access is done with

@RunWith(Arquillian.class)
public class MyManagedBeanTest {
    private static final String WEBAPP_SRC = "src/main/webapp";
    private final static Logger LOGGER = LoggerFactory.getLogger(MyManagedBeanTest.class);

    @Deployment(testable = false)
    public static Archive<?> createDeployment0() throws TransformerException, XPathExpressionException, ParserConfigurationException, SAXException, IOException {
        WebArchive retValue = ShrinkWrap.create(WebArchive.class)
                .add(EmptyAsset.INSTANCE, "beans.xml")
                .addClasses(MyManagedBean.class)
                .addAsWebInfResource(
                        new StringAsset("<faces-config version=\"2.0\"/>"),
                        "faces-config.xml");
        Maven.configureResolver().workOffline().resolve("richtercloud:graphene-click-input-radio:war:1.0-SNAPSHOT").withoutTransitivity().asList(JavaArchive.class).forEach(dependency -> retValue.addAsLibrary(dependency));
        //add all webapp resources
        retValue.merge(ShrinkWrap.create(GenericArchive.class)
                .as(ExplodedImporter.class)
                .importDirectory(WEBAPP_SRC)
                .as(GenericArchive.class), "/", Filters.include(".*\\.(xhtml|css|js|png)$"));

        ByteArrayOutputStream archiveContentOutputStream = new ByteArrayOutputStream();
        retValue.writeTo(archiveContentOutputStream, Formatters.VERBOSE);
        LOGGER.info(archiveContentOutputStream.toString());
        return retValue;
    }

    @Drone
    private WebDriver browser;
    @ArquillianResource
    private URL deploymentUrl;
    @FindBy(id = "mainForm:mainSelectOneRadio:0")
    private WebElement mainSelectOneRadioOption0;
    @FindBy(id = "mainForm:mainSelectOneRadioPrime:0")
    private WebElement mainSelectOneRadioPrimeOption0;

    @Test
    public void testAll() {
        browser.get(deploymentUrl.toExternalForm()+"index.xhtml");
        LOGGER.debug(browser.getPageSource());
        mainSelectOneRadioOption0.click();
        mainSelectOneRadioPrimeOption0.click();
    }
}

I'm searching for a solution which triggers JSF action methods and AJAX listeners!

I'd be interested in a generic approach as well, e.g. p:selectOneButton produces

<div id="mainForm:mainSelectOneButtonPrime" class="ui-selectonebutton ui-buttonset ui-widget ui-corner-all">
    <div class="ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left">
        <input id="mainForm:mainSelectOneButtonPrime:0" name="mainForm:mainSelectOneButtonPrime" value="aPrime" class="ui-helper-hidden" type="radio">
        <span class="ui-button-text ui-c">aPrime</span>
    </div>
    <div class="ui-button ui-widget ui-state-default ui-button-text-only">
        <input id="mainForm:mainSelectOneButtonPrime:1" name="mainForm:mainSelectOneButtonPrime" value="bPrime" class="ui-helper-hidden" type="radio">
        <span class="ui-button-text ui-c">bPrime</span>
    </div>
    <div class="ui-button ui-widget ui-state-default ui-button-text-only ui-corner-right">
        <input id="mainForm:mainSelectOneButtonPrime:2" name="mainForm:mainSelectOneButtonPrime" value="cPrime" class="ui-helper-hidden" type="radio">
        <span class="ui-button-text ui-c">cPrime</span>
    </div>
</div>
<input name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-5130093024933213812:2291815208147638618" autocomplete="off" type="hidden">

which doesn't seem to have anything in common with the HTML generated for p:selectOneRadio at first sight. Maybe there's a trick.

I'm using PrimeFaces 6.1.

like image 371
Kalle Richter Avatar asked Mar 07 '23 16:03

Kalle Richter


1 Answers

ui-helper-hidden-accessible is a JQuery layout helper to hide items visually. The radio button you see is actually the parent element, <div class="ui-radiobutton ui-widget">.

The problem can be resolved by clicking on the radio button label. The 'for` attribute put the focus on the input label associated with it

@FindBy(css = "[for='mainForm:mainSelectOneRadioPrime:0']")
private WebElement mainSelectOneRadioPrimeOption0;
like image 111
Guy Avatar answered Mar 14 '23 06:03

Guy