Query Calabash Android

Hey guys, I will post today some queries to find elements in different ways with calabash-android in console !

 

Basic usage

Using the class of the elements (Button, Label, View, Webview, Tableview, etc…). If you don’t know what is the class, you can use only * to find all the elements on the screen.

 

Results:

 

You can use size, to know the qty of the elements with this class:

 

 

Filtering by class

 

Simple class name

Filtering by class means that we only want to look at certain types of views. This is what we’ve done before when we were searching for Button or TextView:

This is called filtering by simple name. The result shows you that the full class name is android.widget.Button: this is what we call fully qualified class name and the andorid.widgetpart is called package name.

When you query with simple name Calabash only looks at the last bit of the name. This is also case insensitive, so Button, button or bUtTOn is the same in this context.

 

Fully qualified class name

You can also use the fully qualified class name in the query:

Interestingly this gives us two results: the login button and a checkbox. Why is the CheckBox included? In order to understand this you have to understand subclasses.

Let’s look at an example:

There are a lot different buttons in Android like ToggleButton and the Switch. A ToogleButton is just a specific type of Button with some extra functionality and look. A Button is just a simple button with a text in it where the ToggleButton has two states (on and off). So every ToggleButton is a Button but not every Button is a ToggleButton.

The CheckBox is also a subclass of Button – which explains why we get two results. If you use the fully qualified name in the query it will give you the class and all of it’s subclasses. So if there were ToggleButtons and Switches it would also return those.

The main distinction is this:

  • using simple name only looks at the last part of the class and does a simple comparison
  • using the qualified name looks at the class hierarchy and gives back all the class and all of it’s subclasses

 

Filtering by property

Usually there are several buttons, textviews and edit texts on the screen so filtering by class is not enough to identify a single element.

In the query results you get back a set of properties for each view like id, text, class.

Calabash allows you to filter on these properties.

The most used property is the id. On Android every view can have an id, which is an invisible string identifier. The id is not necessarily unique and also not every view has one. Even with these restriction using id is the most reliable way to find views.

 

id property

Let’s try to find the login button by id:

The first part of the query is the class filtering: * means give me all the views. The second part is the property filtering: id:'login_button' means find the view that has the id login_button.

The filtering happens on the result of the first part of the query string. Previously we used "*" so the filtering was applied to all the views. But we could have used different class.

 

text property

Of course id is not the only property that you can filter on. Another commonly used one is the text. This is the label of the view which is visible on the screen:

This will find the view on the screen which has a text Login. Once I found them though I use id instead of text. This results in more stable tests. Labels tend to change more frequently than ids. It also eliminates all the translation and localisation problems.

What if a view doesn’t have an id? Ask the developers to add one. It’s a 5 second task and it will make your life a lot easier.

 

Other properties

Let’s try to find all the disabled buttons:

Notice that while we used single quotes for string parameters we’re not using anything here for false.

The third type of parameter is the integer parameter (numbers). One example is the width parameter:

This gives you all the views that are exactly 200 pixel wide. Notice that we are not using the single quotes to define the value.

You can also combine the filters using multiple criteria. Let’s get all the views which are enabled and the are 50 pixel heigh:

 

How it works

The general syntax for property filtering is the following:

The prop is the name of the property and the value is either a string, a boolean or an integer value.

We’ve already used a couple of properties like id, text, enabled and width. There are a lot more. For starters you can use all the attributes that you see in the query results. But that’s not everything.

Every view object supports a set of methods – you can find all the supported methods on the view’s documentation. For example the View class has an isEnabled() method. When Calabash tries to resolve the property it find the following methods on the view: prop(), getProp() and isProp() in this order.

So for the enabled property it tries to find the enabled(), getEnabled() or isEnabled() methods.

If Calabash does find a method it calls it and compares the return value with the value you specified. If it doesn’t match it gets rid of that view. It also discards the view if it can’t find the appropriate methods.

 

Useful things

In addition to the basic query syntax there are a couple of useful things you can use.

 

marked

The marked keyword is a shorthand for identifying an element by it’s name. It’s a convenience method to filter by id, text or contentDescription properties.

 

index

The result of each query is a standard Ruby array. Because of this you can access the elements directly in it. If you want to get the first view on screen you can do it like this:

The query language also supports the index keyword. The index is very similar than using the array indexing – it gives you the element at a given index:

This is usually used with lists and other collection views. Often you just want to click on the first element on a list.

Be warned though: you should only use index if you really need it. Using index to identify elements in the layout is usually not a good idea – it leads to brittle tests and hard to diagnose problems.

Use ids, text and other identifiers and don’t rely on the ordering of the items!

 

Returning certain properties

Sometimes you’re only interested in certain properties not the full result. Let’s say you want to get all the id’s on the screen:

As you can see a result is an array of strings where only the ids are shown. Isn’t this much nicer?

You can put any property as the second argument of query to get the data you’re interested in. Another example to get all the labels for the enabled views:

query("* enabled:true", :text)

 

Predicate

Calabash supports some filters that are not present in UIScript. Particularly we support filtering by simple NSPredicates. For example searching for a string prefix:

"label {text BEGINSWITH 'Cell 1'}"

which would return the labels with text Cell 1 and Cell 10.

In general you use a NSPredicate by writing a filter: {selector OP val}, where selector is the name of an Objective-C selector to perform on the object, OP is operation, and val is a string or integer value.

Common operations

  • BEGINSWITH, prefix, e.g., "label {text BEGINSWITH 'Cell 1'}"
  • ENDSWITH, suffix, e.g., "label {text ENDSWITH '10'}"
  • LIKE, wildcard searches, e.g., "label {text LIKE 'C*ll'}"
  • CONTAINS, substring, e.g., "label {text CONTAINS 'ell'}"
  • Comparison, <, >, …
  • Case or diacritic insensitive lookups, e.g., "label {text CONTAINS[cd] 'cell'}"

To understand what additional options are available, consult the full syntax for NSPredicate documented by Apple: NSPredicate Syntax

 

Direction

There are four directions descendant, child, parent and sibling. These determines the direction in which search proceeds.

Often, query expressions are a sequence of ClassName expressions. For example:

 "tableViewCell label"

this means “first find all UITableViewCell views, then inside of those, find all the UILabel views”. The key here is the word inside. This is determined by the query direction.

By default the direction is descendant, which intuitively means “search amongst all subviews (or sub-views of sub-views, etc).” But you can change the traversal direction. Here is an advanced example:

label marked:'Tears in Heaven' parent tableViewCell descendant 
tableViewCellReorderControl

This query finds a label ‘Tears in Heaven’, and then proceeds to find the tableViewCell that contains this label (i.e., moving in the parent instead of descendant direction). From the tableViewCell we move down and find the tableViewCellReorderControl.

Valid directions are descendant, parent, child and sibling. Both descendant and childlooks for subviews inside a view. The difference is that descendant keep searching down in the sub-view’s subviews, whereas child only looks down one level. The direction sibling searches for views that are “at the same level” as the present view (this is the same as: first find the immediate parent, then find all subviews except for the view itself).

 

Thank you again !

Bye 🙂

Fonts:

https://github.com/calabash/calabash-ios/wiki/05-Query-syntax

http://krazyrobot.com/2014/04/calabash-using-query/

One thought on “Query Calabash Android

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.