dr-watin

Working surgery for Watin.Net. All examples were created within Visual Studio 2008 set up for C# language. My aim is to show simple complete examples of working code mainly focused around google's website. By doing this will allow you the user to apply these examples to an actual website for themselves. If you are new to WatiN I suggest you follow the posts in order.

Thursday 9 April 2009

Post Six - On Error grab a screenshot

I read sometime ago of a rival test automation product that boasted that you could configure your auto-generated script to grab a screenshot and save this image to file if an error occurred within your test. This sounded a useful utility to have so I set about writing my own methods to achieve this within watiN. The direction I've taken to manage this task is to use the clipboard to store the screenshot and then write the contents of the clipboard to an image file. In total there are 3 methods to handle this so lets look at the main method first dealing with writing the contents of the clipboard to file.

private static void SaveClipBoardImagetoFile()
{
//Line 1
if (Clipboard.GetDataObject() != null)
{
//Line 2
IDataObject data = Clipboard.GetDataObject();
//Line 3
if (data == null)new ArgumentNullException("data");
//Line 4
if (data.GetDataPresent(DataFormats.Bitmap))
{
//Line 5
using (Bitmap image = (Bitmap)data.GetData(DataFormats.Bitmap, true))
{
//Line 6
string FilePath = DateFormatFileNameUpdate("C:\\temp\\");
try
{
//Line 7
image.Save(FilePath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
catch
{
//Line 8
Directory.CreateDirectory("C:\\Temp");
//Line 9
image.Save(FilePath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
else
{
MessageBox.Show("The Data In Clipboard is not as image format");
}
}
else
{
MessageBox.Show("The Clipboard was empty");
}
}

So lets look at this method in more detail. The first thing we do 'Line 1' is to check wether the clipboard has any data stored in it, if not we simply show a messagebox to that effect. If we do have data then 'Line 2' creates the clipboard object 'data' and passes into this the clipboard data. 'Line 3' then checks that we have valid data, if not we throw an 'null' exception error. 'Line 4' checks that the data retrieved is in fact of bitmap format, if not then we display a message box to highlight this fact. 'Line 5' we create a bitmap object to hold the image data. We use a 'using' statement here as this is handy for us as it cleans up the bitmap object automatically when exiting the 'using' section. 'Line 6' calls a further method named 'DateFormatFileNameUpdate' passing in the parameter 'C\temp\' whose main job is to append to the file path a date\time stamp followed by the file name of 'ErrorImage.jpg'. This method is detailed more in the next section. Line 7 now attempts to write the image to file as part of a 'try\catch' statement, if the temp directory does not exist an error is returned, we catch this in 'Line 8' and create a directory and then write the image out to file in 'Line 9'.

Now lets look in more detail at the method 'DateFormatFileNameUpdate'.
private static string DateFormatFileNameUpdate(string filepath)
{
//Line 1
string d = DateTime.Now.ToString();
//Line 2
d.Trim();
//Line 3
string v = d.Replace("/", "_");
//Line 4
string v1 = v.Replace(":", "_");
//Line 5
string v2 = v1.Replace(" ", "_");
//Line 6
return filepath + v2 + "_ErrorImage.jpg";
}

This method takes the filepath string, 'C:\temp\' as it's parameter. Line 1 then stores the current system date and time in a string variable 'd'. Line 2 removes any possible spaces from the start or end of variable 'd'. Line 3 then replaces any occurance of the character '/' (forward slash) which is auto generated by the date\time function (which are illegal in a file path) with underscore characters, Line 4 repeats the same process for any ':' (colon character) and Line 5 replaces any spaces with underscores. Line 6 then returns the new file path string, for an example would look like this:
'C:\temp\04_10_2009_15_34_21_ErrorImage.jpg'.

You need to place the following 'using' statements at the head of the 'WorkingExamples' class i.e. just below the watiN using statements to facilitate this new code:
using System;
using System.IO;
using System.Window.Forms;
using System.Drawing;
using System.Threading;

The third method (shown below) shows us how we implement the above 2 methods, 'SaveClipBoardImagetoFile' & 'DateFormatFileNameUpdate', you'll notice that these two methods are both private methods and the 'GrabScreenOnError' is of type public because this is the method we call to implement the screen capture. The 'GrabScreenOnError' method simply loads the google page and takes a screen shot by implementing 'SendKeys' method. Although these methods are designed to work when an error is thrown you don't have to use them simply for this purpose. I show where to place the screen grab call within a try/catch block for error catching purposes.

public static void GrabScreenOnError()
{
//Line 1
IBrowser browser = BrowserFactory.Create(BrowserType.InternetExplorer);
//Line 2
browser.GoTo(Element_ID.GOOGLE_URL);
//Line 3
Thread.Sleep(500);
try
{
//Line 4
SendKeys.SendWait(Element_ID.CTRL + Element_ID.PRINT_SCREEN);
//Line 5
SaveClipBoardImagetoFile();
}
catch (Exception)
{
//Line 6
//SendKeys.SendWait(Element_ID.CTRL + Element_ID.PRINT_SCREEN);
//Thread.Sleep(500);
//SaveClipBoardImagetoFile();
}
finally
{
browser.Dispose();
}
}

So let's expain this method in more detail. Lines 1 & 2 we've used before and should now be familiar with. The first thing thats new is Line 3. Hear we use a built in .Net method called 'Sleep' which needs the using statement we put at the head of the class, 'System.Threading'. This Sleep call allows us to pause the program execution for a number of ms (milliseconds - 1000 milliseconds = 1 second), so Line 3 is pausing for half a second just to ensure we have the google page loaded before we use Line 4 to simulate the key press of 'Alt' & 'PrtSc' buttons on the keyboard. Using the 'Alt' keypress allows us grab the active dialog on screen (hopefully the error message dialog) and not all dialogs displayed, in this example we grab the whole google page. SendKeys again is a built in .Net method to simultate key presses. The 'SendWait' attribute waits until the message is processed i.e. the screen capture is actived. Line 5 calls the 'SaveClipBoardImagetoFile' method discussed earlier. The lines under 'Line 6' (all commented out) is to show where we would normally put the code relating to capturing the screen when an error is thrown.

So how do we get all this to work. Firstly all three methods 'SaveClipBoardImagetoFile', 'DateFormatFileNameUpdate', & 'GrabScreenOnError' should be placed into the 'WorkingExamples' class. Then within the 'main' method of the 'Program' class place the following call to 'WorkingExamples.GrabScreenOnError()' .

One further thing to note is that within the 'GrabScreenOnError' method we use references to the Element_ID class when using the SendKeys call, below is the code you need to place into the Elememt_ID class so we can reference the key press id's. I've supplied you with most of the special keypress codes so you can experiment with other combinations.

//KeyPress
public const string ALT = "%";
public const string CTRL = "^";
public const string SHIFT = "+";
public const string BACKSPACE = "{BACKSPACE}";
public const string BREAK = "{BREAK}";
public const string CAPS_LOCK = "{CAPSLOCK}";
public const string DELETE = "{DELETE}";
public const string DOWN_ARROW = "{DOWN}";
public const string END = "{END}";
public const string ENTER = "{ENTER}";
public const string ESC = "{ESC}";
public const string F1 = "{F1}";
public const string F10 = "{F10}";
public const string F11 = "{F11}";
public const string F12 = "{F12}";
public const string F13 = "{F13}";
public const string F14 = "{F14}";
public const string F15 = "{F15}";
public const string F16 = "{F16}";
public const string F2 = "{F2}";
public const string F3 = "{F3}";
public const string F4 = "{F4}";
public const string F5 = "{F5}";
public const string F6 = "{F6}";
public const string F7 = "{F7}";
public const string F8 = "{F8}";
public const string F9 = "{F9}";
public const string HELP = "{HELP}";
public const string HOME = "{HOME}";
public const string INSERT = "{INSERT}";
public const string LEFT_ARROW = "{LEFT}";
public const string NUM_LOCK = "{NUMLOCK}";
public const string PAGE_DOWN = "{PGDN}";
public const string PAGE_UP = "{PGUP}";
public const string PRINT_SCREEN = "{PRTSC}";
public const string RIGHT_ARROW = "{RIGHT}";
public const string SCROLL_LOCK = "{SCROLLLOCK}";
public const string TAB = "{TAB}";
public const string UP_ARROW = "{UP}";

Wednesday 11 March 2009

Post Five - Use Built in Variables

So now let's refactor the code to make it easier to read and maintain in the future. Were going to make use of our Element_ID class we prepared earlier. This will allow us to store ID's\names of elements within our code. For example the search text box weve used on Google's search page has a name of 'q' (I must admit not a very interesting or descriptive name) but none the less if we wish to use this textbox we must reference it by 'q'. Now let's say a few month's down the line we have written a hundred methods using the said textbox! And then Google in their wisdom change the name to 'x'! All our tests will break and we will have to track down all references to 'q' in all of our test to remedy the situation. But there is a better way. We store our 'q' reference in our Element_ID class so now all 100 test reference the same source for the name of 'q'. Now if Google do change the name to 'x' we only have one place to update and all our test will carry on working. So add the following 3 lines to the Element_ID class as shown below:

namespace WatinTestGround
{
class Element_ID
{
//Google Search
public const string GOOGLE_URL = "http://www.google.co.uk";
public const string SEARCH_TEXTBOX_ID = "q";
public const string SEARCH_BUTTON_ID = "btnG";
}
}


Update the 'GoogleSearch' method within the 'WorkingExamples' class to use the 'Element_ID' class as shown below:

public static void GoogleSearch()
{
//Line 1
IBrowser browser = BrowserFactory.Create(BrowserType.InternetExplorer);
//Line 2. Find the search text field and type Watin in it
browser.GoTo(Element_ID.GOOGLE_URL);
//Line 3
ITextField searchText = browser.TextField(Element_ID.SEARCH_TEXTBOX_ID);
//Line 4
searchText.Value = "Watin";
//Line 5
IButton searchButton = browser.Button(Element_ID.SEARCH_BUTTON_ID);
//Line 6
searchButton.Click();
//Line 7
browser.Dispose();
}


You'll notice line 2, 3 & 5 now reference the 'Element_ID' class to obtain the ID's\Names we used to hard code within our methods.

Friday 6 March 2009

Post Four - Google Search Example

Now at last we can start some coding. We need to add a method to our 'WorkingExamples' class. Write or copy the following into our class:

public static public void GoogleSearch()
{
//Line 1
IBrowser browser = BrowserFactory.Create(BrowserType.InternetExplorer);
//Line 2.
browser.GoTo("www.google.co.uk");
//Line 3
ITextField searchText = browser.TextField(Find.ByName("q"));
//Line 4
searchText.Value = "Watin";
//Line 5
IButton = searchButton = browser.Button(Find.ByName("btnG"));
//Line 6
searchButton.Click();
//Line 7
browser.Dispose();
}


So the whole class should now look like this:

using WatiN.Core;
using WatiN.Core.Interfaces;
namespace WatinTestGround
{
public static class WorkingExamples
{
public static void GoogleSearch()
{
//Line 1
IBrowser browser = BrowserFactory.Create(BrowserType.InternetExplorer);
//Line 2.
browser.GoTo("www.google.co.uk");
//Line 3
ITextField searchText = browser.TextField(Find.ByName("q"));
//Line 4
searchText.Value = "Watin";
//Line 5
IButton = searchButton = browser.Button(Find.ByName("btnG"));
//Line 6
searchButton.Click();
//Line 7
browser.Dipose();
}
}
}


To run this code we need to reference it from our 'Program' class, so amend the 'Main' method within this class as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WatinTestGround
{
class Program
{
[STAThread]
static void Main(string[] args)
{
WorkingExamples.GoogleSearch();
}
}
}


So let's expain a bit more of whats going on within our 'GoogleSearch' method. First thing to notice is if you wish to put comments within your code to start the line with 2 forward slashes '//'.

Next 'using statements', hear were pulling in the the 'WatiN.Core' librarys that hold all of the classes we need to run WatiN.

Line 1. Now we create our browser object. In this instance we create an IE object but we can easily create a FireFox object the same way.

Line 2. We now tell our browser object to 'GoTo' a url address ie. Google.

A brief explaination regarding lines 3 & 5. We have created our browser object (browser) but every button, textbox, link, frame etc displayed in our browser is an object in itself. So we must create objects of the same type we wish to interact with within our browser object. Remember that every object you create has a set of 'Propertys' automatically added to it. To see and access these property's just type in your objects name followed by a .(dot) and you will get a popup list of all the associated propertys.

Line 3. So we must create a 'TextField' object to enter our search query into.

So what line 3 is saying is create me an object called 'searchText' of type 'ItextField' that is in my browser object and identified by the name of 'q'.

Line 4. The 'Value' property of our 'searchText' object is 'Watin', so put this value into the textbox with the name of 'q'.

Line 5. We must create a 'Button' object to click to instigate our search for 'Watin'. So line 5 is saying create me an object called 'searchButton' of type 'IButton' that is in my browser object and identified by the name of 'btnG'.

Line 6. Activate the 'Click' property of my 'searchButton' object.

Line 7. This is our clean up code to release the 'browser' object memory and close our ie browser window. Note: To see the search work and produce data comment out line 7 otherwise the browser gets closed before you see any results.

Tuesday 24 February 2009

Post Three - Set Up

You will need Visual Studio installed, all examples are build with 2008 version but I'm sure 2003 or 2005 should work just as well. The examples use the C# language as this appears the most popular at present.

So let's start by creating our project. Start Visual Studio, from the 'File' menu select the following options: 'New/Project'. The 'New Project' dialog will display. Ensure that the 'Windows' option displayed within the left hand side 'Project Types' pane is highlighted. Now select and highlight on the 'Console Application' option displayed within the right hand 'Templates' pane. Within the 'Name' textbox displayed under both panes enter the name as: 'WatinTestGround' this automatically changes the 'Solution Name' textbox to match. Leave the 'Location' textbox to it's default unless you have your own source code structure in place. Click the 'OK' button to generate your project.

Your project will open displaying the following page:

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

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


We need add the following line before our 'Main' method within our 'Program' class.
[STAThread]
Note: Include the open\close square brackets. So your class code should
look like this:

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

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

}
}
}


For a full explanation on the reason for this refer to the following documentation:
http://watin.sourceforge.net/apartmentstateinfo.html

We now need to created two further class files within our project. To do this right click on the 'WatinTestGround' class name within the 'Solution Explorer' usually found on the right hand side of the VS interface. If this is not displayed you can display it by clicking the 'Solution Explorer' option found under the 'View' menu option. After right clicking the above option from the dialog box displayed click the 'Add/Class' option. Enter into the displayed 'Add New Item' dialog within the 'Name' textbox 'WorkingExamples.cs' and click the 'Add' button. Right click the 'WatinTestGround' class again within 'Solution Explorer' and add a further class naming it 'Element_Id.cs'.

Now we need to make sure our 'WorkingExamples' class is in the correct format. So within 'Solution Explorer' click and display the 'WorkingExample.cs' class. When it loads it should look like this:

using WatiN.Core;
using WatiN.Core.Interfaces;

namespace WatinTestGround
{
public static class WorkingExamples
{

}
}

If you need to change anything in your class to match the above do it and save your changes.

Lastly we need to reference our Watin libary (installed earlier) so our project has access to all the required watin classes. Right click the 'Reference' option found within the 'Solution Explorer' within our 'WatinTestGround' class and click on the 'Add Reference...' option. The 'Add Reference' dialog will display (there may be a short delay before it loads). Click on the 'Browse' tab and using the file dialog displayed navigate to our newly created 'Watin2.0' folder. Locate the 'Bin' folder and select and highlight the 'watiN.Core.dll' file and click the 'OK' button. Thats it we have referenced our project to the watin librarys.

Post Two - Install IE Developer Toolbar

This is a must have accessory that makes getting information from the browser object (DOM) easy. Go to the following url: 'http://www.microsoft.com/downloads/' and in the 'Search' textbox enter 'ie developer toolbar'. When the page loads follow the download and install instructions. All the following descriptions relate to IE7. After installing close your current ie browser and open a new ie browser.
Click on the '>>' arrow icon displayed next to the 'Tools' option in the ie menu bar and select the 'IE Developer Toolbar' option. The toolbar will now display in the bottom third of the browser. For full instructions on how to use the ie toolbar click the instructions link on the download website. To get quickly into action with the ie toolbar click the 'Arrow overlaying a box' icon from the ie toolbar menu.
Now whatever you hover over in the browser will highlight with a solid blue border.
Clicking an item/link will now show all its information within the ie toolbar. Try it out. When you want to return to normal actions just click the 'Arrow overlaying a box' icon again to disable it.

Post One - Install Watin

First things first, we need to get the latest copy of Watin and install it. Luckly this is a simple process, so here goes. Go to the following url: http://watin.sourceforge.net/ the home page will load. Click the 'Download' tab and then click the 'Download' link for the current release. Currently this is '2.0 CTP3' (but obviously may have changed to a later number). From the next page click on the zip file usually displayed under the full release name. The download should start and you will be presented with a dialog box asking where to save or open the file. I prefer to save the file to my 'desktop'. When downloaded double click the zip file (you will need winzip or similar installed) and unzip the files to the following location 'Program Files\Watin2.0' on your C drive (you will need to create the'Watin2.0' folder). Thats it Watin is installed.