Come si fa in modo che Selenium 2.0 attenda il caricamento della pagina?
Come si fa in modo che Selenium 2.0 attenda il caricamento della pagina?
Risposte:
Puoi anche controllare il pageload usando il seguente codice
IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
Utilizzare la classe WebDriverWait
Vedi anche qui
Puoi aspettarti di mostrare qualche elemento. qualcosa come in C #:
WebDriver _driver = new WebDriver();
WebDriverWait _wait = new WebDriverWait(_driver, new TimeSpan(0, 1, 0));
_wait.Until(d => d.FindElement(By.Id("Id_Your_UIElement"));
Se si imposta l'attesa implicita del driver, quindi si chiama il findElement
metodo su un elemento che si prevede si trovi nella pagina caricata, WebDriver eseguirà il polling per quell'elemento fino a quando non trova l'elemento o raggiunge il valore di timeout.
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
fonte: attese implicite
In generale, con Selenium 2.0 il driver Web dovrebbe restituire il controllo al codice chiamante solo dopo aver determinato che la pagina è stata caricata. In caso contrario, è possibile chiamare waitforelemement
, che cicla intorno alla chiamata findelement
fino a quando non viene trovata o scade (è possibile impostare il timeout).
If click() causes a new page to be loaded via an event or is done by sending a native event (which is a common case on Firefox, IE on Windows) then the method will not wait for it to be loaded and the caller should verify that a new page has been loaded.
JavascriptExecutor
, in attesa che document.readyState sia "completo". A causa del viaggio di andata e ritorno dal selenio al browser, credo che le condizioni di gara siano mitigate e questo "sempre" funziona per me. Dopo "click ()" quando mi aspetto che una pagina venga caricata, aspetto esplicitamente (utilizzando WebDriverWait) il readystate. ]
Implementazione di Ruby:
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
@driver.execute_script("return document.readyState;") == "complete"
}
È possibile rimuovere la System.out
linea. È stato aggiunto a scopo di debug.
WebDriver driver_;
public void waitForPageLoad() {
Wait<WebDriver> wait = new WebDriverWait(driver_, 30);
wait.until(new Function<WebDriver, Boolean>() {
public Boolean apply(WebDriver driver) {
System.out.println("Current Window State : "
+ String.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState")));
return String
.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState"))
.equals("complete");
}
});
}
Tutte queste soluzioni vanno bene per casi specifici, ma presentano almeno uno dei due possibili problemi:
Non sono abbastanza generici: vogliono che tu sappia, in anticipo, che alcune condizioni specifiche saranno vere per la pagina che stai andando (ad es. Alcuni elementi verranno visualizzati)
Sono aperti a una condizione di competizione in cui si utilizza un elemento che è effettivamente presente nella vecchia pagina e nella nuova pagina.
Ecco il mio tentativo di una soluzione generica che evita questo problema (in Python):
Innanzitutto, una generica funzione "wait" (usa un WebDriverWait se vuoi, li trovo brutti):
def wait_for(condition_function):
start_time = time.time()
while time.time() < start_time + 3:
if condition_function():
return True
else:
time.sleep(0.1)
raise Exception('Timeout waiting for {}'.format(condition_function.__name__))
Successivamente, la soluzione si basa sul fatto che il selenio registra un numero ID (interno) per tutti gli elementi di una pagina, incluso l' <html>
elemento di livello superiore . Quando una pagina si aggiorna o carica, ottiene un nuovo elemento html con un nuovo ID.
Quindi, supponendo che si desideri fare clic su un collegamento con il testo "il mio collegamento", ad esempio:
old_page = browser.find_element_by_tag_name('html')
browser.find_element_by_link_text('my link').click()
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
Per ulteriori helper Pythonic, riutilizzabili e generici, puoi creare un gestore di contesto:
from contextlib import contextmanager
@contextmanager
def wait_for_page_load(browser):
old_page = browser.find_element_by_tag_name('html')
yield
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
E poi puoi usarlo praticamente su qualsiasi interazione di selenio:
with wait_for_page_load(browser):
browser.find_element_by_link_text('my link').click()
Credo sia a prova di proiettile! Cosa ne pensi?
Maggiori informazioni in un post sul blog qui
Puoi anche usare la classe: ExpectedConditions
attendere esplicitamente che un elemento venga visualizzato sulla pagina web prima di poter intraprendere qualsiasi azione ulteriori azioni
È possibile utilizzare la ExpectedConditions
classe per determinare se un elemento è visibile:
WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));
Consulta l' ExpectedConditions class Javadoc
elenco di tutte le condizioni che puoi verificare.
La risposta di Imran è stata riformulata per Java 7:
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState"
).equals("complete");
}
});
Questa sembra essere una grave limitazione di WebDriver. Ovviamente l'attesa di un elemento non implica che la pagina venga caricata, in particolare il DOM può essere completamente compilato (già stato) per cui JS è ancora in esecuzione e CSS e immagini stanno ancora caricando.
Credo che la soluzione più semplice sia impostare una variabile JS sull'evento onload dopo che tutto è stato inizializzato e controllare e attendere questa variabile JS in Selenium.
Se si desidera attendere il caricamento di un elemento specifico, è possibile utilizzare il isDisplayed()
metodo su un RenderedWebElement
:
// Sleep until the div we want is visible or 5 seconds is over
long end = System.currentTimeMillis() + 5000;
while (System.currentTimeMillis() < end) {
// Browsers which render content (such as Firefox and IE) return "RenderedWebElements"
RenderedWebElement resultsDiv = (RenderedWebElement) driver.findElement(By.className("gac_m"));
// If results have been returned, the results are displayed in a drop down.
if (resultsDiv.isDisplayed()) {
break;
}
}
(Esempio dalla Guida introduttiva a 5 minuti )
RenderedWebElement
nell'API. Tuttavia, il isDisplayed()
metodo è ora disponibile direttamente su WebElement
.
Attendere esplicitamente o attendere condizionatamente in questa attesa fino a quando non viene data questa condizione.
WebDriverWait wait = new WebDriverWait(wb, 60);
wait.until(ExpectedConditions.elementToBeClickable(By.name("value")));
Questo attenderà ogni elemento web per 60 secondi.
Usa implicitamente l'attesa di ogni elemento della pagina fino a quel momento.
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
Questo attenderà ogni elemento web per 60 secondi.
Usa implicitamente l'attesa di ogni elemento della pagina fino a quel momento.
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
questo aspetta ogni elemento nella pagina per 30 sec.
Un'altra attesa è Attesa esplicita o attesa condizionale in questa attesa fino alla condizione specificata.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
In id indica l'id dell'elemento statico che viene visualizzato in modo diffuso sulla pagina, non appena la pagina viene caricata.
Sono sorpreso che i predicati non siano stati la prima scelta poiché in genere sai con quali elementi interagirai successivamente nella pagina che stai aspettando di caricare. Il mio approccio è sempre stato quello di costruire predicati / funzioni come waitForElementByID(String id)
e waitForElemetVisibleByClass(String className)
, ecc. E quindi di usarli e riutilizzarli ovunque ne avessi bisogno, sia per il caricamento della pagina che per il cambio del contenuto della pagina che sto aspettando.
Per esempio,
Nella mia classe di test:
driverWait.until(textIsPresent("expectedText");
Nel mio genitore della classe di test:
protected Predicate<WebDriver> textIsPresent(String text){
final String t = text;
return new Predicate<WebDriver>(){
public boolean apply(WebDriver driver){
return isTextPresent(t);
}
};
}
protected boolean isTextPresent(String text){
return driver.getPageSource().contains(text);
}
Anche se questo sembra molto, si occupa di controllare ripetutamente per te e l'intervallo per la frequenza di controllo può essere impostato insieme al tempo di attesa finale prima del timeout. Inoltre, riutilizzerai tali metodi.
In questo esempio, la classe genitore ha definito e avviato il WebDriver driver
e il WebDriverWait driverWait
.
Spero che aiuti.
Il modo migliore per attendere il caricamento della pagina quando si utilizzano i collegamenti Java per WebDriver consiste nell'utilizzare il modello di progettazione Oggetto pagina con PageFactory. Ciò ti consente di utilizzare ciò AjaxElementLocatorFactory
che per dirla agisce semplicemente come un'attesa globale per tutti i tuoi elementi. Presenta limitazioni su elementi come caselle di selezione o transizioni javascript complesse, ma ridurrà drasticamente la quantità di codice necessario e accelererà i tempi di test. Un buon esempio può essere trovato in questo post sul blog. Si presuppone una conoscenza di base di Core Java.
http://startingwithseleniumwebdriver.blogspot.ro/2015/02/wait-in-page-factory.html
Soluzione NodeJS:
In Nodejs puoi ottenerlo tramite le promesse ...
Se scrivi questo codice, puoi essere sicuro che la pagina è completamente caricata quando arrivi a ...
driver.get('www.sidanmor.com').then(()=> {
// here the page is fully loaded!!!
// do your stuff...
}).catch(console.log.bind(console));
Se scrivi questo codice, navigherai e il selenio attenderà 3 secondi ...
driver.get('www.sidanmor.com');
driver.sleep(3000);
// you can't be sure that the page is fully loaded!!!
// do your stuff... hope it will be OK...
Dalla documentazione del selenio:
this.get (url) → Thenable
Pianifica un comando per passare all'URL specificato.
Restituisce una promessa che verrà risolta al termine del caricamento del documento .
Ecco una versione Java 8 della risposta attualmente più votata :
WebDriverWait wait = new WebDriverWait(myDriver, 15);
wait.until(webDriver -> ((JavascriptExecutor) myDriver).executeScript("return document.readyState").toString().equals("complete"));
Dov'è myDriver
un WebDriver
oggetto (dichiarato in precedenza).
Nota : tenere presente che questo metodo ( document.readyState
) controlla solo il DOM.
Chiama sotto la funzione nel tuo script, questo attenderà fino a quando la pagina non viene caricata utilizzando JavaScript
public static boolean isloadComplete(WebDriver driver)
{
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
/**
* Call this method before an event that will change the page.
*/
private void beforePageLoad() {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("document.mpPageReloaded='notYet';");
}
/**
* Call this method after an event that will change the page.
*
* @see #beforePageLoad
*
* Waits for the previous page to disappear.
*/
private void afterPageLoad() throws Exception {
(new WebDriverWait(driver, 10)).until(new Predicate<WebDriver>() {
@Override
public boolean apply(WebDriver driver) {
JavascriptExecutor js = (JavascriptExecutor) driver;
Object obj = js.executeScript("return document.mpPageReloaded;");
if (obj == null) {
return true;
}
String str = (String) obj;
if (!str.equals("notYet")) {
return true;
}
return false;
}
});
}
È possibile passare dal documento a un elemento, nel caso in cui venga modificata solo una parte di un documento.
Questa tecnica è stata ispirata dalla risposta di Sincebasic.
SeleniumWaiter :
import com.google.common.base.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
public class SeleniumWaiter {
private WebDriver driver;
public SeleniumWaiter(WebDriver driver) {
this.driver = driver;
}
public WebElement waitForMe(By locatorname, int timeout){
WebDriverWait wait = new WebDriverWait(driver, timeout);
return wait.until(SeleniumWaiter.presenceOfElementLocated(locatorname));
}
public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {
// TODO Auto-generated method stub
return new Function<WebDriver, WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
};
}
}
E a te lo usi :
_waiter = new SeleniumWaiter(_driver);
try {
_waiter.waitForMe(By.xpath("//..."), 10);
}
catch (Exception e) {
// Error
}
È possibile utilizzare il metodo esistente di seguito per impostare il tempo per pageeLoadTimeout nell'esempio seguente se il caricamento della pagina richiede più di 20 secondi, quindi genererà un'eccezione per il ricaricamento della pagina
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS)
Puoi attendere esplicitamente la visualizzazione di un elemento sulla pagina Web prima di poter intraprendere qualsiasi azione (come element.click ())
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(new ExpectedCondition<WebElement>(){
@Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("myDynamicElement"));
}});
Questo è quello che ho usato per uno scenario simile e funziona benissimo.
Il modo migliore che ho visto è utilizzare stalenessOf
ExpectedCondition, per aspettare che la vecchia pagina diventi obsoleta.
Esempio:
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement oldHtml = driver.findElement(By.tagName("html"));
wait.until(ExpectedConditions.stalenessOf(oldHtml));
Aspetterà dieci secondi affinché il vecchio tag HTML diventi obsoleto, quindi genererà un'eccezione se ciò non accade.
Uso node + selenium-webdriver (la cui versione è 3.5.0 ora). quello che faccio per questo è:
var webdriver = require('selenium-webdriver'),
driver = new webdriver.Builder().forBrowser('chrome').build();
;
driver.wait(driver.executeScript("return document.readyState").then(state => {
return state === 'complete';
}))
Puoi usare wait. ci sono fondamentalmente 2 tipi di attesa nel selenio
- Attesa implicita
Questo è molto semplice, vedi la sintassi seguente:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
- Attesa esplicita
Attendere esplicitamente o attendere condizionatamente in questa attesa fino a quando non si verifica una determinata condizione.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
È possibile utilizzare altri immobili come visblityOf()
,visblityOfElement()
Se qualcuno usa selenide:
public static final Long SHORT_WAIT = 5000L; // 5 seconds
$("some_css_selector").waitUntil(Condition.appear, SHORT_WAIT);
Altre condizioni sono disponibili qui: http://selenide.org/javadoc/3.0/com/codeborne/selenide/Condition.html
Nel mio caso, ho usato quanto segue per conoscere lo stato di caricamento della pagina. Nella nostra applicazione sono presenti le gif di caricamento e le ascolto come segue per eliminare i tempi di attesa indesiderati nello script.
public static void processing(){
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[@id='Msgpanel']/div/div/img")));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[@id='Msgpanel']/div/div/img")));
}
Dove xpath individua la gif nel DOM HTML. Successivamente, è anche possibile implementare i metodi di azione Fare clic su.
public static void click(WebElement elementToBeClicked){
WebDriverWait wait = new WebDriverWait(driver, 45);
wait.until(ExpectedConditions.visibilityOf(element));
wait.until(ExpectedConditions.elementToBeClickable(element));
wait.ignoring(NoSuchElementException.class).ignoring(StaleElementReferenceException.class); elementToBeClicked.click();
}
Man tutte queste risposte richiedono troppo codice. Questa dovrebbe essere una cosa semplice in quanto piuttosto comune.
Perché non solo iniettare qualche semplice javascript con il webdriver e controllare. Questo è il metodo che uso nella mia classe webscraper. Il javascript è piuttosto semplice anche se non conosci js.
def js_get_page_state(self):
"""
Javascript for getting document.readyState
:return: Pages state.
More Info: https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
"""
ready_state = self.driver.execute_script('return document.readyState')
if ready_state == 'loading':
self.logger.info("Loading Page...")
elif ready_state == 'interactive':
self.logger.info("Page is interactive")
elif ready_state == 'complete':
self.logger.info("The page is fully loaded!")
return ready_state
Come fare in modo che Selenium attenda il caricamento della pagina dopo un clic fornisce il seguente approccio interessante:
WebElement
dalla vecchia pagina.WebElement
fino alStaleElementReferenceException
viene generata.Codice di esempio:
WebElement link = ...;
link.click();
new WebDriverWait(webDriver, timeout).until((org.openqa.selenium.WebDriver input) ->
{
try
{
link.isDisplayed();
return false;
}
catch (StaleElementReferenceException unused)
{
return true;
}
});
new WebDriverWait(driver, 10).until(ExpectedConditions.stalenessOf(element))
.