Search code examples
c#.netseleniumautomationwatin

C# Based Testing Automation Attaching to Existing Browser


C#, Visual Studio 2015, .NET 4.x Framework, Internet Explorer 11 (or latest Chrome), Windows 8.1 Pro workstation.

For testing purposes, using a Windows Forms or Console application written in C#, I need to automate an existing browser instance running on a Windows 8 or 10 system.

I created a Windows Forms application, and I'm able to automate a browser that I start using the Navigate(...) method within the application using the WebBrowser control and do things like click on a button in a Javascript popup, login using a username and password, select an item from the datagridview and click on the "edit" button associated with that item.

However, once that "edit" button is clicked, additional browser windows are created that are now running outside the "scope" of the WebBrowser control.

The web application opens new instances of the browser using window.open(...,_blank);

I've tried working with the NewWindow event, but I don't seem to be able to grab any kind of "handle" or such to the newly opened windows. The event fires, but what I'm seeing when I debug inside the event is just information about the window that I'm currently working with (not the newly spawned window).

The other things I've tried are Selenium and WatIn.

For both, the examples I had an instance of Internet Explorer 11 running on my Windows 8.1 Pro workstation at www.google.com.

Generally, the examples seem to show that for "attaching to an existing instance" of a browser the examples first firing off the browser. I've tried to connect to an existing browser using both libraries, and I've not had success.

I've tried to use the RemoteWebDriver(...) for Selenium, using the InternetExplorer driver. Another Stack Overflow post indicates I don't need the server component running because the browser and application for testing are on the same machine. My code is as follows:

private void doSeleniumStuff()
{
    DesiredCapabilities desired = DesiredCapabilities.InternetExplorer();

    using (IWebDriver driver = new RemoteWebDriver(new Uri("http://www.google.com/wd/hub"), desired))
    {
        IWebElement query = driver.FindElement(By.Name("q"));
        query.SendKeys("Cheese");
        query.Submit();
        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
        wait.Until(d => d.Title.StartsWith("cheese", StringComparison.OrdinalIgnoreCase));
        Console.WriteLine("Page title is: " + driver.Title);
    }
}

I'm somewhat confused about the URL used in the RemoteWebDriver constructor. The documentation doesn't seem to describe this usage well. What is this "/wd/hub" usage all about?

It failes with:

{"Unexpected error. <!DOCTYPE html>\r\n<html lang=en>\r\n  <meta charset=utf-8>\r\n  <meta name=viewport content=\"initial-scale=1, minimum-scale=1, width=device-width\">\r\n  <title>Error 404 (Not Found)!!1</title>\r\n  <style>\r\n    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}\r\n  </style>\r\n  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>\r\n  <p><b>404.</b> <ins>That’s an error.</ins>\r\n  <p>The requested URL <code>/wd/hub/session</code> was not found on this server.  <ins>That’s all we know.</ins>\r\n"}

I've tried using the AttachTo(...) method in WatIn.

[STAThread]
private void doWatNStuff()
{
    using (IE myIE = Browser.AttachTo<IE>(Find.Any))
    {
        DomContainer dc = myIE.DomContainer;
    }
}

Fails in the using with

{"Could not find an IE window matching constraint: Any. Search expired after '30' seconds."}

The example code provided for WatIn has the code first creating an instance of IE and then attaching to it. I can't help but think that WatIn can attach to a running instance of a browser, but WatIn must first create that instance.

That won't meet my needs.

My final attempt was to use System.Windows.Automation to get an open Internet Explorer window and try to work with it. While I get the window, all I can get access to are the Windows and Transform patterns. Hence, I could potentially automate the resizing of the browser window and close it. But, I can't get the DOM or anything useful.

There are a few articles out there about using Interop with MSHTML or SHDocVw, but nothing super helpful.

I would appreciate any guidance anyone can provide on using whatever tools possible for a .NET C# Windows Forms or Console application to use to somehow connect to an independently opened browser window on the same Windows machine and automating it.


Solution

  • I've been using WatiN succesfully for this. A console app with Program.cs that looks like the following works for me:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using WatiN.Core;
    
    namespace WatinTest
    {
      class Program
      {
        [STAThread]
        static void Main(string[] args)
        {
          var ie = IE.AttachTo<IE>(Find.ByTitle(new Regex(".*")));
          foreach (var div in ie.Divs)
          {
            Console.WriteLine(div.IdOrName);
          }
          Console.ReadLine();
        }
      }
    }
    

    This is with Windows 10 and WatiN 2.1.0.