Tag Archives: ControlType.Window

Daily automation: a few windows in parallel


At work, an interesting question was raised: is it possible to work with several windows in parallel if their processes are run under various users? Yes, it surely is.

Task: use UIAutomation to get windows of AUTs that are run under various users.

Requirements: demonstrate how to use the technique and how to determine which window might cause a problem.

Solution: the first example is simple and demonstrates how to handle several windows:

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll
Start-Process calc;
Start-Process calc;
Start-Process calc;
Start-Process calc;
Get-UIAWindow -ProcessName calc | Get-UIAButton -Name 1 | Invoke-UIAButtonClick | Read-UIAControlName
Get-UIAWindow -ProcessName calc | Get-UIAMenuItem -n vi* | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -n scien* | Invoke-UIAMenuItemClick

The sample starts the foursome of calculators, gets them all, clicks the 1 button and changes application mode to the Scientific one.

There is no difficulty with windows run under several user accounts while your test process (i.e., powershell) is running with at least equal or higher privileges.

Now, we’ll start calc under two different user accounts (credentials are not provided to the public domain just for you not locking your accounts ;):

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll;
(Start-Process calc -Credential (Get-Credential) -PassThru).Id
(Start-Process calc -Credential (Get-Credential) -PassThru).Id
# Here, we are writing up numbers to a paper
Get-UIAWindow -ProcessId 5924,6076 | Get-UIAMenuItem -Name vi* | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -Name scien* | Invoke-UIAMenuItemClick

This works, but the sample is not friendly to use: you need to store process ids. Okay, below is a better one-liner:

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll;
@((Start-Process calc -Credential (Get-Credential) -PassThru).Id; (Start-Process calc -Credential (Get-Credential) -PassThru).Id) | %{ Get-UIAWindow -ProcessId $_ | Get-UIAMenuItem -Name vi* | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -Name stat* | Invoke-UIAMenuItemClick; }

Finally, how to learn which window failed? For example, by defining a counting variable that will increment every window and point to an element of the windows array.

Advertisements

Daily automation: eliminating the fragility of tests. Part 3


Many of us faced the situation when a window is gone and a testing tool hits the air for long time. The testing tool tries to get a control, a next control, and so on. Every attempt takes time.

UIAutomation had the remedy for the problem for relatively long time. This feature has never been widely known, so that it’s time to tell you what it is and how it works.

Task: minimize the time that UIAutomation spends on controls of a window that failed to appear.

Requirements: demonstrate how to use the technique with default settings.

Solution: you don’t need to do anything to benefit from this technique in case if a window of your interest is gone:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow;
[UIAutomation.Preferences]::Timeout
[UIAutomation.Preferences]::AfterFailTurboTimeout
Get-UIAWindow -Name calc1 # non-existing window
[UIAutomation.Preferences]::Timeout
[UIAutomation.Preferences]::AfterFailTurboTimeout
Get-UIAWindow -Name calc* # this call get a window
[UIAutomation.Preferences]::Timeout
[UIAutomation.Preferences]::AfterFailTurboTimeout

After we ran this sample, we have the following output:
5000 – the default timeout
2000 – the AfterFailTurboTimeout
2000 – the timeout equals to AfterFailTurboTimeout. This is because calc1 has not been caught and the default timeout was considered as too long to wait for nothing
2000 – the AfterFailTurboTimeout
5000 – the default timeout was restored after the test have got a window
2000 – the AfterFailTurboTimeout
Note: this funcitonality has been broken in 0.8.0 Preview 4 (more accurately, 0.8.0P4 was released despite the fact that several unit tests have not been fixed. Just because this functionality is not widely known and never used explicitly).

Internally, UIAutomation checks that [UIAutomation.CurrentData]::CurrentWindow is $null and [UIAutomation.CurrentData]::LastResult also is $null.

Well, what about controls? Imagine the case you have a window and a child window or a control failed to appear.

This problem is not so obvious as the ‘no such window’ problem. ‘One control is gone’ is a typical case during the development cycle. Nonetheless, some controls are vital for the whole test. For this, a new parameter is here: -IsCritical. Let’s see how it works:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1
[UIAutomation.Preferences]::Timeout
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 10
[UIAutomation.Preferences]::Timeout
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 10 -IsCritical
[UIAutomation.Preferences]::Timeout
[UIAutomation.CurrentData]::LastResult
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1
[UIAutomation.Preferences]::Timeout
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 -IsCritical
[UIAutomation.Preferences]::Timeout

