source

셀레늄을 어떻게 아약스 반응을 기다리죠?

bestscript 2023. 3. 21. 22:16

셀레늄을 어떻게 아약스 반응을 기다리죠?

셀레늄이 달력 위젯과 같은 것을 로딩할 때까지 기다리려면 어떻게 해야 하나요?지금 저는 그냥...Thread.sleep(2500)테스트 케이스를 junit 프로그램으로 내보낸 후.

나는 사용할 것이다.

waitForElementPresent(locator)

이것은 요소가 DOM에 표시될 때까지 대기합니다.

요소가 표시되는지 확인해야 하는 경우,

waitForElementHeight(locator)

요소를 기다리는 것보다 더 일반적인 해결책은 서버에 대한 모든 연결이 닫힐 때까지 기다리는 것입니다.그러면 콜백이 없기 때문에 페이지에 영향을 주지 않는 경우에도 모든 Ajax 콜이 완료될 때까지 대기할 수 있습니다.자세한 내용은 여기를 참조하십시오.

C# 및 jQuery를 사용하여 모든 AJax 콜이 완료될 때까지 대기하기 위해 다음 메서드를 작성했습니다(C#에서 JS 변수에 직접 액세스하는 방법을 알고 있는 사람은 코멘트해 주십시오).

internal void WaitForAjax(int timeOut = 15)
{
    var value = "";
    RepeatUntil(
        () => value = GetJavascriptValue("jQuery.active"), 
        () => value == "0", 
        "Ajax calls did not complete before timeout"
    );
}

internal void RepeatUntil(Action repeat, Func<bool> until, string errorMessage, int timeout = 15)
{
    var end = DateTime.Now + TimeSpan.FromSeconds(timeout);
    var complete = false;

    while (DateTime.Now < end)
    {
        repeat();
        try
        {
            if (until())
            {
                complete = true;
                break;
            }
        }
        catch (Exception)
        { }
        Thread.Sleep(500);
    }
    if (!complete)
        throw new TimeoutException(errorMessage);
}

internal string GetJavascriptValue(string variableName)
{
    var id = Guid.NewGuid().ToString();
    _selenium.RunScript(String.Format(@"window.$('body').append(""<input type='text' value='""+{0}+""' id='{1}'/>"");", variableName, id));
    return _selenium.GetValue(id);
}

python을 사용하는 경우 버튼을 클릭하여 DOM 변경을 기다리는 다음 기능을 사용할 수 있습니다.

def click_n_wait(driver, button, timeout=5):
    source = driver.page_source
    button.click()
    def compare_source(driver):
        try:
            return source != driver.page_source
        except WebDriverException:
            pass
    WebDriverWait(driver, timeout).until(compare_source)

(크레디트: 이 스택오버플로우 응답에 근거합니다)

이것은 나에게 효과가 있다.

public  void waitForAjax(WebDriver driver) {
    new WebDriverWait(driver, 180).until(new ExpectedCondition<Boolean>(){
        public Boolean apply(WebDriver driver) {
            JavascriptExecutor js = (JavascriptExecutor) driver;
            return (Boolean) js.executeScript("return jQuery.active == 0");
        }
    });
}

webdriver aka selenium2에서는 https://www.selenium.dev/documentation/en/webdriver/waits/#implicit-wait에 기재되어 있는 암묵적인 대기 설정을 사용할 수 있습니다.

Java 사용:

WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));

또는 python 사용:

from selenium import webdriver

ff = webdriver.Firefox()
ff.implicitly_wait(10) # seconds
ff.get("http://somedomain/url_that_delays_loading")
myDynamicElement = ff.find_element_by_id("myDynamicElement")

아래 코드(C#)를 사용하면 대상 요소가 표시됩니다.

        internal static bool ElementIsDisplayed()
        {
          IWebDriver driver = new ChromeDriver();
          driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp";
          WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
          By locator = By.CssSelector("input[value='csharp']:first-child");
          IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
          {
            return d.FindElement(locator);
          });
          return myDynamicElement.Displayed;
        }

