Jak sprawić, by Selenium 2.0 czekał na załadowanie strony?
Jak sprawić, by Selenium 2.0 czekał na załadowanie strony?
Odpowiedzi:
Możesz także sprawdzić załadowany pagel za pomocą następującego kodu
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"));
Użyj klasy WebDriverWait
Zobacz także tutaj
Możesz spodziewać się pokazać jakiś element. coś jak w 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"));
Jeśli ustawisz ukryte oczekiwanie sterownika, a następnie wywołasz findElement
metodę na elemencie, który ma znajdować się na ładowanej stronie, WebDriver będzie sondował ten element, aż znajdzie element lub osiągnie wartość limitu czasu.
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
źródło: niejawne-czeka
Ogólnie rzecz biorąc, w Selenium 2.0 sterownik sieci powinien zwracać kontrolę nad kodem wywołującym dopiero po stwierdzeniu, że strona została załadowana. Jeśli tak się nie stanie, możesz zadzwonić waitforelemement
, który cyklicznie dzwoni findelement
do momentu znalezienia lub przekroczenia limitu czasu (można ustawić limit czasu).
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
, czekając, aż „document.readyState” będzie „kompletny”. Ze względu na podróż w obie strony z selenu do przeglądarki, wydaje mi się, że warunki wyścigu są złagodzone, a to „zawsze” działa dla mnie. Po „click ()”, kiedy oczekuję załadowania strony, jawnie czekam (za pomocą WebDriverWait) na readystate. ]
Implementacja Ruby:
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
@driver.execute_script("return document.readyState;") == "complete"
}
Możesz usunąć System.out
linię. Jest dodawany do celów debugowania.
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");
}
});
}
Wszystkie te rozwiązania są odpowiednie w określonych przypadkach, ale występują w nich co najmniej jeden z kilku możliwych problemów:
Nie są wystarczająco ogólne - chcą, abyś wiedział z wyprzedzeniem, że jakiś konkretny warunek będzie spełniony na stronie, na której będziesz (np. Wyświetli się jakiś element)
Są otwarci na warunki wyścigu, w których używasz elementu, który jest faktycznie obecny na starej stronie, a także na nowej stronie.
Oto moja próba ogólnego rozwiązania, które pozwala uniknąć tego problemu (w Pythonie):
Po pierwsze, ogólna funkcja „czekaj” (użyj WebDriverWait, jeśli chcesz, uważam je za brzydkie):
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__))
Następnie rozwiązanie polega na tym, że selen rejestruje (wewnętrzny) numer identyfikacyjny dla wszystkich elementów na stronie, w tym najwyższego poziomu <html>
. Gdy strona się odświeża lub ładuje, otrzymuje nowy element HTML z nowym identyfikatorem.
Zakładając, że chcesz kliknąć link z tekstem „mój link”, na przykład:
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)
Aby uzyskać więcej Pythonicznego, wielokrotnego użytku, ogólnego pomocnika, możesz utworzyć menedżera kontekstu:
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)
A potem możesz go używać do praktycznie każdej interakcji selenu:
with wait_for_page_load(browser):
browser.find_element_by_link_text('my link').click()
Myślę, że to kuloodporne! Co myślisz?
Więcej informacji w poście na ten temat tutaj
Możesz także użyć klasy: ExpectedConditions
aby jawnie poczekać, aż element pojawi się na stronie, zanim będziesz mógł podjąć jakiekolwiek działania dalsze działania
Możesz użyć tej ExpectedConditions
klasy do ustalenia, czy element jest widoczny:
WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));
Zobacz ExpectedConditions class Javadoc
listę wszystkich warunków, które możesz sprawdzić.
Odpowiedź Imrana zmieniona dla 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");
}
});
To wydaje się być poważnym ograniczeniem WebDriver. Oczywiście oczekiwanie na element nie będzie oznaczało załadowania strony, w szczególności DOM może zostać w pełni zbudowany (stan już gotowy), w którym JS nadal działa, a CSS i obrazy są ładowane.
Uważam, że najprostszym rozwiązaniem jest ustawienie zmiennej JS na zdarzenie onload po zainicjowaniu wszystkiego i sprawdzenie i oczekiwanie na tę zmienną JS w Selenium.
Jeśli chcesz poczekać na załadowanie określonego elementu, możesz użyć isDisplayed()
metody na 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;
}
}
(Przykład z 5-minutowego przewodnika wprowadzającego )
RenderedWebElement
interfejs API nie ma . Jednak isDisplayed()
metoda jest teraz dostępna bezpośrednio na WebElement
.
Jawnie czekaj lub warunkowo czekaj w tym oczekiwaniu, aż dany warunek zostanie spełniony.
WebDriverWait wait = new WebDriverWait(wb, 60);
wait.until(ExpectedConditions.elementToBeClickable(By.name("value")));
Będzie to czekać na każdy element sieciowy przez 60 sekund.
Użyj niejawnie czekaj na czekanie każdego elementu na stronie do tego czasu.
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
Będzie to czekać na każdy element sieciowy przez 60 sekund.
Użyj niejawnie czekaj na czekanie każdego elementu na stronie do podanego czasu.
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
to czeka na każdy element na stronie przez 30 sekund.
Kolejne oczekiwanie to Jawne oczekiwanie lub warunkowe oczekiwanie w tym oczekiwaniu na spełnienie określonego warunku.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
W id podaj identyfikator elementu statycznego, który jest wyświetlany w sposób widoczny na stronie, zaraz po załadowaniu strony.
Dziwi mnie, że predykaty nie były pierwszym wyborem, ponieważ zazwyczaj wiesz, z którymi elementami wejdziesz w interakcję na stronie, którą chcesz załadować. Moje podejście zawsze polegało na tworzeniu predykatów / funkcji takich jak waitForElementByID(String id)
iwaitForElemetVisibleByClass(String className)
, itd., A następnie wykorzystywania i ponownego wykorzystywania ich tam, gdzie ich potrzebuję, czy to do ładowania strony czy zmiany treści strony, na którą czekam.
Na przykład,
W mojej klasie testowej:
driverWait.until(textIsPresent("expectedText");
W mojej klasie testowej rodzic:
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);
}
Chociaż wydaje się, że to dużo, zajmuje się to sprawdzaniem wielokrotnie, a przedział czasu, w jakim można to sprawdzić, można ustawić wraz z ostatecznym czasem oczekiwania przed przekroczeniem limitu czasu. Ponadto użyjesz takich metod.
W tym przykładzie klasa nadrzędna zdefiniowała i zainicjowała WebDriver driver
oraz WebDriverWait driverWait
.
Mam nadzieję, że to pomoże.
Najlepszym sposobem oczekiwania na załadowanie strony podczas korzystania z powiązań Java dla WebDriver jest użycie wzorca projektu Obiekt strony z PageFactory. Pozwala to na użycie tego, AjaxElementLocatorFactory
co to po prostu działa, jako globalne oczekiwanie na wszystkie twoje elementy. Ma ograniczenia dotyczące takich elementów, jak drop-boxy lub złożone przejścia javascript, ale drastycznie zmniejszy ilość potrzebnego kodu i przyspieszy czas testowania. Dobry przykład można znaleźć w tym blogu. Zakłada się podstawową wiedzę na temat Core Java.
http://startingwithseleniumwebdriver.blogspot.ro/2015/02/wait-in-page-factory.html
Rozwiązanie NodeJS:
W Nodejs możesz to zdobyć poprzez obietnice ...
Jeśli napiszesz ten kod, możesz być pewien, że strona jest w pełni załadowana, kiedy dojdziesz do ...
driver.get('www.sidanmor.com').then(()=> {
// here the page is fully loaded!!!
// do your stuff...
}).catch(console.log.bind(console));
Jeśli napiszesz ten kod, będziesz nawigować, a selen zaczeka 3 sekundy ...
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...
Z dokumentacji Selenium:
this.get (url) → Nie można
Zaplanuje polecenie przejścia do podanego adresu URL.
Zwraca obietnicę, która zostanie rozwiązana po zakończeniu ładowania dokumentu .
Oto wersja Java 8 najpopularniejszej obecnie odpowiedzi :
WebDriverWait wait = new WebDriverWait(myDriver, 15);
wait.until(webDriver -> ((JavascriptExecutor) myDriver).executeScript("return document.readyState").toString().equals("complete"));
Gdzie myDriver
jest WebDriver
obiekt (zadeklarowany wcześniej).
Uwaga : należy pamiętać, że ta metoda ( document.readyState
) sprawdza tylko DOM.
Wywołaj poniżej Funkcja w skrypcie, to poczeka, aż strona nie zostanie załadowana za pomocą 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;
}
});
}
Możesz zmienić dokument na element, w przypadku gdy zmieniana jest tylko część dokumentu.
Technika ta została zainspirowana odpowiedzią firmy 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);
}
};
}
}
I do ciebie użyj :
_waiter = new SeleniumWaiter(_driver);
try {
_waiter.waitForMe(By.xpath("//..."), 10);
}
catch (Exception e) {
// Error
}
Możesz użyć poniższej istniejącej metody, aby ustawić czas dla pageeLoadTimeout w poniższym przykładzie, jeśli ładowanie strony trwa dłużej niż 20 sekund, spowoduje to wyjątek przeładowania strony
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS)
Możesz jawnie poczekać, aż element pojawi się na stronie, zanim podejmiesz jakąkolwiek akcję (np. 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"));
}});
Tego użyłem w podobnym scenariuszu i działa dobrze.
Najlepszy sposób, jaki widziałem, to wykorzystać stalenessOf
ExpectedCondition, aby poczekać, aż stara strona się zepsuje.
Przykład:
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement oldHtml = driver.findElement(By.tagName("html"));
wait.until(ExpectedConditions.stalenessOf(oldHtml));
Poczeka dziesięć sekund, aż stary znacznik HTML przestanie być aktualny, a następnie wyrzuci wyjątek, jeśli tak się nie stanie.
Używam węzła + selenium-webdriver (która wersja to teraz 3.5.0). robię to:
var webdriver = require('selenium-webdriver'),
driver = new webdriver.Builder().forBrowser('chrome').build();
;
driver.wait(driver.executeScript("return document.readyState").then(state => {
return state === 'complete';
}))
Możesz użyć czekania. w zasadzie są dwa rodzaje oczekiwania w selenie
- Niejawne czekanie
Jest to bardzo proste, zobacz składnię poniżej:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
- Jawne czekanie
Jawnie poczekaj lub warunkowo poczekaj, aż wystąpi podany warunek.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
Można stosować inne właściwości, takie jak visblityOf()
,visblityOfElement()
Jeśli ktoś używa selenidu:
public static final Long SHORT_WAIT = 5000L; // 5 seconds
$("some_css_selector").waitUntil(Condition.appear, SHORT_WAIT);
Więcej warunków można znaleźć tutaj: http://selenide.org/javadoc/3.0/com/codeborne/selenide/Condition.html
W moim przypadku wykorzystałem następujące informacje, aby poznać stan ładowania strony. W naszej aplikacji są ładowane pliki gif i słucham ich w następujący sposób, aby wyeliminować niechciany czas oczekiwania w skrypcie.
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")));
}
Gdzie xpath lokalizuje gif w DOM HTML. Następnie możesz również wdrożyć metody działania Kliknij.
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();
}
Człowieku, wszystkie te odpowiedzi wymagają zbyt dużo kodu. To powinno być proste, ponieważ jest dość powszechne.
Dlaczego po prostu nie wstrzyknąć prostego javascript z webdriver i sprawdzić. Jest to metoda, której używam w mojej klasie webscraper. JavaScript jest dość prosty, nawet jeśli nie znasz 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
Jak sprawić, by Selenium czekał na ładowanie strony po kliknięciu, oferuje następujące interesujące podejście:
WebElement
ze starej strony.WebElement
momentu StaleElementReferenceException
wyrzucenia.Przykładowy kod:
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))
.