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”
Leave a Reply

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”.
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.
Great stuff, thanks very much for posting this..
hi do you have any idea, how to detect popup window using powershell?
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
Philippe..you ROCK! This is an excellent post, I will find this very useful. Thanks very much.
[...] 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/ [...]