Daily automation: command tabs enumeration
Having come with Office 2007, command tabs are seen in more and more applications. What are command tabs? These are just tab items, from a UIAutomation user’s point of view.
Task: open a command tab by its name. Work with elements inside it.
Requirements: provide a standard way to open a command tab and get controls placed on here.
Solution: the first example is how to open each command tab in an application. With OneNote, taken as an example because never stopping complaints against UI-based tests disturb me ;), we’ll activate all the command tabs, one by another.
[UIAutomation.Preferences]::OnSuccessDelay = 0; Get-UIAWindow -pn onenote | ` Get-UIAControlDescendants -ControlType TabItem | ` %{ # set initial state before access each tab item Get-UIATabItem -Name Home | ` Invoke-UIATabItemSelectItem -ItemName Home; [void](Get-UIAButton -Name File*Tab | ` Invoke-UIAButtonClick); # select a tab item $_ | ` Invoke-UIATabItemSelectItem -ItemName $_.Current.Name; # stop to see where we are sleep -Seconds 2; # print out the name of the current tab item Write-Host "$($_.Current.Name)`t" ` "$($_.Current.AutomationId)`t" ` "$($_.Current.ClassName)"; }
As can be easily tested, changing the word onenote to, for example, winword will print out command tabs of Word. (The script tested with OneNote 2010 and Word 2010 on a Windows 8 CP box).
Now we are going to accomplish the second part of this task: printing out all the content of every command tab.
[UIAutomation.Preferences]::OnSuccessDelay = 0; Get-UIAWindow -pn onenote | ` Get-UIAControlDescendants -ControlType TabItem | ` %{ # set initial state before access each tab item Get-UIATabItem -Name Home | ` Invoke-UIATabItemSelectItem -ItemName Home; [void](Get-UIAButton -Name File*Tab | ` Invoke-UIAButtonClick); # select a tab item $_ | ` Invoke-UIATabItemSelectItem -ItemName $_.Current.Name; # stop to see where we are sleep -Seconds 2; # print out the name of the current tab item Write-Host "$($_.Current.Name)`t" ` "$($_.Current.AutomationId)`t" ` "$($_.Current.ClassName)"; # enumerate down the hierarchy Get-UIACustom -Name ($_.Current.Name) | ` Get-UIAControlDescendants | ` %{ # print out the name of the current tab item Write-Host "$($_.Current.Name)`t" ` "$($_.Current.AutomationId)`t" ` "$($_.Current.ClassName)"; } }
Note, here we don’t get the hierarchy from under tab items. Tab items become custom controls when they are clicked. Thus, we used here Get-UIACustom fed with the name of the current command tab.
Daily automation: search for a specific control
Sometimes we don’t know the type of object we are searching for. Often, we don’t even want to know the type. For instance, we need to check the list of controls is represented on a form or wizard page. To have this task done, we need the way that led as directly to the control of our interest.
Another example is a rich form. This may be OWA (Outlook Web Access) or anything else with a grid or a list with data. If we need to get a particular control or controls, there’d be useful a search allowing us to type in a part of name.
These tasks can be easily accomplished with the Search-UIAControl cmdlet. Let’s see how to do that.
Task: demonstrate using the search cmdlet.
Requirements: perform search that consumes a part of control’s name.
Solution: the first search shown below is inside the Calculator window:
Set-StrictMode -Version Latest; ipmo [path]\UIAutomation.dll; [UIAutomation.Mode]::Profile = [UIAutomation.Modes]::Presentation; cls; # here we are searching for all the element whose names start with 'a' Start-Process calc -PassThru | Get-UIAWindow | Search-UIAControl -Name A*; # here we get only button(s) Start-Process calc -PassThru | Get-UIAWindow | Search-UIAControl -ControlType button -Name A*; # this code should return the same as the first code snippet, # because there are only two types of controls whose names start with 'a' Start-Process calc -PassThru | Get-UIAWindow | Search-UIAControl -ControlType button,menubar -Name A*;
The second code start the Control Panel window, searches for the Sound link, open the dialog and searches inside for the Speakers control. Which type does the control have? I don’t know unless I run the code:
Set-StrictMode -Version Latest; ipmo [path]\UIAutomation.dll; [UIAutomation.Mode]::Profile = [UIAutomation.Modes]::Normal; # click the Start button Get-UIADesktop | Get-UIAButton -Name Start | Invoke-UIAButtonClick; # open the Control Panel window Get-UIAListItem -Name 'Control Panel' | Invoke-UIAControlClick; # search for the Sound dialog $w = Get-UIAWindow -Name 'Control Panel'; # the name will be changed during the test Get-UIAWindow -Name 'Control Panel' | Get-UIAEdit -AutomationId 'SearchEditBox' | Set-UIAEditText -Text 'sound'; sleep -Seconds 1; # for the Normal mode only # if you are running this script with the # [UIAutomation.Mode]::Profile = [UIAutomation.Modes]::Presentation # set, you can cut out this sleep # Otherwise, there happened someting like 'the window couldn't form itself without a bit of time' # Thus, this is unavoidable timeout (at least a half of second) # open the Sound dialog #Get-UIAHyperlink -AutomationId 'name' -Name 'Sound' | Invoke-UIAHyperlinkClick; #$w | Get-UIAHyperlink -Name 'Sound' | Invoke-UIAHyperlinkClick; $w | Search-UIAControl -Name 'Sound' -Highlight | Invoke-UIAHyperlinkClick; # search for the Speakers device Get-UIAWindow -Name 'Sound' | Search-UIAControl -Name 'Speak*' -Highlight;
Daily automation: registering user’s clicks
Have you never desired to write all the actions the user of your application performed? What a pity, there’s no embedded logging in our application… Hey, no problem here! The today’s sample demonstrates how to write down all button clicks.
Task: register all the clicks that the user done during the session.
Requirements: use the module for dumping all the button click events happened after the application started.
Solution: for simplicity sake, let’s start Calculator and choose the Standard mode. Close the application and run the following code:
ipmo [path]\UIAutomation.dll; # use the Get-UIAControlDescendants cmdlet to collect all the visible buttons # use the Register-UIAInvokedEvent cmdlet to assign an event action to each button Start-Process calc -PassThru | ` Get-UIAWindow | ` Get-UIAControlDescendants -ControlType Button | ` Register-UIAInvokedEvent -EventAction {param($src) "$($src.Current.Name) clicked" >> $env:TEMP\calc.log;}
After the code is processed by PowerShell, the file will contain all key pressings.
Daily automation: the great mathematical enigma
You may have heard that beyond our little world of PowerShell and software testing boundaries many other things exist. For example, the mathematical theorems that are waiting for its solution. One of them is ‘what is the answer to the expression 2 + 2 / 2?’. I’m not a mathematician, you are perhaps too, so that what’s the reason to argue would it be 2, 3 or even 42?
Well, we are going only to test a program to confirm the readiness to such mathematical things. Meet the candidate: calc.exe!
Task: demonstrate how to test an application.
Requirements: use UIAutomation and TMX modules to report the results.
Solution: below is the code we run against the application under test (AUT):
ipmo [path]\UIAutomation.dll; ipmo [path]\TMX.dll; # Let's create a new test suite # to clean up the data that are possibly out of the current interest [TMX.TestData]::ResetData(); [UIAutomation.Mode]::Profile = [UIAutomation.Modes]::Presentation; New-TMXTestSuite -Name 'Calculator testing' -Id calc; Add-TMXTestScenario -Name 'Calculator menu' -Id calcmenu; Add-TMXTestScenario -Name 'Caclucator simple calculations' -Id maths; # start the application under test (AUT) Start-Process calc -PassThru | Get-UIAWindow; # now we've got the window in the # [UIAutomation.CurrentData]::CurrentWindow variable # set the initial state: Standard Open-TMXTestScenario -Id calcmenu; # we are going to save our test result regarding how the menu works here # the main menu here is expandable/collapsible # what can be learnt by issuing the following line of code: # (Get-UIAMenuItem -Name View).GetSupportedPatterns() Get-UIAMenuItem -Name View | ` Invoke-UIAMenuItemExpand | ` Get-UIAMenuItem -Name Standard | ` Invoke-UIAMenuItemClick -TestResultName 'The Standard menu item is available' ` -TestPassed -TestLog; # the TestLog parameters logs the last line of code # the TestPassed parameter informs that we have gotten the result we expected # the TestResultName parameter gives the name to our test result # test result Id is generated as we don't need to set it manually, this is just result function calculateExpression { param( [string]$modeName ) # produce the input Get-UIAButton -Name 2 | Invoke-UIAButtonClick; Get-UIAButton -Name Add | Invoke-UIAButtonClick -TestResultName "The Add button is clickable $($modeName)" -TestPassed; Get-UIAButton -Name 2 | Invoke-UIAButtonClick; Get-UIAButton -Name Divide | Invoke-UIAButtonClick; Get-UIAButton -Name 2 | Invoke-UIAButtonClick; Get-UIAButton -Name Equals | Invoke-UIAButtonClick -TestResultName "The Equals button is clickable $($modeName)" -TestPassed; # now we need to get the result # there is a difficulty: the name of a Text element (i.e., Label in .NET) is its value # okay, let's find all of them and our module highlight each #Get-UIAControlDescendants -ControlType Text | ` # %{Get-UIAText -AutomationId $_.Current.AutomationId; ` # sleep -Seconds 2; ` # Write-Host "this was " $_.Current.Name $_.Current.AutomationId;} # the last, with Id = 158, is ours [int]$result = (Get-UIAText -AutomationId 158).Current.Name; $result; } # we must determine where we are # as testers, we never believe, we check # you might, for example, investigate into the second level menu: #Get-UIAMenuItem -Name View | ` # Invoke-UIAMenuItemExpand | ` # Get-UIAMenu | ` # Get-UIAControlChildren | ` # %{Write-Host $_.Current.ControlType.ProgrammaticName $_.Current.Name $_.Current.AutomationId;} # the easy way is to simple calculate the buttons # MS UI Automation never returns what it can't see (by design, as it is intended for screen readers) # the Standard mode - 31 # the Statictics mode - 33 # the Programmer mode - 48 # the Scientific mode - 53 [string]$testResultName = 'The Standard mode is available'; # to ensure that after changing the mode # we are working with the actual window Get-UIAWindow -ProcessName calc; if ((Get-UIAControlDescendants -ControlType Button).Count -eq 31) { Close-TMXTestResult -Name $testResultName -TestPassed; } else { Close-TMXTestResult -Name $testResultName -TestPassed:$false; # do additional actions if needed } # now we are going to test calculations so that # we are switching to appropriate scenario Open-TMXTestScenario -Id maths; [string]$modeName = 'in the Standard mode'; calculateExpression $modeName; # in the Standard mode # test the same in the Scientific mode Open-TMXTestScenario -Id calcmenu; Get-UIAMenuItem -Name View | ` Invoke-UIAMenuItemExpand | ` Get-UIAMenuItem -Name Scientific | ` Invoke-UIAMenuItemClick -TestResultName 'The Scientific menu item is available' ` -TestPassed -TestLog; # check the mode we are in now $testResultName = 'The Scientific mode is available'; # to ensure that after changing the mode # we are working with the actual window Get-UIAWindow -ProcessName calc; if ((Get-UIAControlDescendants -ControlType Button).Count -eq 53) { Close-TMXTestResult -Name $testResultName -TestPassed; } else { Close-TMXTestResult -Name $testResultName -TestPassed:$false; # do additional actions if needed } Open-TMXTestScenario -Id maths; [string]$modeName = 'in the Scientific mode'; calculateExpression $modeName; # in the Scientific mode # test reports are still ugly!!! Export-TMXTestResults -As HTML -Path C:\1\calc_result.htm
After all, what is the answer? Okay, to choose the answer is 2 or 3 is not our mission. We only tested it, without the ambition to solve the mathematical problem…