Friday, 7 June 2013

Controlling Selenium and Chrome WebDriver from a C# REPL GUI (while fixing UnitTest)

When I was Running TeamMentor WebAutomation UnitTests locally (using Selenium and ChromeDriver), there were a number of TeamMentor's UnitTests that failed, and could not be fixed with simple changes or re-execution

This post shows how I used the O2 Platform's FluentSharp REPL script_Me capabilities to debug the problem and find a solution.

In the TeamMentor's UserTests here is one that was failing:

image

Source code of test shown above, with the error occurring on the selected line (with clickButton.Click() )

image

To debug this issue, lets open up a C# Repl on the beginning of the test (using FluentSharp’s script_Me Extension Method)

image

Running this UnitTest

image

... will open up the browser and the REPL gui

image

Note: the error shown above is a known bug in the O2 Platform REPL generator, and it is due to the fact that the unitTest variable (passed as a parameter to the REPL GUI) is a collection. This is a minor bug in, which can be dealt with by making the parameter a generic value:

image

... and adding a couple references and using statements:

image

With this set-up we can now control the Chome WebDriver from this REPL Scripts.

Some examples:

1) Get a reference to the teamMentor object and call the homePage method:

image

2) get a strongly typed reference to the ChromeDriver object and invoke Navigate and Refresh

image

3) Get XPath using private field value

image

4) get element reference (in this case the ‘OWASP Top 10’ folder from the OWASP library)

image

5) Click on element and the same error we had when executing the UnitTest normally (first screenshot).

image

Basically the problem is that if that folder is currently not visible, the Click event cannot be invoked.

And just to confirm, if we manually expand that folder, we will get no errors:

image

To fix this we need to open up that folder programmatically.

In these cases I always like to look at jQuery and see if I can use it to address the issue.

So in another Chome window, I opened up the developer tools and used the same GUID as used in the UnitTest to find the folder:

image

Then I call the JavaScript method to close all tree nodes:

image

... and the JavaScript method to open/expand that folder:

image

Back into C# code, here are a couple Extension Methods that will allow us to do this programmatically:

image

… which can be used like this:

image

So now we can invoke (from C#) the JavaScript code that closes all folders:

image

… and the script that opens the folder:

image

Next we create a generic lambda method to open the node, and loop it 5 times (for fun):

image ,

And now, the code from the original UnitTest (i.e the Click event) doesn’t fail anymore:

image

… and we can add the rest of the UnitTest (just to make sure it all works)

image

Note that I had to comment out the .Click event since it was closing down the OWASP Top 10 folder (which meant that nodes.first().Text would return an empty value)

image

Now that we have a fix, the final step is to close the REPL window and update the UnitTest with the new jQuery based commands

So back in the UnitTest file (the one that was failing before), I removed the clickButton code and added the code created in the REPL C# environment:

image

And the test now passes :)

image

To fix the other UnitTests that were also failing (on this class), I transformed the Lambda method into a normal C# function:

image

... which was used in this unit test:

image

… and this one

image

And finally, with these fixes, all unit tests now executed ok :)

image


Related posts:




Scripts used in this post:

a) calling teamMentor.HomePage()
var testBase = (TMTestBase<ChromeDriver>)unitTest;

testBase.teamMentor.homePage();

return unitTest;

//using OpenQA.Selenium
//using OpenQA.Selenium.Chrome
//using TeamMentor.UnitTests.Automation
//O2Ref:TeamMentor.UnitTests.Automation.DLL
//O2Ref:WebDriver.dll
//O2Ref:Microsoft.CSharp.dll 

//O2Tag_SetInvocationParametersToDynamic

b) strongly-typed reference to ChromeDriver object
var testBase = (TMTestBase<ChromeDriver>)unitTest;

//testBase.teamMentor.homePage();

var driver = (ChromeDriver)testBase.driver;
driver.Navigate().Refresh();

return unitTest;

//using OpenQA.Selenium
//using OpenQA.Selenium.Chrome
//using TeamMentor.UnitTests.Automation
//O2Ref:TeamMentor.UnitTests.Automation.DLL
//O2Ref:WebDriver.dll
//O2Ref:Microsoft.CSharp.dll 

//O2Tag_SetInvocationParametersToDynamic


) Get JsTree using XPath
var testBase = (TMTestBase<ChromeDriver>)unitTest;

var driver = (ChromeDriver)testBase.driver;

//testBase.teamMentor.homePage();
//driver.Navigate().Refresh();

var privateField = testBase.field<string>("OWASP_JSTRE_XPATH_EXPRESSION");
var jsTree = driver.FindElementex(By.XPath(privateField));
return jsTree;


//using OpenQA.Selenium
//using OpenQA.Selenium.Chrome
//using TeamMentor.UnitTests.Automation
//O2Ref:TeamMentor.UnitTests.Automation.DLL
//O2Ref:WebDriver.dll
//O2Ref:Microsoft.CSharp.dll 

//O2Tag_SetInvocationParametersToDynamic


) get element and click on link
var testBase = (TMTestBase<ChromeDriver>)unitTest;

var driver = (ChromeDriver)testBase.driver;

//testBase.teamMentor.homePage();
//driver.Navigate().Refresh();