페이지가 jQuery를 지원하는 경우 jQuery.active 함수를 사용하여 모든 Ajax 콜이 종료된 후 타깃 요소가 취득되도록 할 수 있습니다.

 public static bool ElementIsDisplayed()
    {
        IWebDriver driver = new ChromeDriver();
        driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp";
        WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
        By locator = By.CssSelector("input[value='csharp']:first-child");
        return wait.Until(d => ElementIsDisplayed(d, locator));
    }

    public static bool ElementIsDisplayed(IWebDriver driver, By by)
    {
        try
        {
            if (driver.FindElement(by).Displayed)
            {
                //jQuery is supported.
                if ((bool)((IJavaScriptExecutor)driver).ExecuteScript("return window.$ != undefined"))
                {
                    return (bool)((IJavaScriptExecutor)driver).ExecuteScript("return $.active == 0");
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return false;
            }
        }
        catch (Exception)
        {
            return false;
        }
    }

솔루션으로서 다음 방법을 썼습니다(로드 인디케이터는 없었습니다).

public static void waitForAjax(WebDriver driver, String action) {
       driver.manage().timeouts().setScriptTimeout(5, TimeUnit.SECONDS);
       ((JavascriptExecutor) driver).executeAsyncScript(
               "var callback = arguments[arguments.length - 1];" +
                       "var xhr = new XMLHttpRequest();" +
                       "xhr.open('POST', '/" + action + "', true);" +
                       "xhr.onreadystatechange = function() {" +
                       "  if (xhr.readyState == 4) {" +
                       "    callback(xhr.responseText);" +
                       "  }" +
                       "};" +
                       "xhr.send();");
}

그리고 이 메서드를 실제 드라이버로 호출했습니다.자세한 것은, 이 투고입니다.

이것은 나에게 매력적으로 작용한다:

public void waitForAjax() {

    try {
        WebDriverWait driverWait = new WebDriverWait(driver, 10);

        ExpectedCondition<Boolean> expectation;   
        expectation = new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver driverjs) {

                JavascriptExecutor js = (JavascriptExecutor) driverjs;
                return js.executeScript("return((window.jQuery != null) && (jQuery.active === 0))").equals("true");
            }
        };
        driverWait.until(expectation);
    }       
    catch (TimeoutException exTimeout) {

       // fail code
    }
    catch (WebDriverException exWebDriverException) {

       // fail code
    }
    return this;
}

저도 비슷한 상황이었습니다만, 로딩 패널이 없어지도록 Ajax 요구를 기다리고 있었습니다.요청 전후에 html을 조사했더니, Ajax 로딩 패널의 div가 표시되어 있고, Ajax 요구 중에 dix가 표시되고, 요구가 종료된 후에 숨겨져 있습니다.패널이 표시될 때까지 기다렸다가 숨길 때까지 기다리는 기능을 만들었습니다.

public void WaitForModalPanel() { string element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer' and not(contains(@style,'display: none'))]"; WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 2, 0)); wait.Until(ExpectedConditions.ElementIsVisible(By.XPath(element_xpath))); element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer' and contains(@style,'DISPLAY: none')]"; wait.Until(ExpectedConditions.ElementExists(By.XPath(element_xpath))); }

자세한 내용은 이쪽

여기 Morten Christiansen의 대답에 기초한 그루비 버전이 있습니다.

void waitForAjaxCallsToComplete() {
    repeatUntil(
            { return getJavaScriptFunction(driver, "return (window.jQuery || {active : false}).active") },
            "Ajax calls did not complete before timeout."
    )
}

static void repeatUntil(Closure runUntilTrue, String errorMessage, int pollFrequencyMS = 250, int timeOutSeconds = 10) {
    def today = new Date()
    def end = today.time + timeOutSeconds
    def complete = false;

    while (today.time < end) {
        if (runUntilTrue()) {
            complete = true;
            break;
        }

        sleep(pollFrequencyMS);
    }
    if (!complete)
        throw new TimeoutException(errorMessage);
}

static String getJavaScriptFunction(WebDriver driver, String jsFunction) {
    def jsDriver = driver as JavascriptExecutor
    jsDriver.executeScript(jsFunction)
}

위에서 설명한 바와 같이 활성 연결이 닫힐 때까지 기다릴 수 있습니다.

private static void WaitForReady() {
    WebDriverWait wait = new WebDriverWait(webDriver, waitForElement);
    wait.Until(driver => (bool)((IJavaScriptExecutor)driver).ExecuteScript("return jQuery.active == 0"));
}

데이터 전송 속도가 매우 빠르기 때문에 신뢰성이 낮다고 생각합니다. 상의 및 더 페이지 상의 데이터 처리 및 렌더링에도 많은 시간이 소요됩니다.jQuery.active == 0데이터가 아직 페이지에 없을 수 있습니다.

페이지에 요소가 표시될 때까지 명시적으로 기다리는 것이 훨씬 현명합니다. 이와 관련된 몇 가지 답변을 참조하십시오.

웹 응용 프로그램에 사용자 정의 로더가 있거나 데이터가 처리 중임을 나타내는 것이 가장 좋은 상황입니다.이 경우 이 표시가 사라질 때까지 기다릴 수 있습니다.

컨트롤이 "Ajax" 웹 요소인 경우 다음 코드가 Ajax 웹 요소 또는 다른 Ajax 웹 요소가 필요한 작업을 완료하여 보다 안전하게 단계를 계속할 수 있도록 기다립니다.

    public static void waitForAjaxToFinish() {

    WebDriverWait wait = new WebDriverWait(driver, 10);

    wait.until(new ExpectedCondition<Boolean>() { 
        public Boolean apply(WebDriver wdriver) { 
            return ((JavascriptExecutor) driver).executeScript(
                    "return jQuery.active == 0").equals(true);
        }
    }); 

}

다음은 제 가져오기 코드입니다.jQuery.active는 fetch와 함께 사용할 수 없기 때문에 조사 중이었습니다.여기 내가 프록시를 가져오는 것을 도와준 답이 있다, 하지만 이것은 셀레늄을 위한 모조품을 가져오지 않은 에이잭스에 대한 유일한 대답이다.

public static void customPatchXMLHttpRequest(WebDriver driver) {
    try {
        if (driver instanceof JavascriptExecutor) {
            JavascriptExecutor jsDriver = (JavascriptExecutor) driver;
            Object numberOfAjaxConnections = jsDriver.executeScript("return window.openHTTPs");
            if (numberOfAjaxConnections instanceof Long) {
                return;
            }
            String script = "  (function() {" + "var oldFetch = fetch;"
                    + "window.openHTTPs = 0; console.log('starting xhttps');" + "fetch = function(input,init ){ "
                    + "window.openHTTPs++; "

                    + "return oldFetch(input,init).then( function (response) {"
                    + "      if (response.status >= 200 && response.status < 300) {"
                    + "          window.openHTTPs--;  console.log('Call completed. Remaining active calls: '+ window.openHTTPs); return response;"
                    + "      } else {"
                    + "          window.openHTTPs--; console.log('Call fails. Remaining active calls: ' + window.openHTTPs);  return response;"
                    + "      };})" + "};" + "var oldOpen = XMLHttpRequest.prototype.open;"
                    + "XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {"
                    + "window.openHTTPs++; console.log('xml ajax called');"
                    + "this.addEventListener('readystatechange', function() {" + "if(this.readyState == 4) {"
                    + "window.openHTTPs--; console.log('xml ajax complete');" + "}" + "}, false);"
                    + "oldOpen.call(this, method, url, async, user, pass);" + "}" +

                    "})();";
            jsDriver.executeScript(script);
        } else {
            System.out.println("Web driver: " + driver + " cannot execute javascript");
        }
    } catch (Exception e) {
        System.out.println(e);
    }
}

제 경우, 이 문제는 에이잭스 지연으로 보였지만, 메인 페이지 내의 내부 iframe과 관련이 있습니다.셀레미넘에서는, 다음의 기능을 가지는 내부 프레임으로 전환할 수 있습니다.

    driver.switchTo().frame("body");
    driver.switchTo().frame("bodytab");

저는 자바 사용해요.그 후 나는 그 요소를 찾을 수 있었다.

    driver.findElement(By.id("e_46")).click();

prime faces를 사용하고 있는 유저의 경우는, 다음의 작업을 수행합니다.

selenium.waitForCondition("selenium.browserbot.getCurrentWindow().$.active==0", defaultWaitingPeriod);

언급URL : https://stackoverflow.com/questions/2835179/how-to-get-selenium-to-wait-for-ajax-response