2012-10-22 13 views
15

nella mia richiesta, quando apro la pagina XI si aspettano di vedere sia elemento A o elemento B. Essi sono collocati in luoghi diversi nel DOM e possono essere trovati utilizzando i rispettivi ID, ad esempio driver.findElement(By.id("idA"))trovare qualsiasi di due elementi in WebDriver

Come posso chiedere al webdriver di trovare A o B?

C'è del metodo driver.findElements(By) che si fermerà in attesa quando viene trovato almeno un elemento, ma questo metodo mi costringe a utilizzare lo stesso localizzatore per A e B.

Qual è il modo corretto per trovare in modo affidabile A o B, in modo da non dover aspettare il timeout implicito?

+0

ho giocato un po 'in giro con 'driver.findElements (By.cssSelector (" [id = a] [id = b ] "))' ma non è un * o *. Non ho trovato un selettore css * o *. L'approccio – VolkerK

risposta

17

Elemento con I1 id o elemento con 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)) 

non dimenticare fluentWait meccanismo:

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; 
}; 

voi puoi avere maggiori informazioni su fluent Wait here

soluzione IMHO al tuo problema essere come:

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

FYI: here è bello XPath, manuale CssSelector

speranza che questo ti aiuta.

+0

usando xpath OPP o l'operando ha funzionato come un incantesimo! Prima trovo uno di questi elementi e poi controllo 'element.getAttribute (" id ")' per trovare quello che ho effettivamente trovato. Grazie! A proposito, capisco correttamente che FluentWait consente di sovrascrivere il timeout implicito predefinito impostato da 'driver.manage(). Timeouts(). ImplicitlyWait (20, TimeUnit.SECONDS);'? –

+0

Quale versione di webdriver stai usando? Con 2.22 'driver.findElement (By.cssSelector (E1 # I1, E2 # I2))' porta a un'eccezione 'org.openqa.selenium.WebDriverException: selettori composti non consentiti' – VolkerK

+0

aggiunto un'altra risposta su fluentWait –

0

@pavel_kazlou, così per quanto riguarda la tua domanda sul FluentWait: Fondamentalmente ci sono due tipi di attesa: esplicita attesa

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

attesa implicito

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

differenza è

  • Ovvio: il tempo di attesa implicito viene applicato a tutti gli elementi nello script ma Solo esplicito per l'elemento specifico
  • In Esplicito è possibile configurare, con quale frequenza (invece di 500 millisecondo) si desidera controllare la condizione.
  • In esplicita è anche possibile configurare per ignorare altre eccezioni rispetto "NoSuchElement" fino timeout .. Utilizzare un FluentWait che funziona in modo simile a WebDriverWait (che di fatto estende FluentWait), ma si dà un po 'più di flessibilità.

Ecco l'esempio di utilizzo di WebDriverWait (Utilizzare un costruttore WebDriverWait diverso per specificare l'intervallo di polling dell'elemento (misurato in millisecondi).):

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

Utilizzando un FluentWait che funziona in modo simile a WebDriverWait (che di fatto estende FluentWait), ma si dà un po 'più di flessibilità: in particolare, la possibilità di scegliere l'eccezione WebDriver di ignorare. Esempio utilizzo:

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

Per concludere la mia nota: fluentWait è un tipo esplicito di aspettare e si offre la possibilità di scegliere in modo esplicito il tipo di eccezione WebDriver di ignorare, dove come ogni attesa implicita comprende importo fisso di tempo di attesa per qualsiasi webElement. IMHO fluente L'approccio Wait è più solido da questo punto di vista.

2

Ecco la mia soluzione, che utilizza un'attesa fluente come altri hanno suggerito. Dovresti sostituire qualsiasi chiamata a getDriver() o riferimenti a un driver con un oggetto driver o il tuo metodo che lo recupera.

/** 
* 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; 
} 

La mia versione controlla anche fare in modo che ogni elemento trovato è singolare, visibile e attiva, ma è possibile rimuovere facilmente che se desideri solo per verificare l'esistenza o se non si cura se i vostri localizzatori sono trovare più elementi di corrispondenza. Controllare la presenza di un elemento sopprimendo il timeout predefinito e chiamando findElements() può sembrare un po 'goffo, ma è apparentemente il modo consigliato di farlo nell'API Selenium.

2

Ho scritto un ExpectedCondition per questo sentitevi liberi di usarlo.

public static ExpectedCondition<By> titleIs(final By[] selectors) { 
    return new ExpectedCondition<By>() { 
     public By apply(WebDriver driver) { 
      WebElement el=null; 
      for (By selector:selectors) { 
       try { 
        el = driver.findElement(selector); 
       } catch (NoSuchElementException ex) { 
        // ignore as we are waiting for that to stop 
       } 
       if (el!=null) return selector; 
      } 
      return null; 
     } 
    }; 
} 
1

Mi sono imbattuto in questo problema e quindi ho fatto un metodo per questo. Si prega di notare che il metodo si trova all'interno di una classe che contiene il webdriver come "auto._driver". Il codice è in Python.

Un esempio di chiamare il metodo potrebbe essere:

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

Ecco una soluzione Java 8.

L'oggetto wrapper:

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)); 
    } 
} 

Un codice di esempio:

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"); 
} 
Problemi correlati