var privateField = testBase.field<string>("OWASP_JSTRE_XPATH_EXPRESSION");
var jsTree = driver.FindElementex(By.XPath(privateField));

var clickButton = driver.FindElement(By.XPath(".//*[@id='30b034e7-f3a2-4a81-acbc-e357129711ff']/ins"));

clickButton.Click();

return clickButton;


//using OpenQA.Selenium
//using OpenQA.Selenium.Chrome
//using TeamMentor.UnitTests.Automation
//O2Ref:TeamMentor.UnitTests.Automation.DLL
//O2Ref:WebDriver.dll
//O2Ref:Microsoft.CSharp.dll 

//O2Tag_SetInvocationParametersToDynamic


) Hello from C#
var testBase = (TMTestBase<ChromeDriver>)unitTest;
var driver = (ChromeDriver)testBase.driver;
//testBase.teamMentor.homePage();
//driver.Navigate().Refresh();
var privateField = testBase.field<string>("OWASP_JSTRE_XPATH_EXPRESSION");
var jsTree = driver.FindElementex(By.XPath(privateField));
var clickButton = driver.FindElement(By.XPath(".//*[@id='30b034e7-f3a2-4a81-acbc-e357129711ff']/ins"));

driver.executeJavaScript("alert('hello from C#' )");

clickButton.Click();
return clickButton;


//using OpenQA.Selenium
//using OpenQA.Selenium.Chrome
//using TeamMentor.UnitTests.Automation
//O2Ref:TeamMentor.UnitTests.Automation.DLL
//O2Ref:WebDriver.dll
//O2Ref:Microsoft.CSharp.dll 
//O2Tag_SetInvocationParametersToDynamic


) loop 5 times
var testBase = (TMTestBase<ChromeDriver>)unitTest;
var driver = (ChromeDriver)testBase.driver;
//testBase.teamMentor.homePage();
//driver.Navigate().Refresh();
var privateField = testBase.field<string>("OWASP_JSTRE_XPATH_EXPRESSION");
var jsTree = driver.FindElementex(By.XPath(privateField));
var clickButton = driver.FindElement(By.XPath(".//*[@id='30b034e7-f3a2-4a81-acbc-e357129711ff']/ins"));

Action<string> closeAll_and_OpenNode = 
    (nodeId)=>{
                    driver.executeJavaScript("TM.Gui.LibraryTree.jsTree.close_all();");                    
                    200.wait();
                    driver.executeJavaScript("TM.Gui.LibraryTree.openNode('#{0}')".format(nodeId));
              };
5.loop(()=> closeAll_and_OpenNode("30b034e7-f3a2-4a81-acbc-e357129711ff"));

return  "done";

//clickButton.Click();
return clickButton;


//using OpenQA.Selenium
//using OpenQA.Selenium.Chrome
//using TeamMentor.UnitTests.Automation
//O2Ref:TeamMentor.UnitTests.Automation.DLL
//O2Ref:WebDriver.dll
//O2Ref:Microsoft.CSharp.dll 
//O2Tag_SetInvocationParametersToDynamic


) final REPL code that contained the original UnitTest’s Asserts
var testBase = (TMTestBase<ChromeDriver>)unitTest;
var driver = (ChromeDriver)testBase.driver;

testBase.teamMentor.homePage();
//driver.Navigate().Refresh();

var privateField = testBase.field<string>("OWASP_JSTRE_XPATH_EXPRESSION");

var clickButton = driver.FindElement(By.XPath(".//*[@id='30b034e7-f3a2-4a81-acbc-e357129711ff']/ins"));

Action<string> closeAll_and_OpenNode = 
    (nodeId)=>{
                    driver.executeJavaScript("TM.Gui.LibraryTree.jsTree.close_all();");                    
                    200.wait();
                    driver.executeJavaScript("TM.Gui.LibraryTree.openNode('#{0}')".format(nodeId));
              };
closeAll_and_OpenNode("30b034e7-f3a2-4a81-acbc-e357129711ff");
clickButton.Click();

var jsTree = driver.FindElementex(By.XPath(privateField));
var nodes = jsTree.FindElements(By.TagName("li"));

Assert.IsTrue(nodes.Count == 10, "There should be 10 Items int this library");

Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A01: Injection"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A02: Cross-Site Scripting (XSS)"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A03: Broken Authentication and Session Management"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A04: Insecure Direct Object References"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A05: Cross-Site Request Forgery (CSRF)"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A06: Security Misconfiguration"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A07: Insecure Cryptographic Storage"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A08: Failure to Restrict URL Access"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A09: Insufficient Transport Layer Protection"));
Assert.IsTrue(nodes.Any(x => x.Text.ToString().Trim() == "A10: Unvalidated Redirects and Forwards"));

return "All Asserts where true";

//using NUnit.Framework
//O2Ref:NUnit.Framework.dll

//using OpenQA.Selenium
//using OpenQA.Selenium.Chrome
//using TeamMentor.UnitTests.Automation
//O2Ref:TeamMentor.UnitTests.Automation.DLL
//O2Ref:WebDriver.dll
//O2Ref:Microsoft.CSharp.dll 
//O2Tag_SetInvocationParametersToDynamic