Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use Selenium with C# windows Form that contains a WebBrowser object?

I'm wondering if it's possible to use Selenium with a C# Windows Form that contains a WebBrowser object.

I'm using selenium and I'm able to create test cases with the Selenium script record; I'm just trying to pinpoint whether or not I can export the C# code and have it all run within a C# environment. I appreciate any thoughts or explanations.

update I got to the point to have Selenium open the WinForm which contains the WebBrowser Component. However from there my test won't execute. Looks like it doesn't understand selenium command. I don't see any error messages being thrown though. hmmm

The thing is winformWithWebBrowserTest.exe which opens winForm with webbrowser is opened. But nothing happens. Below code is the one that fires up the .exe

test code (Selenium command)

namespace ClassLibrary1
{
    class Class2
    {
        private ISelenium selenium;
        private StringBuilder verificationErrors;

        [SetUp]
        public void SetupTest()
        {

            selenium = new DefaultSelenium
                ("localhost", 4444, "*custom C:\\Users\\m-tak\\Documents\\Visual Studio 2010\\Projects\\winformWithWebBrowserTest\\winformWithWebBrowserTest\\bin\\Release\\winformWithWebBrowserTest.exe", "http://www.livemocha.com");

            selenium.Start();
            verificationErrors = new StringBuilder();
        }

        [TearDown]
        public void TeardownTest()
        {
            selenium.Stop();
        }

        [Test]
        public void TheUntitledTest()
        {
            //nothing here gets executed :(
            Console.WriteLine("foobar front");
            selenium.Open("/");
            Console.WriteLine("foobar");
            selenium.WaitForPageToLoad("30000");
            selenium.Open("/users/logout");
            selenium.Open("/users/login");


        }

    }
}

.exe

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
           //this gets executed always......
            HtmlElement head = webBrowser1.Document.GetElementsByTagName("body")[0]; 
            HtmlElement scriptOne = webBrowser1.Document.CreateElement("script");
            IHTMLScriptElement element = (IHTMLScriptElement)scriptOne.DomElement;
            element.text = "function sayHello() { " + 
                "alert('hello');" +
                " }" ;
            head.AppendChild(scriptOne);
            webBrowser1.Document.InvokeScript("sayHello");


        }
        //getter setter..... 
        public WebBrowser getWebBrowser() 
        {
            return this.webBrowser1;

        }
        public void setWebBrowser(WebBrowser wb) 
        {
            this.webBrowser1 = wb;
        }

        //just address bar
        private void button1_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(textBox1.Text))
            {
                webBrowser1.Navigate(textBox1.Text);
            }
        }

        //just address bar
        private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
        {
            if (textBox1.Text != e.Url.ToString())
            {
                textBox1.Text = e.Url.ToString();
            }
        }

    }
}

UPDATE

I made my test simple so now I don't use NUnit. I've created C# console app just to run the c# .exe file. But in my console it would output "--1--" and "--2--" only..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Selenium;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("-- 1---");
            ISelenium selenium;


            selenium = new DefaultSelenium
                ("localhost", 4444, "*custom C:\\Users\\m-takayashiki\\Documents\\Visual Studio 2010\\Projects\\winformWithWebBrowserTest\\winformWithWebBrowserTest\\bin\\Release\\winformWithWebBrowserTest.exe", "http://www.livemocha.com");

            Console.WriteLine("-- 2---");

            selenium.Start();

            Console.WriteLine("-- 3---");


            selenium.Open("/");
            selenium.WaitForPageToLoad("30000");
            selenium.Open("/users/logout");
            selenium.Open("/users/login");


            Console.WriteLine("test test test");

            //tear down
            selenium.Stop();

        }
    }
}

UPDATE

I checked RC log:

15:40:35.379 DEBUG [13] org.openqa.jetty.http.HttpContext - Handler org.openqa.selenium.server.SeleniumDriverResourceHandler in HttpContext[/selenium-server,/selenium-server]
15:40:35.380 DEBUG [13] org.openqa.selenium.server.SeleniumDriverResourceHandler - req: POST /selenium-server/driver/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: localhost:4444
Content-Length: 247
Expect: 100-continue
Connection: keep-alive

