Tag Archives: ExpandCollapsePattern

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: crawling here and there


Sometimes, queries are not the best way to navigate along the Automation tree. Why? Get-UIA[ControlType] cmdlets return only one control. The Search-UIAControl cmdlet returns all controls that match, but it needs previously to run around all the Automation tree that may be too long in certain applications.

What if your only need is to get a few controls situated at the same level of the Automation tree? My choice is to get one and navigate to the next sibling. Theoretically, the control that a Get- cmdlet returned is the first sibling, i.e. navigating to the previous sibling is not demanded at the same extent as to the next sibling. Though, no one guarantees that the control you’ve been given is the first child of its parent.

Thus, several cmdlets are here to help you turning left or right when necessary.

Task: demonstrate how to navigate along the Automation tree.

Requirements: figure out which cmdlets are helpful regarding navigation.

Solution: the first example shows the mistake almost everyone makes. “It should be here, under this, why your cmdlets don’t work?” I hear similar questions regularly. Don’t be confused with your results: the control of your interest might be not here. Where? Anywhere? No, just one or more levels below the hierarchy. Let’s run the following code (all the pieces of code are based on the assumption that the UIAutomation module is loaded, of course):

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControlChildren | %{ Write-Host $_.Current.ControlType.ProgrammaticName $_.Current.Name $_.Current.AutomationId; }

My Calculator returns three controls. Haven’t you been expecting that the Get-UIAControlChildren cmdlet would return all the buttons numbered from 0 till 9? Nay, they are level or levels below. Now, we’ll get all the descendants:

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControlDescendants | %{ Write-Host $_.Current.ControlType.ProgrammaticName $_.Current.Name $_.Current.AutomationId; }

This code returns tens and tens of controls. I even calculated to announce that this time it returned 73 controls:

(Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControlDescendants).Count

The Search-UIAControl returns 75 objects (possibly, objects with empty name and automation id are being calculated in various ways), but somewhat slower: the cmdlet walks the Automation tree what requires much more process-to-process calls.

(Start-Process calc -PassThru | Get-UIAWindow | Search-UIAControl -Name *).Count

So where are the buttons? Just refine the search at first:

(Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControlDescendants -ControlType Button).Count

Calculator in the Scientific mode returned 53 buttons, both buttons with numbers and signs and control buttons like ‘Minimize the window’.
Okay, let’s repeat the question: where our buttons are? Which control might be their parent? The main window? A pane under the main window?

(Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAcontrolParent).Current.ControlType.ProgrammaticName

Right, a pane. The full list of ancestors for this button is provided by the Get-UIAControlAncestors cmdlet:

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAControlAncestors | %{ Write-Host $_.Current.ControlType.ProgrammaticName $_.Current.Name $_.Current.AutomationId; }

The main window contains a pane, it in turn contains another pane, which button are placed on.
It’s time to navigate horizontally:

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAControlNextSibling

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAControlNextSibling| Get-UIAControlNextSibling

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAControlNextSibling| Get-UIAControlPreviousSibling

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAControlPreviousSibling

The first line of code returns the button 0, the second – MR, the third – (any doubts?) 1, the fourth – 4.

Eventually, we are going to know which of buttons is the first. To achieve this goal, we’ll get a button, get its parent, and get the first child:

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAControlParent | Get-UIAControlFirstChild

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 | Get-UIAControlParent | Get-UIAControlLastChild

The first button is MC, the last (don’t you think that the Get-UIAControlLastChild is the less usable cmdlet if even me avoiding to use it?) – ‘ten in the X degree’.

After the navigating crash course has been conpleted, you are able to sail to wherever your controls are!

Daily automation: collecting access keys and accelerators keys


Working with an application under test using the keyboard is not a thing that the majority of us loves. What to speak, Microsoft guys would rebel against such tests. Although I’m also not a big fan of keyboard imitation, the post brought me to an idea to collect keyboard short cuts into a list.

For example, you need to have the full list of short cuts for further consideration, test planning, etc. Maybe, you are worrying about how to test localization to many languages and such a dump would be valuable.

Task: use UIAutomation to collect all the available on the current screen of the AUT access keys and accelerator keys.

Requirements: to form a tab-separated list of controls and their keys.

