/GSAutomation

A Javascript extension/wrapper on iOS UIAutomation that makes test scripts more robust and easier to write.

Primary LanguageJavaScriptMIT LicenseMIT

Summary

GSAutomation is an extension to UIAutomation and aims to make test automation easier to use and maintain, and also keep the test scripts more robust.

You can define test tasks in simple javascript arrays, and get them executed with some handy helper methods.

Work Flow

  1. Import GSAutomation/lib/gsautomation.js
  2. Define some task arrays
  3. In the main test method, call performTask

Example

#import "GSAutomation/lib/gsautomation.js"
var task1 = [
    [Tap, "Button1"],
    [Check, "Text1", "Text2"],
];

function main() {
    performTask(task1);
}

main();

Task Array Definition

A task array is an array of steps. Each step is an array, with action name as the first element and followed by action parameters.

For example

[Tap, "Button1"]

means tap the button named "Button1".

[Check, "Text1", "Text2"]

means check if "Text1" and "Text2" both exist. If not, throw an error and fail the test.

There will be a 1 second delay between each two actions, and for each action a maximum of 10 second delay is allow for the first element to appear. (e.g. after tapping "Button1", up to 10 second is allowed for "Text1" to appear.)

Some actions allow a rescue step, e.g. we can insert an action/step as the last parameter of Tap step:

[Tap, "Button1", [Tap, "Button2"]]

This means that if we failed to tap "Button1", we can try tapping "Button2" first and then try tapping "Button1" again. The rescue step is always going to be an optional parameter at the end of the action/step array.

Sometimes you'll find it hard to determine how the views are organized, e.g. if the button is inside a popover, you should do [Tap, "((Popover))::Button1"]. So in order to investigate the current view hierarchy, use a step [Investigate].

Supported Actions

ActionMeaningParametersAllow rescue?
TapTapping an elementthe element (2nd item)Yes
CheckCheck existence of some elementsa list of elements (2nd, 3rd.. items)No
CheckButtonEnabledCheck if the button is enabledThe button element, then followed by true or false. (true means that we want this button to be enabled; false means that we want this button to be disabled.)No
InvestigateInvestigate the current layout (children elements of the window or an element)optionally an element to start investigating. If there's no param then the investigation is done for the main windowNo
InputType some text. Use after a text field/area is selected.Text to typeYes
ScrollSwipe slowly from the screen center or within an element.Direction.Up, Direction.Down, Direction.Left, or Direction.Right. Optionally add a next parameter to specify the element to scroll at.No
SwipeSwipe quickly from the screen center or within an element.Direction.Up, Direction.Down, Direction.Left, or Direction.Right. Optionally add a next parameter to specify the element to scroll at.No
TapPointTap on a point.A point. e.g. {x:100, y:100}No
PickSelect a value in a picker wheelthe picker wheel element as the 2nd item, and the value to select as the 3rd itemYes
TryTapSimilar to Tap, but allows failure. (e.g. if the button is not tappable, it won't fail the test.)the button as the 2nd itemNo
WaitWait for some additional time. This action is normally unnecessary since a 1-second wait is applied automatically before every step. Use this only to add addition waiting time.Time to wait (seconds).No
WaitForWait for some additional time until an element appears. This action is normally unnecessary since a maximum of 10-second wait is applied automatically in order to access an element. Use this only to add addition waiting time.2nd item is the element to wait for, and the 3rd item is the maximum time to wait (seconds).No
BackgroundBackground the app for a while and return to the app.2nd item is time to background (seconds).No
FeatureStarting a feature. The steps after this step and before the next feature call or the end this task array will be grouped into this feature.the name of the featureNo
NoteLog a message/note.any textNo

Referencing to UI elements

Top level elements can be defined directly through their names, e.g. title for a button, label, or file name for an image. If it belongs to the top level view, (The hierarchy is different and simpler than the hierarchy in the code, only standard containers like table view, tab bar, navigation bar, etc create levels; if a simple view A owns view B, B is still accessible from top level.) it can be accessed through the format "parent::child", e.g. "table::cell".

To specify the type of the element, we can use the format "((type))name". Type names should be based on UIAutomation API (e.g. UIAButton, UIATableView), but for simplicity we can skip "UIA", and just call it Button. So we can define elements like "((Button))Log In".

To investigate the current hierarchy and available elements, insert a step [Investigate].

For example, if you see this from [Investigate]:

[null] [object UIAWindow] @ (0, 0) w=768, h=1024
[null] [object UIAPopover] @ (492, 471) w=266, h=434
- [The Title] [object UIAStaticText] @ (500, 497) w=250, h=44
- [Empty list] [object UIATableView] @ (500, 541) w=250, h=297
- - [null] [object UIATableCell] @ (500, 551) w=250, h=51
- - - [Cell One] [object UIAStaticText] @ (500, 551) w=250, h=51
- - [null] [object UIATableCell] @ (500, 602) w=250, h=50
- - - [Cell Two] [object UIAStaticText] @ (500, 602) w=250, h=50

You can access "Cell Two" by:

"((Popover))::((TableView))::((TableCell))::Cell Two"

When there are multiple items matching the same type and name, we can do something like "((Element))[[1]]" to specify an index. The index is 0 based. For example:

[null] [object UIAWindow] @ (0, 0) w=768, h=1024
[null] [object UIAScrollView] @ (0, 0) w=400, h=400
- [Hello] [object UIAStaticText] @ (0, 0) w=250, h=44
- [World] [object UIAStaticText] @ (0, 100) w=250, h=44
[null] [object UIAScrollView] @ (0, 0) w=400, h=400
- [Hi] [object UIAStaticText] @ (0, 0) w=250, h=44
- [Hi] [object UIAStaticText] @ (0, 100) w=250, h=44

You can access the second "Hi" label by:

"((ScrollView))::Hi[[1]]"

You don't need to include the index for the parent object (otherwise it's going to be painful to manage since it's hard to predict the order of multiple scrollviews in the window). The index is only for the deepest item in the chain.

Additional Helper Methods

There are a number of helper methods provided by GSAutomation to make UIAutomation tests a lot easier. E.g. to check whether an element is on screen, you can do

if (findChild(win(), "Something")) {
    // ...
}

To use some device-dependent logic, simply do something like:

var item = isPad() ? "iPad text" : "iPhone text";

To print some debug log, simply do

log("some text");

Please refer to GSAutomation/lib/gsautomation.js to see all the supported helper methods.

License

Copyright (C) 2013 by Hulu, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.