gpt4 book ai didi

selenium - 动态在 FindsBy 中与 selenium 一起使用

转载 作者:行者123 更新时间:2023-12-02 00:42:12 26 4
gpt4 key购买 nike

我有这个规范

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 DefaultElementLocatorDefaultElementLocatorFactory 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com