为什么我可以点击 type=radio 的 h:selectOneRadio 输入,而不是 p:selectOneRadio 和 Graphene/Selenium 的输入?
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
在 table 中产生 <input type="radio">
,在 table 中产生 p:selectOneRadio
,在 table 中产生 p:selectOneRadio
,一些 div
在 input
附近。两者的 ID 都是 [form id]:[selectOneRadio id]:[option number]
,当使用 @FindBy(id="[...]")
访问它时,我可以在 Graphene 功能测试中成功地将其用于普通 JSF,而 PrimeFaces 变体由于 org.openqa.selenium.ElementNotInteractableException
而失败。调查生成的 HTML 我没有看到差异
<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&v=5.0">
<script type="text/javascript" src="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/jquery/jquery.js.xhtml;jsessionid=48ca919d0b7e89661f92149ac321?ln=primefaces&v=5.0"></script>
<script type="text/javascript" src="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/primefaces.js.xhtml;jsessionid=48ca919d0b7e89661f92149ac321?ln=primefaces&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
的任何其他原因,我也不会。
访问完成
@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 侦听器的解决方案!
我也对通用方法感兴趣,例如p:selectOneButton
产生
<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 没有任何共同之处。或许有窍门。
我正在使用 PrimeFaces 6.1。
ui-helper-hidden-accessible
是一个 JQuery 布局助手,用于在视觉上隐藏项目。你看到的单选按钮其实就是父元素,<div class="ui-radiobutton ui-widget">
.
单击单选按钮标签可以解决该问题。 “for”属性 put the focus on the input label associated with it
@FindBy(css = "[for='mainForm:mainSelectOneRadioPrime:0']")
private WebElement mainSelectOneRadioPrimeOption0;
@Guy 回答正确。
这里的问题是 PrimeFaces 正在 HTML 之上应用自己的样式,它覆盖了您尝试单击的元素。
Selenium 检查目标元素 mainForm:mainSelectOneRadio:0
是否在单击顶部元素时收到事件。但是在这种情况下,覆盖是通过一个不是目标元素的后代的兄弟容器完成的。因此 Selenium 假定该元素不会接收事件并引发 ElementNotInteractableException
(参见 event bubbling and propagation)。
您可以通过访问 oneRadio.xhtml 并通过右键单击检查单选按钮来清楚地看到问题。您会看到所选 DOM 元素和 <input>
位于 DOM 树的两个不同分支中。
要解决此问题,请单击标签,因为它没有叠加层(请参阅@Guy 的解决方案)。该标签具有 for
属性,这意味着所有事件都将转发到分配给 for
的元素,该元素是目标 <input>
.
您也可以直接点击覆盖。不过,您必须使用 XPath 来表达这种关系。
目标 <input>
父级的父级:
@FindBy(xpath = "id('mainForm:mainSelectOneRadioPrime:2')/../..")
private WebElement mainSelectOneRadioPrimeOption0;
或第一个 <td>
有目标 <input>
:
@FindBy(xpath = "//td[.//input[@id='mainForm:mainSelectOneRadioPrime:2']]")
private WebElement mainSelectOneRadioPrimeOption0;
A h:selectOneRadio
在 table 中产生 <input type="radio">
,在 table 中产生 p:selectOneRadio
,在 table 中产生 p:selectOneRadio
,一些 div
在 input
附近。两者的 ID 都是 [form id]:[selectOneRadio id]:[option number]
,当使用 @FindBy(id="[...]")
访问它时,我可以在 Graphene 功能测试中成功地将其用于普通 JSF,而 PrimeFaces 变体由于 org.openqa.selenium.ElementNotInteractableException
而失败。调查生成的 HTML 我没有看到差异
<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&v=5.0">
<script type="text/javascript" src="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/jquery/jquery.js.xhtml;jsessionid=48ca919d0b7e89661f92149ac321?ln=primefaces&v=5.0"></script>
<script type="text/javascript" src="/34696ceb-eeaa-4b35-88dd-f3c8fc5901bf/javax.faces.resource/primefaces.js.xhtml;jsessionid=48ca919d0b7e89661f92149ac321?ln=primefaces&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
的任何其他原因,我也不会。
访问完成
@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 侦听器的解决方案!
我也对通用方法感兴趣,例如p:selectOneButton
产生
<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 没有任何共同之处。或许有窍门。
我正在使用 PrimeFaces 6.1。
ui-helper-hidden-accessible
是一个 JQuery 布局助手,用于在视觉上隐藏项目。你看到的单选按钮其实就是父元素,<div class="ui-radiobutton ui-widget">
.
单击单选按钮标签可以解决该问题。 “for”属性 put the focus on the input label associated with it
@FindBy(css = "[for='mainForm:mainSelectOneRadioPrime:0']")
private WebElement mainSelectOneRadioPrimeOption0;
@Guy 回答正确。
这里的问题是 PrimeFaces 正在 HTML 之上应用自己的样式,它覆盖了您尝试单击的元素。
Selenium 检查目标元素 mainForm:mainSelectOneRadio:0
是否在单击顶部元素时收到事件。但是在这种情况下,覆盖是通过一个不是目标元素的后代的兄弟容器完成的。因此 Selenium 假定该元素不会接收事件并引发 ElementNotInteractableException
(参见 event bubbling and propagation)。
您可以通过访问 oneRadio.xhtml 并通过右键单击检查单选按钮来清楚地看到问题。您会看到所选 DOM 元素和 <input>
位于 DOM 树的两个不同分支中。
要解决此问题,请单击标签,因为它没有叠加层(请参阅@Guy 的解决方案)。该标签具有 for
属性,这意味着所有事件都将转发到分配给 for
的元素,该元素是目标 <input>
.
您也可以直接点击覆盖。不过,您必须使用 XPath 来表达这种关系。
目标 <input>
父级的父级:
@FindBy(xpath = "id('mainForm:mainSelectOneRadioPrime:2')/../..")
private WebElement mainSelectOneRadioPrimeOption0;
或第一个 <td>
有目标 <input>
:
@FindBy(xpath = "//td[.//input[@id='mainForm:mainSelectOneRadioPrime:2']]")
private WebElement mainSelectOneRadioPrimeOption0;