15:40:45.408 DEBUG [13] org.openqa.selenium.server.FrameGroupCommandQueueSet - waiting for window 'null' local frame 'null' for 1790 more secs
15:40:45.408 DEBUG [13] org.openqa.selenium.server.FrameGroupCommandQueueSet - waiting for condition for 1000 more ms
15:40:45.945 DEBUG [12] org.openqa.selenium.server.FrameGroupCommandQueueSet - got condition? : false
like image 800
Meow Avatar asked Feb 23 '11 11:02

Meow


2 Answers

I haven't done this with selenium, but I did the same thing with WatiN when I wanted to automate InfoPath. The approach that I took was as follows:

  1. Kill off all the processes that have that name in my case it was "infopath"

    foreach (Process proc in Process.GetProcessesByName("infopath"))
    {    
      proc.Kill();
    }
    
  2. Launch your test application call Process.GetProcessesByName to get all the process id's then get the window handle of the first as there should only be one process running.

    Process[] updatedInfopathProcessList = Process.GetProcessesByName("infopath");
    if (updatedInfopathProcessList[0].Id == 0)
    {
        throw new ApplicationException("No Infopath processes exist");
    }
    infopathProcessId = updatedInfopathProcessList[0].Id;
    infopathHwnd = updatedInfopathProcessList[0].MainWindowHandle;
    
  3. Now that I had the window handle, I get the IHTMLDocument2 to automate against with WatiN.

    InternalHTMLDOMDocument = IEDom.IEDOMFromhWnd(this.hWnd);
    

The real rubber hits the road in the IEDom class ... code is below ...

    namespace ItiN
    {
        public class IEDom
        {

            internal static IHTMLDocument2 IEDOMFromhWnd(IntPtr hWnd)
            {
                Guid IID_IHTMLDocument2 = new Guid("626FC520-A41E-11CF-A731-00A0C9082637");

                Int32 lRes = 0;
                Int32 lMsg;
                Int32 hr;

                //if (IsIETridentDlgFrame(hWnd))
                //{
                    if (!IsIEServerWindow(hWnd))
                    {
                        // Get 1st child IE server window
                        hWnd = NativeMethods.GetChildWindowHwnd(hWnd, "Internet Explorer_Server");
                    }

                    if (IsIEServerWindow(hWnd))
                    {
                        // Register the message
                        lMsg = NativeMethods.RegisterWindowMessage("WM_HTML_GETOBJECT");
                        // Get the object
                        NativeMethods.SendMessageTimeout(hWnd, lMsg, 0, 0, NativeMethods.SMTO_ABORTIFHUNG, 1000, ref lRes);
                        if (lRes != 0)
                        {
                            // Get the object from lRes
                            IHTMLDocument2 ieDOMFromhWnd = null;
                            hr = NativeMethods.ObjectFromLresult(lRes, ref IID_IHTMLDocument2, 0, ref ieDOMFromhWnd);
                            if (hr != 0)
                            {
                                throw new COMException("ObjectFromLresult has thrown an exception", hr);
                            }
                            return ieDOMFromhWnd;
                        }
                    }
               // }
                return null;
            }

            internal static bool IsIETridentDlgFrame(IntPtr hWnd)
            {
                return UtilityClass.CompareClassNames(hWnd, "Internet Explorer_TridentDlgFrame");
            }

            private static bool IsIEServerWindow(IntPtr hWnd)
            {
                return UtilityClass.CompareClassNames(hWnd, "Internet Explorer_Server");
            }
        }
    }

Sorry this is not for selenium, but it is how I solved the problem for WatiN so I hope it can help. The code is here http://itin.codeplex.com/, and I also think that this has been added to WatiN as well.

like image 75
Bruce McLeod Avatar answered Nov 12 '22 20:11

Bruce McLeod


In theory it would work as long Selenium can inject JS into the browser component. You would need to use the *custom command and pass in the executable that you want Selenium to start up and then it will try do what you want.

like image 28
AutomatedTester Avatar answered Nov 12 '22 18:11

AutomatedTester