如何扩展Selenium By.class以创建更多灵活性? [英] How can I extend the Selenium By.class to create more flexibility?
问题描述
如何扩展Selenium By.class 以创造更多灵活性?我查看了 By .class ,我不太清楚如何处理这个问题。看起来我需要创建一个接口类和一个静态By类,比如 ByJavascriptGetWebElement ,以便创建这种包装器?
How can I extend the Selenium By.class to create more flexibility? I looked at the By.class and I don't quite know how to approach this. It looks like I need to create an interface class and a static By class, such as ByJavascriptGetWebElement, in order to create this kind of wrapper?
我希望能够这样称呼:
By.javascript("return document.querySelector(\"div#item div\");", el );
我还听说过一种更简单的方法,但我更喜欢这样做传统方式:
And I have also heard of an easier way to do it but I would prefer to do it the more traditional way:
public By byJavascriptGetElement( WebElement we ) {
return By. ???
}
任何想法?
推荐答案
这实际上可以轻松完成。
This can actually be easily done.
想法是访问 WebDriver
实例并在其上运行JavaScript(如果支持它)。然后有很多验证,因为我们需要确保我们只返回我们承诺的内容。
The idea is to get access to the WebDriver
instance and run the JavaScript on it (if it supports it). Then there's a lot of validating, because we need to make sure we only return what we promised.
ByJavaScript
class本身将如下所示:
The ByJavaScript
class itself will look like this:
public class ByJavaScript extends By implements Serializable {
private final String script;
public ByJavaScript(String script) {
checkNotNull(script, "Cannot find elements with a null JavaScript expression.");
this.script = script;
}
@Override
public List<WebElement> findElements(SearchContext context) {
JavascriptExecutor js = getJavascriptExecutorFromSearchContext(context);
// call the JS, inspect and validate response
Object response = js.executeScript(script);
List<WebElement> elements = getElementListFromJsResponse(response);
// filter out the elements that aren't descendants of the context node
if (context instanceof WebElement) {
filterOutElementsWithoutCommonAncestor(elements, (WebElement)context);
}
return elements;
}
private static JavascriptExecutor getJavascriptExecutorFromSearchContext(SearchContext context) {
if (context instanceof JavascriptExecutor) {
// context is most likely the whole WebDriver
return (JavascriptExecutor)context;
}
if (context instanceof WrapsDriver) {
// context is most likely some WebElement
WebDriver driver = ((WrapsDriver)context).getWrappedDriver();
checkState(driver instanceof JavascriptExecutor, "This WebDriver doesn't support JavaScript.");
return (JavascriptExecutor)driver;
}
throw new IllegalStateException("We can't invoke JavaScript from this context.");
}
@SuppressWarnings("unchecked") // cast thoroughly checked
private static List<WebElement> getElementListFromJsResponse(Object response) {
if (response == null) {
// no element found
return Lists.newArrayList();
}
if (response instanceof WebElement) {
// a single element found
return Lists.newArrayList((WebElement)response);
}
if (response instanceof List) {
// found multiple things, check whether every one of them is a WebElement
checkArgument(
Iterables.all((List<?>)response, Predicates.instanceOf(WebElement.class)),
"The JavaScript query returned something that isn't a WebElement.");
return (List<WebElement>)response; // cast is checked as far as we can tell
}
throw new IllegalArgumentException("The JavaScript query returned something that isn't a WebElement.");
}
private static void filterOutElementsWithoutCommonAncestor(List<WebElement> elements, WebElement ancestor) {
for (Iterator<WebElement> iter = elements.iterator(); iter.hasNext(); ) {
WebElement elem = iter.next();
// iterate over ancestors
while (!elem.equals(ancestor) && !elem.getTagName().equals("html")) {
elem = elem.findElement(By.xpath("./.."));
}
if (!elem.equals(ancestor)) {
iter.remove();
}
}
}
@Override
public String toString() {
return "By.javaScript: \"" + script + "\"";
}
}
此代码使用< a href =http://guava-libraries.googlecode.com/> Google Guava库。它是Selenium的依赖项,因此您应该在类路径中使用它。但如果你有什么不明白的地方,请看看番石榴。
This code uses the Google Guava library. It is a dependency of Selenium, so you should have it on your classpath. But if there's something you don't understand, look into Guava.
需要考虑的事项:
- 记录整件事。
- 使用更好,更有帮助的例外。考虑一些
WebDriverException
的自定义子类。还添加了更多有用的消息和信息。 - 限制类对包的可见性。或者将它嵌入到静态工厂中(如原始的
By
类中所示),以便它不能直接访问等。 - 写测试。我尝试过最明显的用法(无法找到元素,从驱动程序搜索,从某些上下文搜索),一切似乎都没问题,但我没有广泛测试它。
- Document the whole thing.
- Use better and more helpful exceptions. Consider some custom subclasses of
WebDriverException
. Also add more helpful messages and info. - Restrict the class' visibility to package. Or embed it into a static factory (as seen in the original
By
class) so that it won't be accessible directly etc. - Write tests. I have tried the most obvious usages (element can't be found, search from driver, search from some context) and everything seems to be ok, but I didn't test it extensively.
用法:
WebElement elem = driver.findElement(new ByJavaScript("return document.querySelector('.haha');"));
现在,原来的按
类是一个静态工厂,它给出了自己的各种实现。不幸的是,我们无法为其添加新的静态方法(不更改其来源),因此我们将无法输入 By.javascript(return something;)
。我们必须创建自己的静态工厂来获得类似的东西:
Now, the original By
class is a static factory that gives out various implementations of itself. Unfortunately, we can't add a new static method to it (without changing its source), so we won't be able to type By.javascript("return something;")
. We have to create our own static factory to get something similar:
public class MyBy {
/**
* Returns a {@code By} which locates elements by the JavaScript expression passed to it.
*
* @param script The JavaScript expression to run and whose result to return
*/
public static By javascript(String script) {
return new ByJavaScript(script);
}
}
用法:
WebElement elem = driver.findElement(MyBy.javascript("return document.querySelector('.haha');"));
这篇关于如何扩展Selenium By.class以创建更多灵活性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!