Web UI Automation and Test using PowerShell

Lately, I had to do lots of repetitive tasks at work in order to fix defects in an application. As I grew tired of filling numerous forms with the mandatory data needed for them to validate, I thought that I could maybe use PowerShell to automate the form-filling process.

I very quickly stumbled upon an MSDN article titled Web UI Automation with Windows PowerShell. It was definitely the way to go, but I was not really satisfied with everything I found over there.

Getting a Brower object

This part is similar to the one used in the MSDN article.

$global:ie = New-Object -com "InternetExplorer.Application"
$global:ie.Navigate("about:blank")
$global:ie.visible = $true

Please note that I will use $global:ie and $global:doc all the time, as global variables. I could have used $script: scope variables, but global are better for this task. Here, we are building a bunch of reusable function; declaring these variables as $global: ensures that the variables will be available in scripts that include this utility script. See here for more information on PowerShell variable scopes.

So this, sets $global:ie to a COM Internet Explorer object, navigates to “about:blank” and makes the window visible. Now we need some helper functions to navigate, fill forms and click elements easily.

Helper Functions to Navigate, Click, etc…

First of all, we need a method that yields the script while the current page is loading. So, what it should do is simple: wait for the current page to be loaded then set the $global:doc variable to the loaded document.

Function WaitForPage([int] $delayTime = 100)
{
  $loaded = $false
 
  while ($loaded -eq $false) {
    [System.Threading.Thread]::Sleep($delayTime) 
 
    #If the browser is not busy, the page is loaded
    if (-not $global:ie.Busy)
    {
      $loaded = $true
    }
  }
 
  $global:doc = $global:ie.Document
}

I used an optional parameter here for the delay between checks of the browser’s status. It’s pretty straight forward, while the browser ($global:ie) is busy, wait. Once it’s not busy anymore, assign the document to the $global:doc variable. Let’s now define a function to navigate to a given url.

Function NavigateTo([string] $url, [int] $delayTime = 100)
{
  Write-Verbose "Navigating to $url";
 
  $global:ie.Navigate($url)
 
  WaitForPage $delayTime
}

Note that I used Write-Verbose commands around in functions to output some useful information in my script, making it easier to spot mistakes while running it.

Now, as this was made to fill web forms, let’s define a function that will fill an input text field with a given value.

Function SetElementValueByName($name, $value, [int] $position = 0) {
  if ($global:doc -eq $null) {
    Write-Error "Document is null";
    break
  }
  $elements = @($global:doc.getElementsByName($name))
  if ($elements.Count -ne 0) {
    $elements[$position].Value = $value
  }
  else {
    Write-Warning "Couldn't find any element with name ""$name""";
  }
}

This is heavily used in my scripts. An HTML form always has lots of input elements that have unique names that need to be filled. So, if you need to fill the username input text with Philippe value, just call this function:

SetElementValueByName “username” “Philippe”

Note that there is also a option parameter that is used as the element’s position in the array returned by $global:doc.getElementByName. By default, the used position is 0 because most of the forms will only have one element with a given name. However, it can be that in (badly designed?) forms, two elements have the same name. In this case, you can specify which one you want to fill.

Now, I won’t explain all the functions, but here are the ones I wrote:

Function ClickElementByTagName($tagName, [int] $position = 0)
{
  if ($global:doc -eq $null) {
    Write-Error "Document is null"
    break
  }
  $elements = @($global:doc.getElementsByTagName($tagName))
  if ($elements.Count -ne 0) {
    $elements[$position].Click()
    WaitForPage
  }
  else {
    Write-Error "Couldn't find element ""$tagName"" at position ""$position""";
    break
  }
}
 
Function ClickElementById($id)
{
  $element = $global:doc.getElementById($id)
  if ($element -ne $null) {
    $element.Click()
    WaitForPage
  }
  else {
    Write-Error "Couldn't find element with id ""$id"""
    break
  }
}
 
