Injecting FirebugLite and jQuery into a IE Automation page (JPetStore Example)
When doing IE Automation, sometimes the best way to find out what is happening at the page we’re working on is to open FireBug on it. A cool (and very powerfull) alternative is to use FirebugLite which can be embeded on any page and works on most browsers.
As you can see here http://getfirebug.com/firebuglite#Stable one way to fireup FirebugLite is to execute that bit of Javascript they show under the ‘Add the following link to your bookmarks:’ section.
When using O2’s IE Automation this can be achieved like this:
var firebugLiteScript = "(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');"; ie.eval(firebugLiteScript );
And since that works, let’s add it as an Extension method to the WatiN_ExtensionMethods.cs file that is used on the IE Automation script:
public static class WatiN_IE_ExtensionMethods_FireBugLite { public static WatiN_IE inject_FirebugLite(this WatiN_IE ie) { var firebugLiteScript = "(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');"; ie.eval(firebugLiteScript); return ie; } }
While we are here lets also add jQuery directly (without the need to use the IE_JQuery.cs api)
ie.eval("<a href="http://code.jquery.com/jquery-1.6.2.min.js%22.uri().getHtml">http://code.jquery.com/jquery-1.6.2.min.js".uri().getHtml</a>());
or as an extension method:
public static class WatiN_IE_ExtensionMethods_JQuery { public static WatiN_IE inject_jQuery(this WatiN_IE ie) { ie.eval("<a href="http://code.jquery.com/jquery-1.6.2.min.js%22.uri().getHtml">http://code.jquery.com/jquery-1.6.2.min.js".uri().getHtml</a>()); return ie; } }
Puting it all together, here is a script that:
- opens up JPetStore
- Injects FirebugLite
- Injects jQuery
- runs a jQuery command that:
- selects an ‘a’ with the content ‘Enter the Store’
- changes its border to 2px
- changes it size to 20px
- fades it out in 2000 ms
- fades is it in 2000 ms
var topPanel = panel.clear().add_Panel(); var ie = topPanel.add_IE().silent(true); var jPetStore = new API_JPetStore(ie); jPetStore.homePage(); ie.inject_FirebugLite(); ie.inject_jQuery(); ie.eval("jQuery(\"a:contains('Enter the Store')\").css({'border': 'solid 10px', 'font-size' : 30 } ).fadeOut(2000).fadeIn(2000);");</pre>   //O2File:API_JPetStore.cs //O2File:WatiN_IE_ExtensionMethods.cs //using O2.XRules.Database.Utils.O2 //O2Ref:WatiN.Core.1x.dll
Here is a screenshot what what this looks like:
Using jQuery to consume an ASP.NET Ashx from JSON in two files
Here is a basic asp.net based JSON based ‘webservice’ which can be implemented with two files and requires no compilation
Javascript code (DataHandler.js):
function CallHandler() { $.ajax({ url: "DataHandler.ashx", contentType: "application/json; charset=utf-8", dataType: "json", data: { 'page': '100AAAAAf00' }, responseType: "json", success: OnComplete, error: OnFail }); return false; } function OnComplete(result) { alert(JSON.stringify(result)); } function OnFail(result) { alert('Request Failed'); } CallHandler();
Server side code (DataHandler.ashx):
<%@ WebHandler Language="C#" %></pre> using System.Text; using System.Web; using System.Web.Script.Serialization; public class test { public string var1 {get;set;} public string var2 {get;set;} public string page {get;set;} public test() { var1 = "aaaa"; var2 = "bbb"; } } public class GetDataHandler : IHttpHandler { public HttpContext context; public HttpRequest request; public HttpResponse response; public void handleRequest() { //writeRaw("this is a message"); writeJson(new test() { page = request["page"] }); } public bool IsReusable { get { return false; } } public void ProcessRequest (HttpContext _context) { context = _context; request = _context.Request; response = _context.Response; context.Response.ContentType = "application/json"; context.Response.ContentEncoding = Encoding.UTF8; handleRequest(); } public void writeJson(object _object) { JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); string jsondata = javaScriptSerializer.Serialize(_object); writeRaw(jsondata); } public void writeRaw(string text) { context.Response.Write(text); } }
result
{"var1":"aaaa","var2":"bbb","page":"100AAAAAf00"}
references
based on source code samples from:
Creating a jQuery DataTable using serialized JSON object
Using the same technique used in the JsTree example, here is a DataTable dynamically populated from a serialized JSON object
//based on example from http://www.datatables.net/release-datatables/examples/data_sources/js_array.html //var topPanel = panel.clear().add_Panel(); var topPanel = "Poc - jQuery jsDataTable".popupWindow(900,400); var jsDataTable = new JsDataTable(); jsDataTable .add_Row("Trident", "Internet Explorer 4.0", "Win 95+", 4, "X") .add_Row("Trident", "Internet Explorer 4.0", "Win 95+", 4, "X") .add_Row("Trident", "Internet Explorer 5.0", "Win 95+", 5, "C") .add_Row("Trident", "Internet Explorer 5.5", "Win 95+", 5.5, "A") .add_Row("Trident", "Internet Explorer 6.0", "Win 98+", 6, "A") .add_Row("Trident", "Internet Explorer 7.0", "Win XP SP2+", 7, "A") .add_Row("Gecko", "Firefox 1.5", "Win 98+ / OSX.2+", 1.8, "A") .add_Row("Gecko", "Firefox 2", "Win 98+ / OSX.2+", 1.8, "A") .add_Row("Gecko", "Firefox 3", "Win 2k+ / OSX.3+", 1.9, "A") .add_Row("Webkit", "Safari 1.2", "OSX.3", 125.5, "A") .add_Row("Webkit", "Safari 1.3", "OSX.3", 312.8, "A") .add_Row("Webkit", "Safari 2.0", "OSX.4+", 419.3, "A") .add_Row("Webkit", "Safari 3.0", "OSX.4+", 522.1, "A"); jsDataTable.add_Columns("Engine", "Browser", "Platform"); jsDataTable.add_Column("Version", "center"); jsDataTable.add_Column("Grade", "center"); //adding without using extension methods /*jsDataTable.aaData.Add(new List<string> { "Trident", "Internet Explorer 4.0", "Win 95+", "4", "X" }); jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Engine" }); jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Browser" }); jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Platform" }); jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Version" , sClass = "center" }); jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Grade" , sClass = "center" }); */ //return "{0}".info(jsDataTable.jsonString()); var ie = topPanel.add_IE(); ie.showMessage("loading dataTable js and css"); var jQuery = new IE_JQuery(ie); var cssFile = @"dataTable_demo.css".local(); var styleHtml = "<style type=\"text/css\"> {0} </style>".format(cssFile.fileContents().fixCRLF().remove("".line()).replace("'", "\"")); var injectCSS = "$('body').append($('" + styleHtml + "' ))"; ie.invokeEval(injectCSS); //ie.invokeEval("<a href="http://static.jstree.com/v.1.0pre/jquery.jstree.js%22.uri().getHtml">http://static.jstree.com/v.1.0pre/jquery.jstree.js".uri().getHtml</a>()); // use to get jstree from jstree.com website ie.invokeEval("jquery.dataTables.min.js".local().fileContents()); jQuery.element("div").html("creating local table"); ie.invokeEval("$('body').append($('<div id=\"testDataTable\"/>')); $('#testDataTable').html('test DataTable will go here');"); ie.invokeEval("$('#testDataTable').html( '<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"display\" id=\"example\"></table>' );"); //ie.invokeEval("%('#example').html('aaaaaaaaa')"); /*var dataToBuildTable = " { " + " 'aaData': [" + " [ 'Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X' ], " + " [ 'Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C' ], " + " [ 'Gecko', 'Firefox 3', 'Win 2k+ / OSX.3+', 1.9, 'A' ], " + " [ 'Webkit', 'Safari 1.2', 'OSX.3', 125.5, 'A' ] " + " ]," + " 'aoColumns': [" + " { 'sTitle': 'Engine' }, " + " { 'sTitle': 'Browser' }, " + " { 'sTitle': 'Platform' }, " + " { 'sTitle': 'Version', 'sClass': 'center' }, " + " { 'sTitle': 'Grade', 'sClass': 'center' } " + " ]" + " } "; */ var dataToBuildTable = jsDataTable.jsonString(); ie.invokeEval(" $('#example').dataTable( " + dataToBuildTable +" );" ); //O2File:JsDataTable.cs return "done"; //O2File:IE_JQuery.cs //O2Ref:WatiN.Core.1x.dll //O2Ref:Microsoft.mshtml.dll
Here is the code of the JsDataTable class:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Script.Serialization; using System.Windows.Forms; using O2.Kernel.ExtensionMethods; using O2.DotNetWrappers.ExtensionMethods; //O2Ref:System.Web.Extensions.dll namespace O2.XRules.Database.APIs { public class JsDataTable { public List<List<object>> aaData; public List<JsDataColumn> aoColumns; public JsDataTable() { aaData = new List<List<object>>() ; aoColumns = new List<JsDataColumn>(); } public class JsDataColumn { public string sTitle {get; set; } public string sClass {get; set; } } } public static class JsDataTable_ExtensionMethods { public static string jsonString(this object _object) { return new JavaScriptSerializer().Serialize(_object); } public static JsDataTable add_Row(this JsDataTable jsDataTable, params object[] cells) { return jsDataTable.add_Row(cells.toList()); } public static JsDataTable add_Row(this JsDataTable jsDataTable, List<object> cells) { jsDataTable.aaData.Add(cells); return jsDataTable; } public static JsDataTable add_Column(this JsDataTable jsDataTable, string title) { return jsDataTable.add_Column(title, null); } public static JsDataTable add_Column(this JsDataTable jsDataTable, string title, string _class) { jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = title, sClass = _class }); return jsDataTable; } public static JsDataTable add_Columns(this JsDataTable jsDataTable, params string[] titles) { foreach(var title in titles) jsDataTable.add_Column(title); return jsDataTable; } } }
Creating a jQuery jsTree using serialized JSON object
Based on the code samples from http://mohyuddin.blogspot.com/2009/05/binding-jstree-programmatically-with.html this O2 Platform script shows how to create a jsTree (jQuery plugin) using as a data source a serialized JSON object (this is perfect for use on a web service)
//var topPanel = panel.clear().add_Panel(); var topPanel = "Poc - jQuery jsTree".popupWindow(600,200); var ie = topPanel.add_IE(); </pre> ie.showMessage("loading jstree js"); var jQuery = new IE_JQuery(ie); var cssFile = @"jquery.jstree.style.css".local(); var styleHtml = "<style type=\"text/css\"> {0} </style>".format(cssFile.fileContents().remove("".line())); var injectCSS = "$('body').append($('" + styleHtml + "' ))"; ie.invokeEval(injectCSS); //ie.invokeEval("<a href="http://static.jstree.com/v.1.0pre/jquery.jstree.js%22.uri().getHtml">http://static.jstree.com/v.1.0pre/jquery.jstree.js".uri().getHtml</a>()); // use to get jstree from jstree.com website ie.invokeEval("jquery.jstree.js".local().fileContents()); jQuery.element("div").html("creating local tree"); ie.invokeEval("$('body').append($('<div id=\"testJsTree\"/>')); $('#testJsTree').html('test JsTree will go here');"); var jsTree = new JsTree(); jsTree.add_Node("node 1"); jsTree.add_Node("node 2") .add_Node("child 1") .add_Nodes("sub child 1", "sub child 2"); jsTree.add_Nodes("node 3", "node 4"); ie.invokeEval("treeData = " + jsTree.jsonString()); // this creates a string for the serialized jsTree object //ie.invokeEval("treeData = { 'data' : ['firstnode' ,'2ndNode', '3rdNode' ] }"); // this creates a tree it manually ie.invokeEval("jQuery('#testJsTree').jstree({ 'json_data' : treeData , 'plugins' : [ 'themes', 'json_data' , 'dnd']} );"); jQuery.element("div").html("tree created"); return "ok"; //O2File:JsTreeNode.cs //O2File:IE_JQuery.cs //O2Ref:WatiN.Core.1x.dll //O2Ref:Microsoft.mshtml.dll
Here is the code of the JsTree class (in the JsTreeNode.cs file):
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Script.Serialization; //O2Ref:System.Web.Extensions.dll namespace O2.XRules.Database.APIs { public class JsTree { public List<JsTreeNode> data; public JsTree() { data = new List<JsTreeNode>(); } } public class JsTreeNode { public Attributes attributes { get; set; } public Data data { get; set; } public string state { get; set; } public List<JsTreeNode> children { get; set; } public JsTreeNode() { children = new List<JsTreeNode>(); data = new Data(); attributes = new Attributes(); } public JsTreeNode(string title) : this() { data.title = title; } } public class Attributes { public string id { get; set; } public string rel { get; set; } public string mdata { get; set; } } public class Data { public string title { get; set; } public string icon { get; set; } } public static class JsTree_ExtensionMethods { public static string jsonString(this object _object) { return new JavaScriptSerializer().Serialize(_object); } public static JsTreeNode add_Node(this JsTree jsTree, string title) { var newJsTreeNode = new JsTreeNode(title); jsTree.data.Add(newJsTreeNode); return newJsTreeNode; } public static List<JsTreeNode> add_Nodes(this JsTree jsTree, params string[] titles) { var newJsTreeNodes = new List<JsTreeNode>(); foreach(var title in titles) newJsTreeNodes.Add(jsTree.add_Node(title)); return newJsTreeNodes; } public static JsTreeNode add_Node(this JsTreeNode jsTreeNode, string title) { var newJsTreeNode = new JsTreeNode(title); jsTreeNode.children.Add(newJsTreeNode); return newJsTreeNode; } public static List<JsTreeNode> add_Nodes(this JsTreeNode jsTreeNode, params string[] titles) { var newJsTreeNodes = new List<JsTreeNode>(); foreach(var title in titles) newJsTreeNodes.Add(jsTreeNode.add_Node(title)); return newJsTreeNodes; } } }
Based on the original code sample, here is a working version of its test script:
List<JsTreeNode> nodesList = new List<JsTreeNode>(); for (int i = 1; i < 10; i++) { JsTreeNode node = new JsTreeNode(); node.attributes = new Attributes(); node.attributes.id = "rootnod" + i; node.attributes.rel = "root" + i; node.data = new Data(); node.data.title = "Root node:" + i; node.state = "close"; node.children = new List<JsTreeNode>(); for (int j = 1; j < 4; j++) { JsTreeNode cnode = new JsTreeNode(); cnode.attributes = new Attributes(); cnode.attributes.id = "childnod1" + j; node.attributes.rel = "folder"; cnode.data = new Data(); cnode.data.title = "child node: " + j; cnode.attributes.mdata = "{draggable : true,max_children : 1,max_depth : 1 }"; node.children.Add(cnode); } node.attributes.mdata = "{draggable : false,max_children : 1, max_depth :1}"; nodesList.Add(node); } var jsTree = new JsTree(); jsTree.data= nodesList; var jsonData = new JavaScriptSerializer().Serialize(jsTree); return jsonData;
Invoking jQuery from O2
jQuery is an amazing javascript API and since O2 has a two-way communcations with the hosted IE object, there is a new O2 API that allows the managed (ie. in O2) manipulation of jQuery.
Here is an exampe of this api in action:
var topPanel = "PoC - jQuery IE Automation example".popupWindow(600,700); var ie = topPanel.add_IE().silent(true); ie.open(<a href="http://jquery.bassistance.de/autocomplete/demo/">http://jquery.bassistance.de/autocomplete/demo/</a>); var jQuery = new IE_JQuery(ie); jQuery.id("suggest1") // getting reference to html element with id 'suggest1' .border(12) // seeting the border value to 12 .value("London") // setting the 'value' attribute to 'London' .keydown(); jQuery.wait(200) // wait 200 ms to give time for the '.ac_over:first' to exist .element(".ac_over:first") // get reference to '.ac_over:first' element .parent() // invoke the jQuery 'parent' method .click(); // invoke the jQuery 'click' method jQuery.wait(1000).id("suggest1").border(2); // wait 1s before removing the border</pre> return "ok"; //O2File:IE_JQuery.cs //O2File:WatiN_IE_ExtensionMethods.cs //using O2.XRules.Database.Utils.O2 //O2Ref:WatiN.Core.1x.dll
Here is a longer version of the above script (the following code samples are not ‘standalone *.h2 scripts and will need to be executed inside the ‘Quick Development Environment’ or ‘IE Automation’ scripts (since they need the ‘panel’ variable):
panel.clear(); var ie = panel.add_IE().silent(true); ie.open("http://jquery.bassistance.de/autocomplete/demo/"); var jQuery = new IE_JQuery(ie); jQuery.id("suggest1") // getting reference to html element with id 'suggest1' .border(12) // seeting the border value to 12 .value("London") // setting the 'value' attribute to 'London' .keydown(); jQuery.wait(200) // wait 200 ms to give time for the '.ac_over:first' to exist .element(".ac_over:first") // get reference to '.ac_over:first' element .parent() // invoke the jQuery 'parent' method .click(); // invoke the jQuery 'click' method jQuery.wait(1000).id("suggest1").border(2); // wait 1s before removing the border return "ok"; // here is a more verbose version of the above script ie.wait(1000); jQuery.element("#suggest1").border(12); jQuery.id("suggest1").attr("value","Lond").keydown(); ie.wait(200); jQuery.element(".ac_over:first").parent().click(); // here is a version using much more raw jQuery manipulations and experiements ie.invokeEval("jQuery(\"#suggest1\").attr('value','mode 1')"); ie.wait(1000); jQuery.invokeJQuery("'#suggest1').attr('value','mode 2'"); ie.wait(1000); jQuery.element("#suggest1").invokeJQuery("attr","'value' , 'mode 3' "); ie.wait(1000); jQuery.element("#suggest1").attr("value","L"); ie.wait(1000); jQuery.id("suggest1").invokeJQuery("trigger", "'keydown'"); ie.wait(1000); jQuery.element(".ac_over:first").invokeJQuery("parent","").invokeJQuery("click",""); return "done"; //O2File:IE_JQuery.cs //O2File:WatiN_IE_ExtensionMethods.cs //using O2.XRules.Database.Utils.O2 //O2Ref:WatiN.Core.1x.dll
Here is the Script provided by an Daniel (an O2 user) which inspired the creation of this script:
panel.clear(); var ie = panel.add_IE().silent(true);</pre> ie.open("http://jquery.bassistance.de/autocomplete/demo/"); //ie.invokeEval(incJquery); ie.field("suggest1").value("London"); //entering value inside first autocomplete control ie.invokeEval("$('#suggest1').trigger('keydown')"); //triggering keyboard event ie.wait(100); ie.invokeEval("$('.ac_over:first').parent().click()"); //choosing first value from autocomplete return ie.fields(); //O2File:WatiN_IE_ExtensionMethods.cs //using O2.XRules.Database.Utils.O2 //O2Ref:WatiN.Core.1x.dll
this is how Daniel was injecting jQuery into the page (note that the IE_JQuery.cs api also does this)
var incJquery = "var script = document.createElement('script');"; incJquery += "script.src = 'http://code.jquery.com/jquery-latest.pack.js';"; incJquery += "script.type = 'text/javascript';"; incJquery += "document.body.insertBefore( script, document.body.firstChild );"; ie.invokeEval(incJquery);
Trigger an Keypress event in IE
- directly entering the value on the field – try something like: ie.field(“fieldName”).value(“aaa”); depending on how the original Javascript event hook is set-up that might just do the trick
- directly invoking the javascript function that handles the KeyPress – O2 has now quite extended support for Javascript, and you can easily perform javascript invokes (and receive callbacks). See this links for more details
- https://o2platform.wordpress.com/2011/03/08/running-javascript-in-ie-automation-environment/
- https://o2platform.wordpress.com/2011/03/31/o2-script-util-javascript-object-viewer/
- https://o2platform.wordpress.com/2011/03/07/o2-script-dwr-functions-viewer-and-invoker/
one note on Javascript, if you go down that route, I would advise you to first inject jQuery.js into the page and then do your manipulations (since you can write your Javascript code in jQuery which much easier) . There are already good support for jQuery in O2 which you can leverage:
- use Windows UIAutomation – at last resort you can move the mouse to the location of the field (javascript/WatiN can help to find the screen position) , then simulate a mouse click and finally enter some text (this is basically using Windows to simulate your actions). See this script for an example on how to the use White API (which is the equivalent of WatiN by from Windows): PoC – Automating Notepad.h2