Selenium Webdriver中的弹出窗口

11

我正在使用C# Winform中的Selenium Firefox WebDrivers,我有以下代码以获取单击“webtraffic_popup_start_button”时显示的弹出窗口的句柄,但是它应该获取弹出窗口的句柄,但弹出窗口的句柄与当前句柄相同。

string current = driver.CurrentWindowHandle;
driver.FindElement(By.XPath("//*[@id='webtraffic_popup_start_button']")).Click();
Thread.Sleep(Sleep_Seconds);
popup = driver.CurrentWindowHandle;
Thread.Sleep(3000);
driver.SwitchTo().Window(current);
Thread.Sleep(1000);
任何帮助都将不胜感激,谢谢。 这就是弹出窗口的样子。 Popup_Image

请解释一下 current。您是指父窗口吗?还是有另一个弹出窗口? - Saifur
我不知道,请查看我上传的截图。 - Coderz
3个回答

27

WebDriver绝对不会跟踪任何东西来检测 OS 中实际处于前台的窗口,并且在新的浏览器窗口打开时不会自动切换。这意味着获取新打开的弹出窗口的句柄的正确方法是一个多步骤的过程。要做到这一点,您需要:

  1. 将当前聚焦的窗口句柄保存到变量中,以便稍后可以切换回它。
  2. 获取当前打开的窗口句柄列表。
  3. 执行将导致新窗口出现的操作。
  4. 等待窗口句柄数增加1。
  5. 获取新的窗口句柄列表。
  6. 在句柄列表中找到新的句柄。
  7. 切换到该新窗口。

在使用.NET语言绑定的代码中,可能看起来像这样:

string currentHandle = driver.CurrentWindowHandle;
ReadOnlyCollection<string> originalHandles = driver.WindowHandles;

// Cause the popup to appear
driver.FindElement(By.XPath("//*[@id='webtraffic_popup_start_button']")).Click();

// WebDriverWait.Until<T> waits until the delegate returns
// a non-null value for object types. We can leverage this
// behavior to return the popup window handle.
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
string popupWindowHandle = wait.Until<string>((d) =>
{
    string foundHandle = null;

    // Subtract out the list of known handles. In the case of a single
    // popup, the newHandles list will only have one value.
    List<string> newHandles = driver.WindowHandles.Except(originalHandles).ToList();
    if (newHandles.Count > 0)
    {
        foundHandle = newHandles[0];
    }

    return foundHandle;
});

driver.SwitchTo().Window(popupWindowHandle);

// Do whatever you need to on the popup browser, then...
driver.Close();
driver.SwitchTo().Window(currentHandle);

或者,如果您正在使用.NET绑定,WebDriver.Support程序集中有一个PopupWindowFinder类专门设计用于为您执行这些操作。使用该类要简单得多。

// Get the current window handle so you can switch back later.
string currentHandle = driver.CurrentWindowHandle;

// Find the element that triggers the popup when clicked on.
IWebElement element = driver.FindElement(By.XPath("//*[@id='webtraffic_popup_start_button']"));

// The Click method of the PopupWindowFinder class will click
// the desired element, wait for the popup to appear, and return
// the window handle to the popped-up browser window. Note that
// you still need to switch to the window to manipulate the page
// displayed by the popup window.
PopupWindowFinder finder = new PopupWindowFinder(driver);
string popupWindowHandle = finder.Click(element);

driver.SwitchTo().Window(popupWindowHandle);

// Do whatever you need to on the popup browser, then...
driver.Close();

// Switch back to parent window
driver.SwitchTo().Window(currentHandle);

1
发生了未处理的异常类型 'OpenQA.Selenium.WebDriverTimeoutException',位于 WebDriver.Support.dll 中。额外信息:在 5 秒后超时。 - Coderz
我认为这是因为当它弹出时已经是前置窗口了。 - Coderz
它有点起作用,但在使用driver.Close()后我无法再使用我的驱动程序。 - Coderz
我经常这样做,但是当尝试访问在单击“点击以呼叫”Skype按钮时自动打开的窗口时遇到了问题。我获取了其他窗口句柄并切换到它,但一旦我切换到它,webdriver.url就无法读取,并且整个webdriver对象在几秒钟后变得无法使用。 - Kesty
1
@Gokul 不是的,从来都不是。那是代码示例中的一个打字错误,我已经编辑了答案进行更正。 - JimEvans
显示剩余3条评论

5
如果最后打开的窗口是您的目标,则只需在单击后执行以下操作。
driver.SwitchTo().Window(driver.WindowHandles.ToList().Last());

编辑

//You may need to go back to parent window to perform additional actions;

// to the new window
driver.SwitchTo().Window(driver.WindowHandles.ToList().Last());

 // to the new window
driver.SwitchTo().Window(driver.WindowHandles.ToList().First());
//or
driver.SwitchTo().DefaultContent();

1
WindowHandles 属性返回的列表是明确无序的。不能保证新打开的窗口句柄在列表中的位置,也不能保证第一个浏览器窗口在列表中的位置。此外,DefaultContent 只能切换到给定窗口中的顶层框架;它不会在窗口之间切换焦点。 - JimEvans
@JimEvans 感谢您提供 WindowHandles 的内部信息。那么您会如何处理他的问题呢? - Saifur
请看我的回答,了解我如何处理这个问题。 - JimEvans

0

我有一些你可能会喜欢的代码。最快的解决方案是使用弹出窗口查找器,但我也制作了自己的方法。我永远不会依赖于窗口句柄的顺序来选择适当的窗口。弹出窗口查找器:

PopupWindowFinder finder = new PopupWindowFinder(driver);
driver.SwitchTo().Window(newWin); 

我的自定义方法。基本上,您需要传递要点击的元素、webdriver以及可选的点击元素后等待搜索的时间。

它获取所有当前句柄并创建一个列表。使用该列表从中排除先前存在的窗口,以防意外切换。然后单击启动新窗口的元素。单击后应始终有一定的延迟,因为没有立即发生任何事情。然后它创建一个新列表,并将其与旧列表进行比较,直到找到一个新窗口或循环过期为止。如果未找到新窗口,则返回 null,因此,如果您有一个不总是有效的 iffy webelement,可以进行空值检查以查看切换是否起作用。

public static string ClickAndSwitchWindow(IWebElement elementToBeClicked,
IWebDriver driver, int timer = 2000)
        {
            System.Collections.Generic.List<string> previousHandles = new 
System.Collections.Generic.List<string>();
            System.Collections.Generic.List<string> currentHandles = new 
System.Collections.Generic.List<string>();
        previousHandles.AddRange(driver.WindowHandles);
        elementToBeClicked.Click();

        Thread.Sleep(timer);
        for (int i = 0; i < 20; i++)
        {
            currentHandles.Clear();
            currentHandles.AddRange(driver.WindowHandles);
            foreach (string s in previousHandles)
            {
                currentHandles.RemoveAll(p => p == s);
            }
            if (currentHandles.Count == 1)
             {
                driver.SwitchTo().Window(currentHandles[0]);
                Thread.Sleep(100);
                return currentHandles[0];
            }
            else
            {
                Thread.Sleep(500);
            }
        }
        return null;
    }

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