为什么我单击带有h:selectOneRadio的type = radio的输入,而不单击带有石墨烯/硒的p:selectOneRadio的输入? [英] Why can I click an input with type=radio of a h:selectOneRadio, but not one of a p:selectOneRadio with Graphene/Selenium?

查看:146
本文介绍了为什么我单击带有h:selectOneRadio的type = radio的输入,而不单击带有石墨烯/硒的p:selectOneRadio的输入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

h:selectOneRadio在表中产生<input type="radio">,在表中的<input type="radio">中产生p:selectOneRadio,在input周围有一些div.两者的ID都是[form id]:[selectOneRadio id]:[option number],使用@FindBy(id="[...]")访问它时,我可以在石墨烯功能测试中成功地将其用于普通JSF,而PrimeFaces变体由于org.openqa.selenium.ElementNotInteractableException而失败.调查生成的HTML,我看不出有什么区别

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>

如果我在Payara 4.1.2或ElementNotInteractableException的任何其他原因上部署应用程序,也不会.

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

访问完成

@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();
    }
}

我正在寻找一种触发JSF操作方法和AJAX侦听器的解决方案!

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

我也会对通用方法感兴趣,例如p:selectOneButton产生

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">

乍一看似乎与为p:selectOneRadio生成的HTML没有任何共同之处.也许有一个窍门.

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

可以在> https://github.com/krichter722/graphene中找到SSCCE -click-input-radio .

我正在使用PrimeFaces 6.1.

I'm using PrimeFaces 6.1.

推荐答案

@Guy的答案正确.

@Guy has the right answer.

这里的问题是PrimeFaces在HTML之上应用了自己的样式,该样式覆盖了您要单击的元素.

The issue here is that PrimeFaces is applying its own styling on top of the HTML which is covering the element you are trying to click.

Selenium在单击顶部的元素时检查目标元素mainForm:mainSelectOneRadio:0是否接收到事件.但是在这种情况下,覆盖是通过同级容器完成的,而同级容器不是目标元素的后代.因此,Selenium假定该元素将不会接收事件,并引发ElementNotInteractableException(请参阅事件冒泡和传播).

Selenium checks that the targeted element mainForm:mainSelectOneRadio:0 receives the events when the element on top is clicked. But in this case the overlay is done with a sibling container which is not an descendant of targeted element. Thus Selenium assumes that the element will not receive the events and raises an ElementNotInteractableException (see event bubbling and propagation).

通过访问 oneRadio.xhtml ,您可以清楚地看到此问题.并通过右键单击检查单选按钮.您会看到所选的DOM元素和<input>位于DOM树的两个不同分支中.

You can clearly see the issue by visiting oneRadio.xhtml and by inspecting the radio button with a right click. You'll see that the selected DOM element and the <input> are located in two different branches of the DOM tree.

要解决此问题,请单击标签,因为它没有覆盖层(请参阅@Guy的解决方案).标签具有for属性,这意味着所有事件都转发到分配给目标<input>for元素.

To overcome this issue, either click the label since it has no overlay (see solution from @Guy). The label has the for attribute which mean that all the events are forwarded to the element assigned to for which is the targeted <input>.

您也可以直接单击叠加层.不过,您必须使用XPath来表达这种关系.

You could also directly click the overlay. Though, you'll have to use an XPath to express the relationship.

目标<input>的父母的父母:

    @FindBy(xpath = "id('mainForm:mainSelectOneRadioPrime:2')/../..")  
    private WebElement mainSelectOneRadioPrimeOption0;

或具有目标<input>的第一个<td>:

    @FindBy(xpath = "//td[.//input[@id='mainForm:mainSelectOneRadioPrime:2']]")
    private WebElement mainSelectOneRadioPrimeOption0;

这篇关于为什么我单击带有h:selectOneRadio的type = radio的输入,而不单击带有石墨烯/硒的p:selectOneRadio的输入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