Solution: below is an example that can collect two levels of the menu items (the main menu and one level deeper). It works not bad, but in the last test, mspaint, it gets in the endless loop.

The whole script kill the WordPress engine (only the sourcecode area:)), so that I divided it into parts. The whole script will in the samples within the next version, 0.7.3, today later or tomorrow.

Set-StrictMode -Version Latest;
cls
ipmo [path]\UIAutomation.dll;
function Out-ElementDetail

{
 param(
 [System.Windows.Automation.AutomationElement]$element,
 [int]$level,
 [string]$parentAccessKey
 )

[string]$accessKey = $element.Current.AccessKey;
 if ($accessKey.Length -eq 0) {
 $accessKey = "None";
 } else {
 if ($parentAccessKey.Length -gt 0) {
 $accessKey = `
 $parentAccessKey + "=>" + $accessKey;
 }
 }
 Write-Host "$($level)`t" `
 "$($element.Current.ControlType.ProgrammaticName)`t" `
 "$($element.Current.Name)`t" `
 "$($element.Current.AutomationId)`t" `
 "$($element.Current.AcceleratorKey)`t" `
 "$($accessKey)";
 try {
 $global:dict.Add($accessKey, $element.Current.Name);
 }
 catch {
 Write-Host "Error: $($element.Current.Name)`t$($accessKey)";
 }
}
function Enum-Children
{
 param(
 [System.Windows.Automation.AutomationElement]$windowElement,
 [int]$level
 )

try {
 $apppid = $windowElement.Current.ProcessId;
 $elements = $null;
 if ($level -eq 0) {
 Out-ElementDetail $windowElement 0;
 try {
 $null = $windowElement | Invoke-UIAMenuItemClick;
 $elements = `
 Get-UIAWindow -ProcessId $apppid | `
 Get-UIAControlDescendants -ControlType MenuItem,SplitButton,Custom,ListItem;
 }
 catch {
 $elements = $windowElement | Invoke-UIAMenuItemExpand | Get-UIAControlDescendants -ControlType MenuItem,SplitButton,Custom; #,ListItem;
 }
 }
 if ($elements -ne $null -and $elements.Count -gt 0) {
 foreach ($element in $elements) {
 Out-ElementDetail $element ($level + 1) ($windowElement.Current.AccessKey);
 [void]($element | Move-UIACursor -X 5 -Y 5);
 try {
 Enum-Children $element ($level + 1);
 }
 catch {}
 }
 }
 if ($level -eq 0) {
 try{
 $null = $windowElement | Invoke-UIAMenuItemClick;
 }
 catch {
 $null = $windowElement | Invoke-UIAMenuItemCollapse;
 }
 }
 }
 catch {}
}
function Report-AccessKeys
{
 param(
 [string]$startName,
 [string]$stopName
 )

 if ($stopName.Length -eq 0) {
 $stopName = $startName;
 }

 [UIAutomation.Preferences]::OnSuccessDelay = 300;
 Write-Host "Access Key report for $($startName)";
 Write-Host "Level`tControlType`tName`tAutomationId`tAcceleratorKey`tAccessKey";
 [System.Collections.Generic.Dictionary``2[System.String,System.String]]$global:dict = `
 New-Object "System.Collections.Generic.Dictionary``2[System.String,System.String]";
 $topLevelMenuItems = Start-Process $startName -PassThru | `
 Get-UIAWindow | `
 Get-UIAControlDescendants -ControlType ToolBar,MenuBar | `
 Get-UIAControlDescendants -ControlType MenuItem,SplitButton,Custom,ListItem;

 foreach ($element in $topLevelMenuItems) {
 Enum-Children $element 0;
 }

 Stop-Process -Name $stopName;
}

Report-AccessKeys services.msc mmc;
Report-AccessKeys compmgmt.msc mmc;
Report-AccessKeys devmgmt.msc mmc;
Report-AccessKeys calc;
Report-AccessKeys notepad;
Report-AccessKeys mspaint;

The sample script crawls around controls and writes all the collected to the screen and in a dictionary (one dictionary per a function call).

Homework: fix the script. Script never finishes up mspaint with control types selected to work with (less types selected allow the script to finish, but the output is too poor). Also, it needs support of deeper levels of the menu (have you seen vSphere Client 5.0?).

(MS) UIAutomation: all about a combo box


Task: automate combo box selection.

Task #2: provide a homework helping to learn the control.

Requirements: describe both getting the selection and selecting list items.

Solution: below is the preparation (services.msc is already running, select a service, open the context menu, click Properties):

Get-UIAWindow -Name Services | `
Get-UIADataGrid | `
Get-UIADataItem -Name Alerter | `
Invoke-UIAControlContextMenu | `
Get-UIAMenuItem -Name Properties | `
Invoke-UIAMenuItemClick;

If you are sitting against a Windows 7 box, select for our test another service, for example, BranchCache.

Now the combo box can be seen. It has three or four values (I’m again doing the testing on a Windows XP box as I care about working on older and the newest operating systems): Automatic, Manual, and Disabled. There might or mightn’t be two ‘Automatic…’ items.

How to get what is selected now? Do so:

$((Get-UIAWindow -p mmc | Get-UIAComboBox | Get-UIAComboBoxSelection)[0]).Current

Now, is there a way to set a selection? Who roared ‘SetFocus; SendKeys’? Shut up, it’s not a sport but a torture. If a control is healthy and developers’ hands are sewn directly to shoulders, we can and we must use patterns.

Get-UIAWindow -p mmc | `
Get-UIAComboBox | `
Invoke-UIAComboBoxExpand | `
Get-UIAList | `
Get-UIAListItem -Name Automatic | `
Invoke-UIAListItemSelectItem -ItemName Automatic | `
Invoke-UIAListItemClick;

Too long? Let’s shortenize:

Get-UIAWindow -p mmc | `
Get-UIAComboBox | `
Invoke-UIAComboBoxExpand | `
Get-UIAListItem -Name Automatic | `
Invoke-UIAListItemSelectItem -ItemName Automatic | `
Invoke-UIAListItemClick;

We managed to delete only the Get-UIAList cmdlet. Why?
1) All the Get- cmdlets can perform search from the point they’ve been given with. Given a window, a childwindow or a control, from any of that start, a Get- cmdlet (excluding Get-UIAWindow as it does not have the -InputObject parameter) can continue the search down the Automation tree.
2) All the Invoke- (and pattern-cmdlets Get- and Set- like Get-UIAComboBoxSelection. Recognize them in a way: Get/Set-UIA[ControlType][Pattern name or abbreviation]) cmdlets can’t perform any search. They should be fed with a pipeline from a Get- cmdlet or a cmdlet that passed thru (-PassThru’ed) the object of the AutomationElement type.

