2014-10-14 2 views
2

У меня есть эта спецификациядинамическое Использование в FindsBy с селеном

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 динамичны, как выше: СМ #{DYNAMIC-ID}

ответ

6

Насколько я знаю, это не существует из коробки. Аннотация FindBy принимает статические Strings. Вы, вероятно, нужно изменить пользовательский процессор FindBy аннотаций подобно тому, как это сделал блоггер: http://brimllc.com/2011/01/selenium-2-0-webdriver-extending-findby-annotation-to-support-dynamic-idxpath/

Другой дискуссионную тему здесь: https://groups.google.com/forum/#!topic/webdriver/awxOw0FoiYU где Саймон Стюарт показывает пример того, как это может быть достигнуто.

UPDATE:

Я на самом деле реализовать это, потому что мне нужно было это достаточно, чтобы попробовать. Я не создавал аннотацию пользовательского поиска (что мне, возможно, придется делать в будущем).

Я написал реализации для ElementLocator и ElementLocatorFactory, которые позволяют заменять строки для локаторов, указанных с использованием существующих аннотаций. Если вы знаете или можете определить во время выполнения значения для замены, это будет работать для вас.

По умолчанию PageFactory использует classesDefaultElementLocator и DefaultElementLocatorFactory реализаций ElementLocator и ElementLocatorFactoryinterfaces для настройки обработки аннотаций, но реальная логика в 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 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); 
    } 
} 
Смежные вопросы