2012-10-22 14 views
15

w mojej aplikacji podczas otwierania strona XI spodziewać się zarówno elementem A lub elementu B. Są one umieszczone w różnych miejscach w DOM i można znaleźć za pomocą ich identyfikatorów, na przykład driver.findElement(By.id("idA"))znaleźć żadnego z dwóch elementów w webdriver

Jak mogę poprosić webdrivera o znalezienie A lub B?

Istnieje metoda driver.findElements(By) który zatrzyma czeka, gdy co najmniej jeden element zostanie znaleziony, ale metoda ta zmusza mnie do korzystania z tego samego lokalizatora dla A i B.

Jaki jest właściwy sposób wiarygodny znaleźć albo czy B, więc nie muszę czekać na określony czas?

+0

że odtwarzane nieco wokół z 'driver.findElements (By.cssSelector (" id = [a] [id = b ] "))' ale to nie jest * lub *. Nie znalazłem selektora css * lub *. – VolkerK

Odpowiedz

17

Element z I1 ID lub elementu o id I2

XPath: //E1[@id=I1] | //E2[@id=I2]

css: css=E1#I1,E2#I2

driver.findElement(By.xpath(//E1[@id=I1] | //E2[@id=I2])) 
driver.findElement(By.cssSelector(E1#I1,E2#I2)) 

nie zapomnieć o fluentWait mechanizmu:

public WebElement fluentWait(final By locator){ 

     Wait<WebDriver> wait = new FluentWait<WebDriver>(driver) 
       .withTimeout(30, TimeUnit.SECONDS) 
       .pollingEvery(5, TimeUnit.SECONDS) 
       .ignoring(org.openqa.selenium.NoSuchElementException.class); 

     WebElement foo = wait.until(
       new Function<WebDriver, WebElement>() { 
        public WebElement apply(WebDriver driver) { 
         return driver.findElement(locator); 
        } 
       } 
     ); 
     return foo; 
}; 

ty można uzyskać więcej informacji na temat fluentWait here

IMHO rozwiązaniem problemu będzie jak:

fluentWait(By.xpath(//E1[@id=I1] | //E2[@id=I2])); 
fluentWait(By.cssSelector(E1#I1,E2#I2)) 

FYI: here jest ładny XPath, instrukcja cssSelector

nadzieja to pomaga.

+0

podejście z użyciem xpath OR operand działało jak czar! Najpierw znajduję którykolwiek z tych elementów, a następnie po prostu zaznacz 'element.getAttribute (" id ")', aby znaleźć który faktycznie znalazłem. Dziękuję Ci! Nawiasem mówiąc, czy dobrze rozumiem, że FluentWait pozwala zastąpić domyślny niejawny czasu ustawionego przez 'driver.manage() limity czasu() implicitlyWait (20, TimeUnit.SECONDS);..'? –

+0

Jakiej wersji oprogramowania używasz? Z 2.22 'driver.findElement (By.cssSelector (E1 # I1, E2 # I2))' prowadzi do wyjątku 'org.openqa.selenium.WebDriverException: Selektory złożone niedozwolone' – VolkerK

+0

dodał jeszcze jedną odpowiedź na temat fluentWait –

0

@pavel_kazlou, dobrze dotycząca pytanie na FluentWait: Zasadniczo istnieją dwa rodzaje wait: Explicit wait

WebDriverWait.until(condition-that-finds-the-element) 

niejawny czekać

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); 

Różnica jest

  • Oczywiste - Implicit wait time jest stosowany do wszystkich elementów w skrypcie , ale Jawny tylko do określonego elementu
  • W języku Explicit możesz skonfigurować, jak często (zamiast 500 millisecond) chcesz sprawdzić warunek.
  • W trybie jawnym można także skonfigurować tak, aby ignorował inne wyjątki niż "NoSuchElement" przed upływem limitu czasu. Użyj FluentWait, który działa podobnie do WebDriverWait (co w rzeczywistości rozszerza FluentWait), ale zapewnia nieco większą elastyczność.

Oto przykład użycia WebDriverWait (Użyj innego konstruktora WebDriverWait, aby określić interwał sondowania elementu (mierzony w milisekundach).):

new WebDriverWait(webDriver(), 10, 50).until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath))); 

Korzystanie z FluentWait który działa podobnie do WebDriverWait (która w rzeczywistości rozciąga FluentWait), ale daje trochę więcej elastyczności: w szczególności możliwość wyboru wyjątek WebDriver zignorować. Przykładem Wykorzystanie:

new FluentWait(webDriver()) 
.withTimeout(timeout, TimeUnit.SECONDS) 
.pollingEvery(50, TimeUnit.MILLISECONDS) 
.ignoring(NoSuchElementException.class) 
.until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath))); 

Kończąc moją uwagę: fluentWait jest Explicit typu oczekiwania i zapewnia możliwość wyboru wyraźnie jaki typ wyjątku WebDriver ignorować, gdzie jak każdy niejawny wait obejmuje stałą ilość czasu, aby czekać dla dowolnego webElement. Z tego punktu widzenia podejście IMHO fluentWait jest bardziej niezawodne.

2

Oto moje rozwiązanie, które wykorzystuje płynne oczekiwanie, jak sugerowali inni. Konieczne będzie zastąpienie wszystkich wywołań funkcji getDriver() lub odniesień do sterownika za pomocą obiektu sterownika lub własnej metody, która go pobiera.

/** 
* Waits for any one of a given set of WebElements to become displayed and 
* enabled. 
* 
* @param locators 
*   An array of locators to be sought. 
* @param timeout 
*   Timeout in seconds. 
*/ 
protected void waitForOneOfManyToBePresent(By[] locators, int timeout) { 
    try { 
     (new WebDriverWait(getDriver(), timeout)) 
      .until(somethingIsPresent(locators)); 
    } catch (TimeoutException timeoutEx) { 
     // Do what you wish here to handle the TimeoutException, or remove 
     // the try/catch and let the TimeoutException fly. I prefer to 
     // rethrow a more descriptive Exception 
    } 
} 

/** 
* Condition for presence of at least one of many elements. 
* 
* @param locators 
*   An array of By locators to be sought. 
* @return Boolean T if at least one element is present, F otherwise. 
*/ 
protected ExpectedCondition<Boolean> somethingIsPresent(By[] locators) { 
    final By[] finalLocators = locators; 
    return new ExpectedCondition<Boolean>() { 
     public Boolean apply(WebDriver driver) { 
      boolean found = false; 
      for (By locator : finalLocators) { 
       if (isElementPresent(locator)) { 
        found = true; 
        break; 
       } 
      } 
      return new Boolean(found); 
     } 
    }; 
} 

/** 
* Similar to does element exist, but also verifies that only one such 
* element exists and that it is displayed and enabled. 
* 
* @param by 
*   By statement locating the element. 
* @return T if one and only one element matching the locator is found, and 
*   if it is displayed and enabled, F otherwise. 
*/ 
protected boolean isElementPresent(By by) { 
    // Temporarily set the implicit timeout to zero 
    driver.manage().timeouts().implicitlyWait(0, TimeUnit.MILLISECONDS); 
    // Check to see if there are any elements in the found list 
    List<WebElement> elements = driver.findElements(by); 
    boolean isPresent = (elements.size() == 1) 
      && elements.get(0).isDisplayed() && elements.get(0).isEnabled(); 
    // Return to the original implicit timeout value 
    driver.manage().timeouts() 
       .implicitlyWait(Properties.TIMEOUT_TEST, TimeUnit.SECONDS); 
    // Properties.TIMEOUT_TEST is from other personal code, replace with your 
    // own default timeout setting. 
    return isPresent; 
} 

Moja wersja sprawdza również, aby upewnić się, że wszelkie znalezione elementem jest liczba pojedyncza, widoczne i włączona, ale można łatwo usunąć, że jeśli chcesz tylko sprawdzić dla istnienia lub jeśli nie obchodzi jeśli są lokalizatory znajdowanie wielu pasujących elementów. Sprawdzanie obecności elementu przez pomijanie domyślnego limitu czasu, a następnie wywoływanie metody findElements() może wydawać się nieostre, ale najwyraźniej jest to zalecany sposób w Selenium API.

2

Napisałem ExpectedCondition, aby móc z niego korzystać.

1

Natknąłem się na ten problem, więc zrobiłem dla niego metodę. Należy zwrócić uwagę, że metoda znajduje się w klasie zawierającej webdriver jako "self._driver". Kod jest w języku Python.

Przykładem wywołanie metody byłoby:

self.MES (3, ('name', 'name_of_element1'), ('id', 'id_of_element2'))

from selenium.common.exceptions import NoSuchElementException 
import time 

def MES(self, wait_time, element1, element2): 
    ''' 
    A function to check a website for multiple elements at the same time 
    MultiElementSearch. Returns the element if found, or False if neither 
    are found. 
    It will also throw a ValueError is the element locator type is not 
    valid. 

    MES(int, (str, str), (str, str)) -> Element or bool 
    ''' 
    time1 = time.time() 
    while time.time() < (time1 + wait_time): 
     try: 
      if element1[0] == 'id': 
       selection1 = self._driver.find_element_by_id(element1[1]) 
      elif element1[0] == 'name': 
       selection1 = self._driver.find_element_by_name(element1[1]) 
      elif element1[0] == 'xpath': 
       selection1 = self._driver.find_element_by_xpath(element1[1]) 
      elif element1[0] == 'link_text': 
       selection1 = self._driver.find_element_by_link_text(element1[1]) 
      elif element1[0] == 'partial_link_text': 
       selection1 = self._driver.find_element_by_partial_link_text(
        element1[1]) 
      elif element1[0] == 'tag_name': 
       selection1 = self._driver.find_element_by_tag_name(element1[1]) 
      elif element1[0] == 'class_name': 
       selection1 = self._driver.find_element_by_class_name(
        element1[1]) 
      elif element1[0] == 'css_selector': 
       selection1 = self._driver.find_element_by_css_selector(
        element1[1]) 
      else: 
       raise ValueError(
        'The first element locator type is not vaild') 
      return selection1 

     except NoSuchElementException: 
      pass 

     try: 
      if element2[0] == 'id': 
       selection2 = self._driver.find_element_by_id(element2[1]) 
      elif element2[0] == 'name': 
       selection2 = self._driver.find_element_by_name(element2[1]) 
      elif element2[0] == 'xpath': 
       selection2 = self._driver.find_element_by_xpath(element2[1]) 
      elif element2[0] == 'link_text': 
       selection2 = self._driver.find_element_by_link_text(element2[1]) 
      elif element2[0] == 'partial_link_text': 
       selection2 = self._driver.find_element_by_partial_link_text(
        element2[1]) 
      elif element2[0] == 'tag_name': 
       selection2 = self._driver.find_element_by_tag_name(element2[1]) 
      elif element2[0] == 'class_name': 
       selection2 = self._driver.find_element_by_class_name(
        element2[1]) 
      elif element2[0] == 'css_selector': 
       selection2 = self._driver.find_element_by_css_selector(
        element2[1]) 
      else: 
       raise ValueError(
        'The second element locator type is not vaild') 
      return selection2 
     except NoSuchElementException: 
      pass 
    return False 
0

Oto rozwiązanie Java 8.

Przedmiotem owijka:

import org.openqa.selenium.By; 
import org.openqa.selenium.WebDriver; 
import org.openqa.selenium.WebElement; 
import org.openqa.selenium.support.ui.ExpectedCondition; 
import org.openqa.selenium.support.ui.Wait; 
import org.openqa.selenium.support.ui.WebDriverWait; 

public class SelectorWebElement 
{ 
    private WebElement webElement; 
    private By by; 

    private SelectorWebElement(WebElement webElement, By by) 
    { 
     this.webElement = webElement; 
     this.by = by; 
    } 

    public By getBy() 
    { 
     return by; 
    } 

    public WebElement getWebElement() 
    { 
     return webElement; 
    } 

    private static ExpectedCondition<SelectorWebElement> findFirstElement(By... selectors) 
    { 
     return driver -> 
     { 
      for (By selector : selectors) 
      { 
       try 
       { 
        assert driver != null; 
        WebElement webElement = driver.findElement(selector); 
        if (webElement.isDisplayed()) 
        { 
         return new SelectorWebElement(webElement, selector); 
        } 
       } catch (Exception ignored) 
       { 

       } 
      } 

      return null; 
     }; 
    } 

    public static SelectorWebElement waitForFirstElement(WebDriver driver, 
                 long timeout, 
                 By... selectors) 
    { 
     Wait wait = new WebDriverWait(driver, timeout); 
     return (SelectorWebElement) wait.until(findFirstElement(selectors)); 
    } 
} 

Kod przykład:

By badPasswordSelector = By.cssSelector("..."); 
By myAccountPage = By.cssSelector("..."); 
SelectorWebElement selectorWebElement = SelectorWebElement.waitForFirstElement(driver, 5, badPasswordSelector, myAccountPage); 

By matchedSelector = selectorWebElement.getBy(); 

if (matchedSelector.equals(badPasswordSelector)) 
{ 
    System.out.println("Bad password"); 
} else if (matchedSelector.equals(myAccountPage)) 
{ 
    System.out.println("Successfully logged in"); 
} 
Powiązane problemy