To shortenize even more, you may cut out the first call, Get-UIAWindow (as it’s always stored in the [UIAutomation.CurrentData]::CurrentWindow variable), but be very attentively doing so: if you/your test Get-s a window (the main or an active child), closes it and tries to work with subsequent calls of Get-UIAWindow, error definitely will appear.

Homework: practisize working with the combo box, try another values, get the selection (I suppose that you know what is the difference between the selected list item and the selected list item that is braced inside the dotted box), save selection to a file, change selection, read it back from the file you’ve created, set the saved selection to the combo box, and do it until a new post got published. 🙂

(MS) UIAutomation: exporting from a grid


Task: export data from the data grid to a text file. As an example, use the Registry editor.

Task #2: provide a field for learning the module.

Requirements: just do it. Navigate through the Registry, find the values and export them to a file.

Solution: run the following code to get to the node:

Get-UIAWindow -Name 'Registry Editor' | `
	Get-UIATreeItem -Name HKEY_LOCAL_MACHINE | `
	Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name SOFTWARE | `
	Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name Microsoft | `
	Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name Windows | `
	Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name CurrentVersion | `
	Set-UIAFocus;

After the node is active, we’ll export the values:

	Get-UIADataGrid | `
		ConvertFrom-UIADataGrid | `
		Out-File "$($Env:TEMP)\export.txt";

All the values are already in the file.

ConvertFrom- cmdlets provide standard capabilities as do standard Convert cmdlets from Microsoft. For instance, you can pipeline to the Select-String cmdlet if you need check that a particular value(s) is in the grid.

To find out what was exported to the file, run the following:

Get-Content -Path "$($Env:TEMP)\export.txt" | `
	ConvertFrom-Csv;

Homework: try to export datagrid data from another key in the Registry editor or from the Services snap-in.

%d bloggers like this: