- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有这个规范
Scenario Outline: Display widget
Given I have a valid connection
When I navigate to home using <browser>
Then The element in css selector #<id> > svg > g.x.axis.percent > text:nth-child(1) should be <value>
Examples:
| browser | id | valye |
| Chrome | Widget1 | 213.00 |
使用此页面定义
class BarSummaryPage
{
[FindsBy(How = How.CssSelector, Using="#{DYNAMIC-ID} > svg > g.x.axis.percent > text:nth-child(1)")]
private IWebElement Mes;
}
我需要在FindsBy
动态中配置Using
属性,如上所示:参见#{DYNAMIC-ID}
最佳答案
据我所知,这并不是现成的。 FindBy
注释采用静态 Strings
仅有的。您可能需要自定义修改 FindBy
注释处理器类似于该博主所做的:https://web.archive.org/web/20180612042724/http://brimllc.com/2011/01/selenium-2-0-webdriver-extending-findby-annotation-to-support-dynamic-idxpath/
这里的另一个讨论线程:https://groups.google.com/forum/#!topic/webdriver/awxOw0FoiYU其中 Simon Stewart 展示了如何实现这一目标的示例。
更新:
我实际上已经实现了这个,因为我需要它来尝试。我没有创建自定义查找器注释(我将来可能必须这样做)。
我为 ElementLocator
编写了实现和ElementLocatorFactory
允许使用现有注释指定的定位器进行字符串替换。如果您知道或可以在运行时确定要替换的值,那么这对您有用。
默认情况下,PageFactory
使用 classes
DefaultElementLocator
和DefaultElementLocatorFactory
ElementLocator
的实现和ElementLocatorFactory
interfaces
用于设置注释的处理,但真正的逻辑在 Annotations class
。我编写了自己的 ElementLocator
实现,和ElementLocatorFactory
并编写了我自己的版本 Annotations
来进行处理。只是和我定制的classes
的来源有一些区别以及 Selenium 源代码中的内容。
public class DynamicElementLocator implements ElementLocator {
private static final XLogger log = XLoggerFactory.getXLogger(DynamicElementLocator.class.getCanonicalName());
private final SearchContext searchContext;
private final boolean shouldCache;
private final By by;
private WebElement cachedElement;
private List<WebElement> cachedElementList;
//The only thing that differs from DefaultElementLocator is
//the substitutions parameter for this method.
public DynamicElementLocator(final SearchContext searchContext, final Field field, final Map<String,String>
substitutions) {
log.entry(searchContext, field, substitutions);
this.searchContext = searchContext;
//DynamicAnnotations is my implementation of annotation processing
//that uses the substitutions to find and replace values in the
//locator strings in the FindBy, FindAll, FindBys annotations
DynamicAnnotations annotations = new DynamicAnnotations(field, substitutions);
shouldCache = annotations.isLookupCached();
by = annotations.buildBy();
log.debug("Successful completion of the dynamic element locator");
log.exit();
}
/**
* Find the element.
*/
public WebElement findElement() {
log.entry();
if (cachedElement != null && shouldCache) {
return log.exit(cachedElement);
}
WebElement element = searchContext.findElement(by);
if (shouldCache) {
cachedElement = element;
}
return log.exit(element);
}
/**
* Find the element list.
*/
public List<WebElement> findElements() {
log.entry();
if (cachedElementList != null && shouldCache) {
return log.exit(cachedElementList);
}
List<WebElement> elements = searchContext.findElements(by);
if (shouldCache) {
cachedElementList = elements;
}
return log.exit(elements);
}
}
这是 DynamicElementLocatorFactory
:
public final class DynamicElementLocatorFactory implements ElementLocatorFactory {
private final SearchContext searchContext;
private final Map<String,String> substitutions;
//The only thing that is different from DefaultElementLocatorFactory
//is that the constructor for this class takes the substitutions
//parameter that consists of the key/value mappings to use
//for substituting keys in locator strings for FindBy, FindAll and
//FindBys with values known or determined at runtime.
public DynamicElementLocatorFactory(final SearchContext searchContext, final Map<String,String> substitutions) {
this.searchContext = searchContext;
this.substitutions = substitutions;
}
//This produces an instance of the DynamicElementLocator class and
//specifies the key value mappings to substitute in locator Strings
public DynamicElementLocator createLocator(final Field field) {
return new DynamicElementLocator(searchContext, field, substitutions);
}
}
这是我的自定义注释处理器。这是大部分工作的地方:
public class DynamicAnnotations extends Annotations {
private static final XLogger log = XLoggerFactory.getXLogger(DynamicAnnotations.class.getCanonicalName());
private final Field field;
private final Map<String,String> substitutions;
//Again, not much is different from the Selenium default class here
//other than the additional substitutions parameter
public DynamicAnnotations(final Field field, final Map<String,String> substitutions) {
super(field);
log.entry(field, substitutions);
this.field = field;
this.substitutions = substitutions;
log.debug("Successful completion of the dynamic annotations constructor");
log.exit();
}
public boolean isLookupCached() {
log.entry();
return log.exit((field.getAnnotation(CacheLookup.class) != null));
}
public By buildBy() {
log.entry();
assertValidAnnotations();
By ans = null;
FindBys findBys = field.getAnnotation(FindBys.class);
if (findBys != null) {
log.debug("Building a chained locator");
ans = buildByFromFindBys(findBys);
}
FindAll findAll = field.getAnnotation(FindAll.class);
if (ans == null && findAll != null) {
log.debug("Building a find by one of locator");
ans = buildBysFromFindByOneOf(findAll);
}
FindBy findBy = field.getAnnotation(FindBy.class);
if (ans == null && findBy != null) {
log.debug("Building an ordinary locator");
ans = buildByFromFindBy(findBy);
}
if (ans == null) {
log.debug("No locator annotation specified, so building a locator for id or name based on field name");
ans = buildByFromDefault();
}
if (ans == null) {
throw log.throwing(new IllegalArgumentException("Cannot determine how to locate element " + field));
}
return log.exit(ans);
}
protected By buildByFromDefault() {
log.entry();
return log.exit(new ByIdOrName(field.getName()));
}
protected By buildByFromFindBys(final FindBys findBys) {
log.entry(findBys);
assertValidFindBys(findBys);
FindBy[] findByArray = findBys.value();
By[] byArray = new By[findByArray.length];
for (int i = 0; i < findByArray.length; i++) {
byArray[i] = buildByFromFindBy(findByArray[i]);
}
return log.exit(new ByChained(byArray));
}
protected By buildBysFromFindByOneOf(final FindAll findBys) {
log.entry(findBys);
assertValidFindAll(findBys);
FindBy[] findByArray = findBys.value();
By[] byArray = new By[findByArray.length];
for (int i = 0; i < findByArray.length; i++) {
byArray[i] = buildByFromFindBy(findByArray[i]);
}
return log.exit(new ByAll(byArray));
}
protected By buildByFromFindBy(final FindBy findBy) {
log.entry(findBy);
assertValidFindBy(findBy);
By ans = buildByFromShortFindBy(findBy);
if (ans == null) {
ans = buildByFromLongFindBy(findBy);
}
return log.exit(ans);
}
//The only thing that is different from the default Selenium implementation is that the locator string is processed for substitutions by the processForSubstitutions(using) method, which I have added
protected By buildByFromLongFindBy(final FindBy findBy) {
log.entry(findBy);
How how = findBy.how();
String using = findBy.using();
switch (how) {
case CLASS_NAME:
log.debug("Long FindBy annotation specified lookup by class name, using {}", using);
String className = processForSubstitutions(using);
return log.exit(By.className(className));
case CSS:
log.debug("Long FindBy annotation specified lookup by css name, using {}", using);
String css = processForSubstitutions(using);
return log.exit(By.cssSelector(css));
case ID:
log.debug("Long FindBy annotation specified lookup by id, using {}", using);
String id = processForSubstitutions(using);
return log.exit(By.id(id));
case ID_OR_NAME:
log.debug("Long FindBy annotation specified lookup by id or name, using {}", using);
String idOrName = processForSubstitutions(using);
return log.exit(new ByIdOrName(idOrName));
case LINK_TEXT:
log.debug("Long FindBy annotation specified lookup by link text, using {}", using);
String linkText = processForSubstitutions(using);
return log.exit(By.linkText(linkText));
case NAME:
log.debug("Long FindBy annotation specified lookup by name, using {}", using);
String name = processForSubstitutions(using);
return log.exit(By.name(name));
case PARTIAL_LINK_TEXT:
log.debug("Long FindBy annotation specified lookup by partial link text, using {}", using);
String partialLinkText = processForSubstitutions(using);
return log.exit(By.partialLinkText(partialLinkText));
case TAG_NAME:
log.debug("Long FindBy annotation specified lookup by tag name, using {}", using);
String tagName = processForSubstitutions(using);
return log.exit(By.tagName(tagName));
case XPATH:
log.debug("Long FindBy annotation specified lookup by xpath, using {}", using);
String xpath = processForSubstitutions(using);
return log.exit(By.xpath(xpath));
default:
// Note that this shouldn't happen (eg, the above matches all
// possible values for the How enum)
throw log.throwing(new IllegalArgumentException("Cannot determine how to locate element " + field));
}
}
//The only thing that differs from the default Selenium implementation is that the locator string is processed for substitutions by processForSubstitutions(using), which I wrote
protected By buildByFromShortFindBy(final FindBy findBy) {
log.entry(findBy);
log.debug("Building from a short FindBy annotation");
if (!"".equals(findBy.className())) {
log.debug("Short FindBy annotation specifies lookup by class name: {}", findBy.className());
String className = processForSubstitutions(findBy.className());
return log.exit(By.className(className));
}
if (!"".equals(findBy.css())) {
log.debug("Short FindBy annotation specifies lookup by css");
String css = processForSubstitutions(findBy.css());
return log.exit(By.cssSelector(css));
}
if (!"".equals(findBy.id())) {
log.debug("Short FindBy annotation specified lookup by id");
String id = processForSubstitutions(findBy.id());
return log.exit(By.id(id));
}
if (!"".equals(findBy.linkText())) {
log.debug("Short FindBy annotation specified lookup by link text");
String linkText = processForSubstitutions(findBy.linkText());
return log.exit(By.linkText(linkText));
}
if (!"".equals(findBy.name())) {
log.debug("Short FindBy annotation specified lookup by name");
String name = processForSubstitutions(findBy.name());
return log.exit(By.name(name));
}
if (!"".equals(findBy.partialLinkText())) {
log.debug("Short FindBy annotation specified lookup by partial link text");
String partialLinkText = processForSubstitutions(findBy.partialLinkText());
return log.exit(By.partialLinkText(partialLinkText));
}
if (!"".equals(findBy.tagName())) {
log.debug("Short FindBy annotation specified lookup by tag name");
String tagName = processForSubstitutions(findBy.tagName());
return log.exit(By.tagName(tagName));
}
if (!"".equals(findBy.xpath())) {
log.debug("Short FindBy annotation specified lookup by xpath");
String xpath = processForSubstitutions(findBy.xpath());
return log.exit(By.xpath(xpath));
}
// Fall through
log.debug("Locator does not match any expected locator type");
return log.exit(null);
}
//This method is where I find and do replacements. The method looks
//for instances of ${key} and if there is a key in the substitutions
//map that is equal to 'key', the substring ${key} is replaced by the
//value mapped to 'key'
private String processForSubstitutions(final String locator) {
log.entry(locator);
log.debug("Processing locator '{}' for substitutions");
List<String> subs = Arrays.asList(StringUtils.substringsBetween(locator, "${", "}"));
log.debug("List of substrings in locator which match substitution pattern: {}", subs);
String processed = locator;
for(String sub : subs) {
log.debug("Processing substring {}", sub);
//If there is no matching key, the substring "${ ..}" is treated as a literal
if(substitutions.get(sub) != null) {
log.debug("Replacing with {}", substitutions.get(sub));
processed = StringUtils.replace(locator, "${" + sub + "}",substitutions.get(sub));
log.debug("Locator after substitution: {}", processed);
}
}
return log.exit(processed);
}
private void assertValidAnnotations() {
log.entry();
FindBys findBys = field.getAnnotation(FindBys.class);
FindAll findAll = field.getAnnotation(FindAll.class);
FindBy findBy = field.getAnnotation(FindBy.class);
if (findBys != null && findBy != null) {
throw log.throwing(new IllegalArgumentException("If you use a '@FindBys' annotation, " +
"you must not also use a '@FindBy' annotation"));
}
if (findAll != null && findBy != null) {
throw log.throwing(new IllegalArgumentException("If you use a '@FindAll' annotation, " +
"you must not also use a '@FindBy' annotation"));
}
if (findAll != null && findBys != null) {
throw log.throwing(new IllegalArgumentException("If you use a '@FindAll' annotation, " +
"you must not also use a '@FindBys' annotation"));
}
}
private void assertValidFindBys(final FindBys findBys) {
log.entry(findBys);
for (FindBy findBy : findBys.value()) {
assertValidFindBy(findBy);
}
log.exit();
}
private void assertValidFindAll(final FindAll findBys) {
log.entry(findBys);
for (FindBy findBy : findBys.value()) {
assertValidFindBy(findBy);
}
log.exit();
}
private void assertValidFindBy(final FindBy findBy) {
log.entry();
if (findBy.how() != null) {
if (findBy.using() == null) {
throw log.throwing(new IllegalArgumentException(
"If you set the 'how' property, you must also set 'using'"));
}
}
Set<String> finders = new HashSet<>();
if (!"".equals(findBy.using())) {
log.debug("Locator string is: {}", findBy.using());
finders.add("how: " + findBy.using());
}
if (!"".equals(findBy.className())) {
log.debug("Class name locator string is {}", findBy.className());
finders.add("class name:" + findBy.className());
}
if (!"".equals(findBy.css())) {
log.debug("Css locator string is {}", findBy.css());
finders.add("css:" + findBy.css());
}
if (!"".equals(findBy.id())) {
log.debug("Id locator string is {}", findBy.id());
finders.add("id: " + findBy.id());
}
if (!"".equals(findBy.linkText())) {
log.debug("Link text locator string is {}", findBy.linkText());
finders.add("link text: " + findBy.linkText());
}
if (!"".equals(findBy.name())) {
log.debug("Name locator string is {}", findBy.name());
finders.add("name: " + findBy.name());
}
if (!"".equals(findBy.partialLinkText())) {
log.debug("Partial text locator string is {}", findBy.partialLinkText());
finders.add("partial link text: " + findBy.partialLinkText());
}
if (!"".equals(findBy.tagName())) {
log.debug("Tag name locator string is {}", findBy.tagName());
finders.add("tag name: " + findBy.tagName());
}
if (!"".equals(findBy.xpath())) {
log.debug("Xpath locator string is {}", findBy.xpath());
finders.add("xpath: " + findBy.xpath());
}
// A zero count is okay: it means to look by name or id.
if (finders.size() > 1) {
throw log.throwing(new IllegalArgumentException(
String.format("You must specify at most one location strategy. Number found: %d (%s)",
finders.size(), finders.toString())));
}
}
}
使用示例:
public class ExampleClass extends SlowLoadableComponent<ExampleClass> {
private final Map<String, String> substitutions;
@FindBy(how = How.ID, using = "somelocator_with_a dynamic_${id}")
private WebElement someElement;
public ExampleClass(final WebDriver driver, final int
loadTimeoutInSeconds, final String idValue) {
substitutions = new HashMap<>(); substitutions.put("id", idValue);
}
//When you call PageFactory.initElements, you need to tell it to use the DynamicElementLocatorFactory
protected void load() {
PageFactory.initElements(new DynamicElementLocatorFactory(getDriver(), substitutions), this);
}
}
2019 年 5 月 1 日更新:我必须对我在答案开头引用的博客文章使用网络存档链接,因为该博客文章无法通过其原始链接访问。
关于selenium - 动态在 FindsBy 中与 selenium 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26366094/
我想要的是能够在输入获得焦点或失去焦点时执行某些操作(两个事件)。 我尝试了以下方法,但这按事件单独工作(单独编码时):仅在焦点上,或仅在失去焦点时。 另外,我希望它尽可能跨平台(包括触摸设备),这是
我分别研究了TableView的Filtering和Pagination。 过滤: this帖子帮助我满足了我的需要 分页: this , this帖子也帮助了我 我想像这样将它们组合在一起: 详情-
我是 TDD 方法的新手,所以我想知道是否有人经历过这种机智可以启发我一点。我想获得一些关于如何一起使用 UML 和 TDD 方法的线索。 我已经习惯了:用 UML 设计 --> 生成骨架类(然后保持
我尝试使用入口点和 cmd 设置 Docker。 FROM debian:stretch RUN apt-get update && \ apt install gnupg ca-certificat
我想要一个 Class 对象,但我想强制它所代表的任何类扩展类 A 并实现接口(interface) B。 我能做到: Class 或者: Class 但我不能两者兼得。有办法做到这一点吗? 最佳答案
我是 Rubymine 的长期用户。 Rubymine 非常适合基于 html 的 Rails 应用程序,但我现在正在做更多的 SPA 客户端工作(例如 javascript/react)。我发现我真
我注意到我使用的某个脚本依赖于原型(prototype)。 (Lightbox 2) 它会与 jQuery 在同一页面上一起工作吗?有没有办法确保它们不冲突? 最佳答案 可以,但你需要采取 speci
我需要对表中显示的数据进行分页并通过 ajax 调用获取它 - 这是我通过使用具有以下配置的 dataTables 插件来完成的 - bServerSide : true; sAjaxSource :
我是 gtk 新手,所以想知道在 C 语言中归档和 gtk 是否可以一起使用?例如,我可以从 .txt 文件中读取,然后在相同的代码中使用 gtk 在标签或其他内容中显示它吗?如果是,怎么办? 谢谢!
有没有人设法得到Bck2Brwsr最近与 Java 8/JavaFX 8 一起工作?有没有兼容的机会?我找不到太多关于它的信息,也没有一个好的起点。使用给定的 Maven archetype我遇到了几
在我的应用程序中,用户通过 openid(与 stackoverflow 相同)登录/注销。 我想通过 oauth 向第三方应用程序开放我的应用程序。 如何创建我的 openid-consumer 应
我在启动和运行 Hibernate 和 Spring 时遇到一些问题。我有一个网络服务器项目,它使用了其他几个具有持久实体的项目。我遇到的问题是,对于存储在 WEB-INF/libs 内的另一个 ja
我有 @ControllerAdvice 类,它处理一组异常。我们还有一些其他异常,这些异常用 @ResponseStatus 注释进行注释。为了结合这两种方法,我们使用博客文章中描述的技术:http
我想在屏幕上使用进度条而不是 progressDialog。 我在我的 XML View 文件中插入了一个进度条,我想让它在加载时显示并在不加载时禁用它。 所以我使用的是可见的,但它发生了,所以其余的
CREATE TABLE `users` ( `id` int(11) AUTO_INCREMENT, `academicdegree` varchar(255),
IN() 中使用的查询返回:1, 2。然而,整个查询返回 0 行,这是不可能的,因为它们存在。我在这里做错了什么? SELECT DISTINCT li.auto_id FROM links
亲们, 我如何在使用 Jade 生成的表单上实现 jQuery 样式?我想做的是美化 表单并使它们可点击。我在 UI 方面很糟糕。期间。 我如何在表单上实现这个可选择的方法? http://jquer
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我可以: auto o1 = new Content; 但不能: std::shared_ptr o1(new Content); std::unique_ptr o1(new Content); 我
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 4 年前。 Improve this qu
我是一名优秀的程序员,十分优秀!