B2G/QA/Automation/Style Guide/Howtos
How-Tos
Using WebIDE to Define Locators
WebIDE can connect to firefox OS devices, and lets the user to view the locators of Firefox OS app. For basics of WebIDE, please refere to this page.
Prerequisites:
- From the phone, go to Settings -> Developer and select "ADB and DevTools" under "Debugging via USB"
- Connect the phone to PC via USB.
- From WebIDE, click 'Select Runtime' and select the phone under "USB Devices".
- From the device, allow USB connection.
- From the upper right corner, select the app that you wish to view. If no apps are shown in the list, go to Runtime -> Runtime Info drop-down menu, and click "Request Higher Privileges" button. The phone will restart, and you should be able to see all apps loaded on the phone.
Finding locators in Apps:
When an app is selected from WebIDE, it will automatically open on the device as well. Select the pick button on WebIDE, (left of inspector tab), and when the pick button is highlighted, tap the element that you wish to locate. Corresponding line would be highlighted.
- For example, if you see <progress id="crop-view" class="skin-dark" ..., you can set your locator as: _locator = (By.ID, "crop-view").
- If you're looking for a sub-attribute, for example, data-l10n-id="back-button" then you can set your locator as _locator = (By.CSS_SELECTOR, '[data-l10n-id="back-button"]')
The WebIDE shows the css source in real time, so you should be able to see the changes in value after UI interactions.
Finding locators on System level:
Some of the dialogs or windows actually belongs to the System level, even when they are instantiated from pressing a button in an app. If tapping the UI element does not highlight the element, try switching the app to 'System' and tap it again. Remember, if you're doing this, you also have to switch to the system frame by calling self.marionette.switch_to_frame(), and switch back to the app frame once you return to the app window.
Finding locators in Browser:
There is a difference between Browser and Browser Tabs. When you select Browser on WebIDE, you're checking the main view of the Browser app only. In order to check the actual content of an URL, you have to select the corresponding tab. Pick the tab listed under the 'Tab' heading at the bottom of the app list in WebIDE.
Caution:
There are cases where you see it on WebIDE, but Marionette can't find them. In such case, it's usually caused by one of the following reasons:
- Marionette is on the wrong app frame
- Marionette is on the wrong shadow DOM context
- (In case of Browser) Marionette is on the wrong Search window context
CSS Selectors
Use following references as guides.
Handling shadow DOM <njpark>
What is shadow DOM?
Basically, shadow DOM enables to encapsulate a DOM subtree in a document, without exposing its inner working to the main DOM tree. This makes marionette unable to see the elements under the shadow DOM, even if you can see it on WebIDE. For a concise explanation of shadow DOM, refer to this page.
Python Marionette Methods
Python Marionette handles shadow DOM issues by changing the frame with the command switch_to_shadow_root(). What this command does is to change the context into the shadow DOM, so you can view the elements within. Once you're done with shadow DOM elements, you have to return to the standard app frame by either switch_to_shadow_root() call without parameter, or self.apps.switch_to_displayed_app()
Note that a shadow DOMs can be nested, in which case you have to call switch_to_shadow_root() multiple times. Refer to this comment for such case.
It's not always straightforward to see which ones are shadow DOM elements. Sure way to know it is when marionette cannot find the element that is being shown on WebIDE, then one of the parent element is a shadow DOM element. Also, when you call switch_to_shadow_root() method on a non-shadow DOM element, it will throw an error.
Example
Music helper methods contain a number of examples of shadow DOM usage.
def tap_cover_in_player_view(self):
# here, we switch to the active iframe view, and then switch into the shadow DOM within
# because self._rating_view_locator is within the shadow DOM
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
self.marionette.switch_to_shadow_root(self.marionette.find_element(*self._cover_image_shadow_dom_locator))
# wait until the overlay disappears, and once the check is done, get out of the shadow root
# but you'll be still inside the self._active_view_locator frame
Wait(self.marionette).until(expected.element_not_displayed(*self._rating_view_locator))
self.marionette.switch_to_shadow_root()
# after tapping the element, re-enter into the shadow DOM, and check for the self._rating_view_locator being displayed
# self.apps.switch_to_displayed_app() exits both shadow DOM and the active iframe view
self.marionette.find_element(*self._cover_image_shadow_dom_locator).tap()
self.marionette.switch_to_shadow_root(self.marionette.find_element(*self._cover_image_shadow_dom_locator))
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(*self._rating_view_locator))))
self.apps.switch_to_displayed_app()
Switching frames and system frame
TBD - mwargers
Handling browser instances
TBD - mwargers
Returning the page object after completing an action <njpark>
When invoking a certain UI action causes the phone to open a new page, it is recommended to return the appropriate page object.
On a related note, each page should have its own class definition, where it lists methods that manipulates and checks the associated UI elements.
music_app.wait_for_music_tiles_displayed()
# tapping albums tab returns the list_view object,
# because it opens the new view
list_view = music_app.tap_albums_tab()
# tapping the first album returns the sublist_view object
sublist_view = albums[0].tap_first_album()
# tapping the song opens the player view
player_view = sublist_view.tap_first_song()
# select stop
player_view.tap_play()