Below is the output:
5000 – the default timeout
5000 – the default timeout as the -IsCritical parameter has not been used
2000 – the turbo timeout (thanks to the -IsCritical parameter)
5000 – the default timeout (after the first control that has been caught successfully)
5000 – IsCritical does nothing if there wasn’t a problem

The general rule is to use the -IsCritical parameter for child windows, default buttons and controls like combo boxes and radio buttons if their state blocks you from going forward.

Daily automation: peeling the onion


Sometimes, hit happens. It’s a shame to confess, but rarely even I recommend simple Win32 clicks. Today was the story about such clicks. A member of the test team, not a newcomer to UIAutomation and with thousands of test cases in the suite behind, the guy complained that Invoke-UIAControlClick (i.e., PostMessage) did not work!

I approached, and it didn’t work! The panic was spreading out among the testers, suggestions like ‘use mouse_event!’ or ‘revise the code!’ was hovering around the room.

The answer was simple. The Invoke-UIAControlClick cmdlet as well as the PostMessage/SendMessage Win32 call hits by control’s handle. We attempted to do the following:

Get-UIAWindow -n window_name | Set-UIAFocus | Get-UIATab -n tab_name | Invoke-UIAControlClick -X 500 -Y 125;

What the world was awaiting was:

Get-UIAWindow -n window_name | Set-UIAFocus | Get-UIATab -n tab_name | Get-UIAPane | Get-UIAPane | Get-UIAPane | Invoke-UIAControlClick -X 500 -Y 125;

Each control in this pipeline had a handle and there was no point to hit the tab if the link was on the lowest pane.
Layers are of almost the same size and put one over one. Even spy++ couldn’t help as nobody knew which layers was pointed to by the spy++ target cursor in order to collect messages.

Task: help to find the right level.

Requirements: write out the script that hit-tests every layer of controls and prints their handles.

Solution: below is the script that walks down the hierarchy and prints out handles of controls:

function hittest
{
 param(
 [System.Windows.Automation.AutomationElement]$element,
 [int]$X,
 [int]$Y
 )
 if ($element | Invoke-UIAControlClick -X $X -Y $Y) {
 Write-Host "The control has handle $($element | Read-UIAControlNativeWindowHandle)";
 }
}

Get-UIAWindow -Name *window*name* | Set-UIAFocus | `
 Get-UIATab -Name *tab*name* | `
 Get-UIAControlDescendants | `
 %{hittest $_ 500 125;}

The script tries to click on each control and outputs the handle value. The Invoke-UIAControlClick cmdlet couldn’t decide was or wasn’t a click successful and returns $true if no crash were during the script execution.

Anyhow, you can use handles to get controls and click by handle in a cycle to find out the control you need.

Daily automation: how to test help files. Part 2. Better navigation


Today’s post is a next version of the recently posted HelpWalker.ps1. This version (HelpWalker2.ps1) brought to us the following improvements:

  • scroll down, in order to make nodes visible. The Invoke-UIAControlClick cmdlet uses Win32 API functions to perform clicks, and these functions are bound to the visible area to be working
  • scroll horizontally, so that the middle of some long tree node names are put geometrically in the tree area. Otherwise, it was seen as if click was send not to a node but to a form splitter or even to a right-side page.

Task: improve the script that all the clicks, especially done in the lower area of the tree, get working.

Requirements: the script should walk through any number of tree nodes, independently of how long their names are.

Solution: below is the script that walks through all nodes with no exception:

####################################################################################################
# Script name: HelpWalker2.ps1
# Description: demonstrates how to work with the Invoke-UIATreeItemScrollItem cmdlet
# Copyright: http://SoftwareTestingUsingPowerShell.com, 2012
####################################################################################################

param(
 [string]$FileName,
 [string]$AppTitle,
 [string]$HelpTitle
 )
Set-StrictMode -Version Latest
cls;

#ipmo $global:uiautomationModule;

if ($FileName.Length -eq 0 -or $AppTitle.Length -eq 0 -or $HelpTitle.Length -eq 0) {
 Write-Host "Please specify parameters as .\HelpWakler2.ps1 app_file_name application_title help_title";
 exit;
}

# set a small delay between nodes
# because the refresh of each page should take time
[UIAutomation.Preferences]::OnSuccessDelay = 100;

# create our enum object
[PSObject]$testNames = New-Object PSObject;
$testNames | `
 Add-Member -MemberType NoteProperty -Name AppTitle -Value $AppTitle -PassThru | `
 Add-Member -MemberType NoteProperty -Name HelpTitle -Value $HelpTitle -PassThru | `
 Add-Member -MemberType NoteProperty -Name AppName -Value $FileName;

