Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Components

Matti Schneider edited this page Oct 7, 2015 · 3 revisions

Components reference

A Component is a JavaScript file containing a hash whose enclosing curly braces will be automatically added and containing entries that can be of two types: elements or actions.

File naming

Component files have to end with Component.js.

It is good practice to name your components after the frontend components they model: SearchBarComponent.js, TreeSelectorComponent.js

Elements

A component element maps a testing-space name to a DOM element and, as such, contains all needed information to retrieve one in a web page.

CSS selectors

The simplest way to target a DOM element is through a CSS selector. If you want to retrieve an element through a CSS selector, simply associate the element name to its selector:

// example/DuckDuckGo/SearchBarComponent.js
field:        '[name=q]',
submitButton: '#search_button_homepage'

Here, two elements have been defined: field and submitButton, both through CSS selectors. When these elements are accessed in a scenario, the selected element will be retrieved from the currently-active webpage. If they are not present, actions on them will fail (unless of course you were testing for their absence).

Alternative location methods

To use another location strategy than CSS, simply map the component element name to a single-key hash mapping the locator type to its selector.

For example, the two definitions above could as well have been written:

// example/DuckDuckGo/SearchBarComponent.js
field:        { name: 'q' },
submitButton: { id:   'search_button_homepage' }

All WebDriver locator types are supported, and are also aliased to shorter versions:

  • xpath
  • partial link text (aliased to a)
  • link text (aliased to linkText)
  • id
  • name
  • class name (aliased to class)
  • tag name (aliased to tag)
  • css selector (aliased to css)

Straight strings are actually simply expanded to { css: <value> }.

If you are faced with a complex (or poorly automatically) generated DOM, your best option is probably to use XPath.

Actions

A component action maps a testing-space name to a JavaScript function, usually combining simple interaction steps on elements to model a user behavior.

It is usually good practice to group elements at the top of a component declaration, add an empty line and then group actions.

Magic actions

Some actions are generated magically on the declared elements. All generated actions are asynchronous and return Q promises.

Typing text & sending keystrokes

All elements have a magic set<ElementName> associated action generated, which takes the keystroke sequence to send to that element as a parameter.

// SearchBarComponent.js
field:        'input[name=q]'
// in `1 - SearchScenario.js`
description: 'Search for something',
steps: [
    SearchBarComponent.setField('something')   // automatically generated
                                            // sends the 's-o-m-e-t-h-i-n-g' keystroke sequence
                                            // after having focused the `field` element
]

Setters either reject their returned promise if the element on which they are applied can't be found, or fulfill it with the element on which they are applied.

You won't use the return value unless in a custom action.

Clicking

Some element naming conventions trigger the generation of associated clicking actions:

// SearchBarComponent.js
submitButton:   '#search_button_homepage'
// in `1 - SearchScenario.js`
description: 'Search for something',
steps: [
    SearchBarComponent.submit()   // automatically generated
                               // clicks the `submitButton` element
]

An element named <elementName><typeSuffix> will automatically augment its declaring component with an <elementName> action if <typeSuffix> is one of:

  • "Link"
  • "Button"
  • "Checkbox"
  • "Option"
  • "Radio"

Such actions return a promise which will either be fulfilled, or rejected if the element could not be found or clicked.

Custom actions

Custom actions are functions you write in the component description. They usually return a promise whose fulfillment state will be used to consider the step as passed or failed. They can also be synchronous, in which case they should throw to fail as a step.

// ClockComponent.js
getCurrentHour: function() {
    return this.result.then(function(resultElement) {
        return resultElement.text();
    }).then(function(text) {
        return text.split(':')[0];  // get the hour only
    });
}

As you can see, actions can access elements in the same component as they are declared by using the this keyword.

An important thing to note is that, in this case, the element accessor returns a promise for the element, not the element straight away. You must chain it with then to a function that will be executed once the element is available, and which will be passed a reference to the wd object representing it, with all wd's API available. If the element is not found on the page, the promise will be rejected with an error describing the reason why the element could not be accessed.

Magic actions are also available for you to call on this component:

// example/DuckDuckGo/SearchBarComponent.js
field:        '[name=q]',
submitButton: '#search_button_homepage',

searchFor: function searchFor(term) {
    return this.setField(term)()
               .then(this.submit());
}

These actions also return promises.

Look out for the double call: the first component action in a chain in other component actions have to be called twice (once with their actual parameters, another without any param).

This limitation is due to the wrapping that is injected for scenarios. It is known and will be fixed in an upcoming release. You can track progress in issue #99.

Another important thing is that your function may get called several times in a row if it gets rejected, until the timeout is exhausted.

This is what allows Watai to be very resilient against varying load times, but it means you should (of course) avoid global mutable state.