Function ClickElementByName($name, [int] $position = 0)
{
  if ($global:doc -eq $null) {
    Write-Error "Document is null"
    break
  }
  $elements = @($global:doc.getElementsByName($name))
  if ($elements.Count -ne 0) {
    $elements[$position].Click()
    WaitForPage
  }
  else {
    Write-Error "Couldn't find element with name ""$name"" at position ""$position"""
    break
  }
}

These are used to click on objects of the DOM in order to submit the form. These functions are not error proof, but as far as I used this stuff, I didn’t have issues.

A Little Example

Let’s write something very simple to test these functions. I will do a advanced search on google using these functions:

NavigateTo "http://www.google.com/advanced_search"
SetElementValueByName "as_oq" "Unisys Fenix PLDA"
SetElementValueByName "num" "30"
SetElementValueByName "lr" "lang_en"
ClickElementByName "btnG"

This gives you a little example of the kind of things you can do. It’s rather simple, but very powerful if you have repetitive tasks to on some web sites.

Download the full script here.

Comments

7 Responses to “Web UI Automation and Test using PowerShell”

  1. Jack Ukleja on July 15th, 2009 04:00

    Have you tried using Selenium RC? A very easy to use plug-in for Firefox that lets you record and playback and make assertions. “No code required”.

  2. Philippe on July 16th, 2009 23:32

    Nope, didn’t know about that tool. Looks very intresting. Thanks for pointing it out!

    However, what I wrote is more for a personnal use. The application I’m currently working on need a few forms of inputs before reaching the part that I’m working on, so this comes in handy.

  3. Carl on July 29th, 2009 12:59

    Great stuff, thanks very much for posting this..

  4. chan on January 21st, 2010 09:58

    hi do you have any idea, how to detect popup window using powershell?

  5. tom on March 2nd, 2010 22:19

    I also use that stuff for webtesting…
    As i didn’t find a way to automate a fileupload natively i did this with waitn in posh.(http://watin.sourceforge.net/)
    Pretty sure you ca n handle PopUps as well

  6. Davo on March 19th, 2010 06:18

    Philippe..you ROCK! This is an excellent post, I will find this very useful. Thanks very much.

  7. Tune Up Your PC » Post Topic » UAC - Accessing Internet Explorer on March 28th, 2010 03:17

    [...] Accessing Internet Explorer can be useful for obtaining Web content. The usual approach uses a COM object called InternetExplorer.Application like this: $ie = New-Object -comObject InternetExplorer.Application$ie.visible = $true$ie.navigate(‘http://www.powershell.com’)$ie$ie.Document Unfortunately, this approach fails when you use IE with enhanced security (Vista UAC for example). While you can open IE and navigate to the Web page, you will lose access to the document property once the Web site is loaded because the COM object works only for local files. Once you navigate to a Web page, IE launches again with lower privileges, and PowerShell has no way of accessing the new instance. To work around this issue, you can use yet another COM object called Shell.Application. It returns all Explorer windows, including all open IE windows. All you need do is pick the IE window you need, by specifying a keyword found in its title bar. The following example launches a Web page and then waits until a browser window opened with the word “PowerShell” in its title bar. Next, the code returns the IE object and accesses the Web page content. & “$env:programfilesInternet Exploreriexplore.exe” ‘http://powershell.com’$win = New-Object -comObject Shell.Application$try = 0$ie2 = $nulldo {  Start-Sleep -milliseconds 500  $ie2 = @($win.windows() | ? { $_.locationName -like ‘*PowerShell*’ })[0]  $try ++  if ($try -gt 20) {    Throw “Web Page cannot be opened.”  }} while ($ie2 -eq $null)$ie2.document$ie2.Document.body.innerHTML http://powershell.com/cs/blogs/tips/archive/2009/04/03/accessing-internet-explorer.aspx   http://www.pvle.be/2009/06/web-ui-automationtest-using-powershell/ [...]

Leave a Reply