# start the AUT, its help file and get the window
Start-Process $testNames.AppName -PassThru | Get-UIAWindow -Seconds 120 | `
 Get-UIAMenuItem -Name Help | Invoke-UIAMenuItemClick | `
 Get-UIAMenuItem -Name 'Help*Topic*' | INvoke-UIAMenuItemClick;
Get-UIAWindow -Name $testNames.HelpTitle -Seconds 120;

function Invoke-TreeNodeExpand
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element
 )
 [void]($element | Invoke-UIAControlClick -DoubleClick -PassThru:$false);
}

function Invoke-TreeNodeCollapse
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element
 )
 [void]($element | Invoke-UIAControlClick -DoubleClick -PassThru:$false);
}
function Invoke-TreeNodeShowRightPane
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element
 )
 [void]($element | Invoke-UIAControlClick -PassThru:$false);
}

function Invoke-TreeNodeScrollTo
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element
 )
 [void]($element | Invoke-UIATreeItemScrollItem);
}

function Invoke-TreeNodeChildrenProcess
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element,
 [string]$NodeHierarchy
 )

 # children of the node or top-level nodes
 $children = $element | Get-UIAControlChildren -ControlType TreeItem;

if ($children -ne $null -and $children -is [Object[]] -and $children.Count -gt 0) {

# we store the previous node in a variable, so that
 # we can return to it and collapse its chilren
 # after we walked through all of them
 [System.Windows.Automation.AutomationElement]$previousChild = $null;

foreach ($childNode in $children) {
 try {
 if ($previousChild -ne $null) {
 # collapse the previous node
 Invoke-TreeNodeShowRightPane $previousChild;
 Invoke-TreeNodeCollapse $previousChild;
 }
 $previousChild = $childNode;

# expand the current node
 Invoke-TreeNodeScrollTo $childNode;
 Invoke-TreeNodeExpand $childNode;
 Invoke-TreeNodeShowRightPane $childNode;

# print out the hierarchy
 [string]$fullHierarchy = "";
 if ($NodeHierarchy.Length -gt 0) {
 $fullHierarchy = $NodeHierarchy + " -> " + $childNode.Current.Name;
 } else {
 $fullHierarchy = $childNode.Current.Name;
 }
 Write-Host $fullHierarchy;

# go down the hierarchy
 Invoke-TreeNodeChildrenProcess $childNode $fullHierarchy;
 }
 catch {
 Write-Host $Error[0];
 break;
 }
 }
 }
}

# first time, the tree is given as the root node
$rootNode = Get-UIATree -Seconds 120;
Invoke-TreeNodeChildrenProcess $rootNode;

The script accepts three parameters, as required, and a typical script run looks like:

.\HelpWalker2.ps1 services.msc Services '*Microsoft*Management*'

You should administrative rights or run PowerShell as administrator, if you are trying this sample. Usually, for regular applications, end user’s rights are enough.

Here are more samples how to run the script:

.\HelpWalker2.ps1 compmgmt.msc 'Computer*Management' '*Disk*Management*'
.\HelpWalker2.ps1 dsa.msc '*Active*Directory*Users*' '*Microsoft*Management*'
.\HelpWalker2.ps1 certmgr.msc *Cert* *Micr*Manag*

Daily automation: how to test help files. Part 1. Walkthrough


Testing of help files is a rather monotony task. What should be tested in help files? Usually, the list is

  • ability to run the help file itself
  • how the help file reacts on pressing F1 everywhere in the application UI
  • help pages hierarchy and navigation
  • help page are working, some txt are represented
  • hyperlinks work, inside the help as well as outside
  • the content of the pages (we can check only help topics and keywords, the rest, i.e. articles’ body, should be read by humans)

Today we’ll learn how to walk all the nodes in the help file.

Task: create a script that is a universal tool for navigating inside help files.

Requirements: the script should accept parameters as follows: file name of the application under test (AUT), the title of the AUT, the title of the help file.

Solution: below is the script that walks through all nodes except the cases the list is tool long to be shown in the tree (the API functions that are used can’t work in the invisible area):

param(
 [string]$FileName,
 [string]$AppTitle,
 [string]$HelpTitle
 )
Set-StrictMode -Version Latest
cls;

ipmo [path]\UIAutomation\UIAutomation.dll;
# set a small delay between nodes
# because the refresh of each page should take time
[UIAutomation.Preferences]::OnSuccessDelay = 100;

