Wednesday, May 6, 2020

WinForms DataGridView with Appium/WinAppDriver (Python)

I recently spent some time trying to get the DataGridView of Winforms working (well enough) with Appium. The exposed properties (visible via inspect.exe) are quite minimal. Luckily, there are a couple basics exposed directly as attributes (nothing as nice as selected index though) and with some raw Appium commands/queries, we can expose nicely wrapped methods to use in our automation scripts:

Please view/clone the GitHub repo - you can build the DataGrid sample project easily with .Net Core (you need python, appium modules, Dot Net Core - see python\README.md for details):

cd winforms\datagrid
dotnet restore
dotnet build

cd python
python datagrid_sample.py

A Reusable Python DataGridView Helper

The appium_datagridview.py wraps a Winform's DataGridView. It does this by taking a python object that wraps object locating in constructor. It then attempts to acquire the WebElement. Once found, it verifies it is expected control type (Table). The class then exposes a few helper methods:
  • row_count()
  • col_count()
  • send_key_repeated()
  • selected_row()
  • visible_rows()
  • move_to_row()
The DataGridView may be one of the more complex Winform objects, so this class may not work for all layouts/settings. However, should be a decent base to extend to handle most/all settings of the Grid control. The first few methods listed above are pretty simple and use the built in attributes/methods and do not add much.

However, with selected_row(), we start to get a little tricky in getting the selected row (again, there is no actual property for this). We send a SHIFT key to the grid control - this will focus the grid without changing the selected row). This action will end up with the cell/row becoming the active web element (if it wasn't already). We could be in a cell/row/etc based on your grid. If the focused item is not what appears to be a row, the code makes use of the active elements RuntimeId and some XPath to find the parent row element.

The visible_rows() method finds all Rows using XPath, and loops through them to decide if they appear visible or not. It bases this on have a valid y value. Of screen rows have negative values. Worth noting, that a row may be half on and half off (so height of element can change from scroll).

To select a row via Appium, I created move_to_row(). Initially, I planned to use mouse scrolling and scrollbar. Though, sending key presses turned out to be much simpler, and the DataGridView auto scrolls to the current row once you start sending key presses (Home, End, Right, Left, Up, Down, etc). So, much easier to send X Up/Down keys in one go to get both selection and ensure the row is visible. A neat little trick in python is to multiple a string by a value to repeat it X times (also, it is faster with Appium to send one string of all Key Chars then to send one string multiple times). Hence, this method uses the selected_row() method to find current row - decides if we need to go up or down, and sends the keys.

Getting this working took quite a bit of experimenting. And, I'm sure there are flaws with different Grid settings. Hoping this might be useful to others as a starting point. Leave a comment if you find this useful - or fork the repo and extend it as you desire. Would love to hear success stories. And, building up a python appium framework for Winforms Automation testing would benefit quite a few people.


No comments:

Post a Comment

WinForms DataGridView with Appium/WinAppDriver (Python)

I recently spent some time trying to get the DataGridView of Winforms working (well enough) with Appium. The exposed properties (visible via...