当新窗口打开并恢复执行时,Selenium Web Driver如何知道?

31

我在使用Selenium WebDriver自动化一个Web应用程序时遇到了问题。

当页面上有一个按钮,点击该按钮会打开一个新窗口。当我使用以下代码时,它会抛出 OpenQA.Selenium.NoSuchWindowException: No window found 异常。

WebDriver.FindElement(By.Id("id of the button that opens new window")).Click();
//Switch to new window
_WebDriver.SwitchTo().Window("new window name");
//Click on button present on the newly opened window
_WebDriver.FindElement(By.Id("id of button present on newly opened window")).Click();
为了解决上述问题,我在按钮单击和 SwitchTo 语句之间添加了 Thread.Sleep(50000);
WebDriver.FindElement(By.Id("id of the button that opens new window")).Click();
Thread.Sleep(50000); //wait
//Switch to new window
_WebDriver.SwitchTo().Window("new window name");
//Click on button present on the newly opened window
_WebDriver.FindElement(By.Id("id of button present on newly opened window")).Click();

这解决了问题,但我不想使用Thread.Sleep(50000);语句,因为如果窗口打开需要更长时间,代码就会失败;如果窗口快速打开,则会不必要地使测试变慢。

有没有办法知道窗口何时打开,然后测试可以恢复其执行?

9个回答

30

在执行任何操作之前,您需要把控制权切换到弹出窗口。使用这个方法可以解决你的问题。

在打开弹出窗口之前获取主窗口的句柄并保存它。

String mwh=driver.getWindowHandle();

现在尝试执行一些操作来打开弹出窗口:

driver.findElement(By.xpath("")).click();

Set s=driver.getWindowHandles(); //this method will gives you the handles of all opened windows

Iterator ite=s.iterator();

while(ite.hasNext())
{
    String popupHandle=ite.next().toString();
    if(!popupHandle.contains(mwh))
    {
        driver.switchTo().window(popupHandle);
        /**/here you can perform operation in pop-up window**
        //After finished your operation in pop-up just select the main window again
        driver.switchTo().window(mwh);
    }
}

4
有时会出现打开新标签页但句柄尚未添加到驱动实例的情况。我的解决方案是在单击之前获取当前句柄计数,然后在 while 循环内检查计数是否改变。只有在计数改变时才切换到新打开的标签页,如此 driver.switchTo().window(handles[handles.count() - 1]);,其中 handles 在每次迭代中都会更新。 - Mr. Blond
@Mr.Blond 我同意。这是导致Selenium随机失败的核心问题。解决方案是进行数量对比,最好是在WebDriverWait.until()函数内部进行。 - Marinos An

13

你可以等待操作成功,例如在Python中:

from selenium.common.exceptions    import NoSuchWindowException
from selenium.webdriver.support.ui import WebDriverWait

def found_window(name):
    def predicate(driver):
        try: driver.switch_to_window(name)
        except NoSuchWindowException:
             return False
        else:
             return True # found window
    return predicate

driver.find_element_by_id("id of the button that opens new window").click()        
WebDriverWait(driver, timeout=50).until(found_window("new window name"))
WebDriverWait(driver, timeout=10).until( # wait until the button is available
    lambda x: x.find_element_by_id("id of button present on newly opened window"))\
    .click()

2

我终于找到了答案,我使用以下方法切换到新窗口:

public String switchwindow(String object, String data){
        try {

        String winHandleBefore = driver.getWindowHandle();

        for(String winHandle : driver.getWindowHandles()){
            driver.switchTo().window(winHandle);
        }
        }catch(Exception e){
        return Constants.KEYWORD_FAIL+ "Unable to Switch Window" + e.getMessage();
        }
        return Constants.KEYWORD_PASS;
        }

我使用以下代码来切换到父窗口:

 public String switchwindowback(String object, String data){
            try {
                String winHandleBefore = driver.getWindowHandle();
                driver.close(); 
                //Switch back to original browser (first window)
                driver.switchTo().window(winHandleBefore);
                //continue with original browser (first window)
            }catch(Exception e){
            return Constants.KEYWORD_FAIL+ "Unable to Switch to main window" + e.getMessage();
            }
            return Constants.KEYWORD_PASS;
            }

我认为这将有助于您在窗口之间进行切换。


1
    WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(max duration you want it to check for new window));
    wait.until(ExpectedConditions.numberOfWindowsToBe(2));//here 2 represents the current window and the new window to be opened

2
请不要仅仅发布代码作为答案,还要提供解释您的代码是如何解决问题的。带有解释的答案通常更有帮助和更高质量,并且更有可能吸引赞同。 - Mark Rotteveel

1
你可以使用WebDriverWait等待另一个窗口弹出。 首先,你需要保存所有已打开窗口的当前句柄:
private Set<String> windowHandlersSet = driver.getWindowHandles();

然后您单击按钮打开一个新窗口并等待它弹出:

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(driver -> !driver.getWindowHandles().equals(windowHandlersSet));

这个功能检查当前窗口句柄集合是否与保存的句柄集合有变化。我在Internet Explorer下编写测试时使用了这个解决方案,因为打开新窗口总是需要几秒钟的时间。


1

我使用这个方法等待窗口打开,它对我有效。

C#代码:

public static void WaitUntilNewWindowIsOpened(this RemoteWebDriver driver, int expectedNumberOfWindows, int maxRetryCount = 100)
    {
        int returnValue;
        bool boolReturnValue;
        for (var i = 0; i < maxRetryCount; Thread.Sleep(100), i++)
        {
            returnValue = driver.WindowHandles.Count;
            boolReturnValue = (returnValue == expectedNumberOfWindows ? true : false);
            if (boolReturnValue)
            {
                return;
            }
        }
        //try one last time to check for window
        returnValue = driver.WindowHandles.Count;
        boolReturnValue = (returnValue == expectedNumberOfWindows ? true : false);
        if (!boolReturnValue)
        {
            throw new ApplicationException("New window did not open.");
        }
    }

然后我在代码中调用这个方法。
Extensions.WaitUntilNewWindowIsOpened(driver, 2);

0

Js 代码

     await firstPage.clickOnLink();
        let tabs = await driver.getAllWindowHandles();
        await driver.switchTo().window(tabs[1]);
        await driver.wait(await until.titleContains('myString'), 2000);

0

虽然这个问题已经有了答案,但是对我来说它们都不是很有用,因为我不能依赖于获取任何新窗口,我需要进行更多的过滤,所以我开始使用Dadoh的解决方案,但是我对它进行了一些调整,直到我想出了这个解决方案,希望它能对某人有所帮助。

public async Task<string> WaitUntilNewWindowIsOpen(string expectedWindowTitle, bool switchToWindow, int maxRetryCount = 100)
{
    string newWindowHandle = await Task.Run(() =>
    {
        string previousWindowHandle = _driver.CurrentWindowHandle;
        int retries = 0;
        while (retries < maxRetryCount)
        {
            foreach (string handle in _driver.WindowHandles)
            {
                _driver.SwitchTo().Window(handle);
                string title = _driver.Title;
                if (title.Equals(expectedWindowTitle))
                {
                    if(!switchToWindow)
                        _driver.SwitchTo().Window(previousWindowHandle);
                    return handle;
                }
            }
            retries++;
            Thread.Sleep(100);
        }
        return string.Empty;
    });
    return newWindowHandle;
}

所以在这个解决方案中,我选择将预期的窗口标题作为参数传递给函数,循环所有窗口并比较新窗口标题,这样可以确保返回正确的窗口。以下是调用此方法的示例:
await WaitUntilNewWindowIsOpen("newWindowTitle", true);

0
以下函数可以等待给定的最大时间,直到您的新窗口打开。
public static void waitForWindow(int max_sec_toWait, int noOfExpectedWindow) {
    FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver);
    wait.pollingEvery(Duration.ofMillis(200));
    wait.withTimeout(Duration.ofSeconds(max_sec_toWait));
    wait.ignoring(NoSuchWindowException.class);
    Function<WebDriver, Boolean> function = new Function<WebDriver, Boolean>(){
        @Override
        public Boolean apply(WebDriver driver) {
            Set<String> handel = driver.getWindowHandles();
            if(handel.size() == noOfExpectedWindow) 
                return true;
            else 
                return false;
            }
    };      
    wait.until(function);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接