# create our enum object
[PSObject]$testNames = New-Object PSObject;
$testNames | `
 Add-Member -MemberType NoteProperty -Name AppTitle -Value $AppTitle -PassThru | `
 Add-Member -MemberType NoteProperty -Name HelpTitle -Value $HelpTitle -PassThru | `
 Add-Member -MemberType NoteProperty -Name AppName -Value $FileName;

# start the AUT, its help file and get the window
Start-Process $testNames.AppName -PassThru | Get-UIAWindow -Seconds 120 | `
 Get-UIAMenuItem -Name Help | Invoke-UIAMenuItemClick | `
 Get-UIAMenuItem -Name 'Help*Topic*' | INvoke-UIAMenuItemClick;
Get-UIAWindow -Name $testNames.HelpTitle -Seconds 120;

function Invoke-TreeNodeExpand
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element
 )
 [void]($element | Invoke-UIAControlClick -DoubleClick -PassThru:$false);
}

function Invoke-TreeNodeCollapse
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element
 )
 [void]($element | Invoke-UIAControlClick -DoubleClick -PassThru:$false);
}
function Invoke-TreeNodeShowRightPane
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element
 )
 [void]($element | Invoke-UIAControlClick -PassThru:$false);
}

function Invoke-TreeNodeChildrenProcess
{
 param(
 [ValidateNotNull()]
 [System.Windows.Automation.AutomationElement]$element,
 [string]$NodeHierarchy
 )

 # children of the node or top-level nodes
 $children = $element | Get-UIAControlChildren -ControlType TreeItem;

if ($children -ne $null -and $children -is [Object[]] -and $children.Count -gt 0) {

# we store the previous node in a variable, so that
 # we can return to it and collapse its chilren
 # after we walked through all of them
 [System.Windows.Automation.AutomationElement]$previousChild = $null;

foreach ($childNode in $children) {
 try {
 if ($previousChild -ne $null) {
 # collapse the previous node
 Invoke-TreeNodeShowRightPane $previousChild;
 Invoke-TreeNodeCollapse $previousChild;
 }
 $previousChild = $childNode;

# expand the current node
 Invoke-TreeNodeExpand $childNode;
 Invoke-TreeNodeShowRightPane $childNode;

# print out the hierarchy
 [string]$fullHierarchy = "";
 if ($NodeHierarchy.Length -gt 0) {
 $fullHierarchy = $NodeHierarchy + " -> " + $childNode.Current.Name;
 } else {
 $fullHierarchy = $childNode.Current.Name;
 }
 Write-Host $fullHierarchy;

# go down the hierarchy
 Invoke-TreeNodeChildrenProcess $childNode $fullHierarchy;
 }
 catch {
 Write-Host $Error[0];
 break;
 }
 }
 }
}

# first time, the tree is given as the root node
$rootNode = Get-UIATree -Seconds 120;
Invoke-TreeNodeChildrenProcess $rootNode;

The script accepts three parameters, as required, and a typical script run looks like:

.\HelpWalker.ps1 services.msc Services '*Microsoft*Management*'

You should administrative rights or run PowerShell as administrator, if you are trying this sample. Usually, for regular applications, end user’s rights are enough.

Here are more samples how to run the script:

.\HelpWalker.ps1 compmgmt.msc 'Computer*Management' '*Disk*Management*'
.\HelpWalker.ps1 dsa.msc '*Active*Directory*Users*' '*Microsoft*Management*'

Daily automation: eliminating the fragility of tests. Part 2


The logically next step, after we started searching for a control using a wildcard, is to get a window by part of its name.

Task: use UIAutomation to get a window by a fragment of its name.

Requirements: demonstrate how to use the technique.

Solution: code that is needed is as simple as it can be:

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll;
Start-Process calc; Get-UIAWindow -Name *alc*r

After we started the calc process, all the lines of code below will work:

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll;
Start-Process calc;
Get-UIAWindow -Name *alc*r
Get-UIAWindow -Name Calc*
Get-UIAWindow -Name CA?culator
Get-UIAWindow -pn calc
Get-UIAWindow -pid (Start-Process calc -PassThru).Id
Start-Process calc -PassThru | Get-UIAWindow

There should be placed a note: whereas the asterisk ‘*’ is a replacement for anything including an empty place, the question mark ‘?’ requires that something non-empty was under the sign. I.e., the following replacements are right:
Calc*
*Calculator*
C*l*l*
However, Cal?culator is not right. Ca?culator or Cal?ulator both are right.

%d bloggers like this: