我有这个规格
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;
}
我需要像上面一样动态配置Using
属性FindsBy
:SEE#{DYNAMIC-ID}
据我所知,这并不存在。该FindBy
批注采用静态Strings
而已。您可能需要自定义修改FindBy
注释处理器,类似于此博客的行为:https
://web.archive.org/web/20180612042724/http:
//brimllc.com/2011/01/selenium-2-0-webdriver-扩展查找以支持动态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);
}
}
更新的5/1/2019:我必须在答案开头使用我引用的博客文章的Web存档链接,因为该博客文章的原始链接不可访问。
问题内容: 在将应用程序从Struts 1迁移到Struts 2时 在某些地方,基于请求参数,同一操作类已用于不同类型的视图。 例如:如果is为1意味着需要附加一个参数,或者如果is 2意味着需要附加更多额外的参数,例如,我需要使用将动态参数传递给其他操作。 struts-config.xml 动作班 但是,我无法在Struts 2中做同样的事情。在Struts 2中,动态参数可以使用吗? 问题答
问题内容: 我正在使用PageFactory在Selenium WebDriver for C#中构建页面对象模型。 不幸的是,我发现不会初始化(HTML 标签/下拉菜单)。到目前为止,我已经遇到或想出了一些解决方案,但没有一个是理想的: 并且是,因此我不能仅通过继承它们来强制使用。 在每种方法中从实例手动实例化a 都是很麻烦且重复的。除非我每次都添加一个等待,否则它也会忽略明显的内置等待并抛出s
问题内容: 由于Tor浏览器捆绑包只是Firefox的修补版本,因此似乎应该可以在Tor浏览器中使用。到目前为止,这是我尝试过的: 这将导致空白的Tor浏览器页面打开,并弹出消息: 无法加载Firefox配置文件。 它可能丢失或无法访问。 我知道配置文件是有效/兼容的,因为我可以使用以下命令成功启动浏览器和配置文件: 但是,我不知道如何向打开的浏览器发送命令。 我发现了类似的问题,但是我正在寻找J
我目前正在使用Selenium运行Chrome的实例来测试网页。每次我的脚本运行,一个干净的Chrome实例启动(干净的扩展,书签,浏览历史,等等)。我想知道是否有可能运行我的脚本与Chrome扩展。我试着搜索一个Python示例,但是当我用谷歌搜索这个示例时,什么也没有找到。
问题内容: 我正在尝试使用动态变量名称(我不确定它们的实际名称),但是非常像这样: 返回值告诉我它不起作用。我不知道我在寻找什么语法或技术,这使得很难研究。在前面定义。 问题答案: 将它们包装在: 工作实例 使用是创建动态变量的一种方法,简单的示例:
问题内容: 我正在尝试使用 selenium* 选择包裹在 angular 1中 的textarea ,但是在DOM中看不到。有一个名为Pytractor的模块。我一直在尝试解决此问题,但是我无法正确使用它。 * 谁能帮我这个? 问题答案: 您还可以使用常规的硒绑定来测试AngularJS应用程序。您将需要使用“ 显式等待”来等待元素的出现,消失,标题/ URL的更改等-用于使您能够继续测试页面的