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 findElementmetodę 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 findelementdo 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.outlinię. 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 ExpectedConditionsklasy do ustalenia, czy element jest widoczny:
WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));
Zobacz ExpectedConditions class Javadoclistę 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 )
RenderedWebElementinterfejs 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 driveroraz 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, AjaxElementLocatorFactoryco 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 myDriverjest WebDriverobiekt (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:
WebElementze starej strony.WebElementmomentu StaleElementReferenceExceptionwyrzucenia.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)).