Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,5 @@
recursive-include gaiatest/atoms *.js
recursive-include gaiatest/resources *.*
exclude MANIFEST.in
include requirements.txt
exclude gaiatest/tests

View file

@ -0,0 +1,5 @@
{
"env": {
"DESKTOP_SHIMS": "0"
}
}

View file

@ -0,0 +1 @@
atoms

View file

@ -0,0 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from gaia_test import *
from mixins import *

View file

@ -0,0 +1,3 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View file

@ -0,0 +1,138 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from marionette_driver.errors import NoSuchElementException
from gaiatest import GaiaApps
from gaiatest import Accessibility
class Base(object):
DEFAULT_APP_HOSTNAME = '.gaiamobile.org'
DEFAULT_PROTOCOL = 'app://'
def __init__(self, marionette):
self.marionette = marionette
self.apps = GaiaApps(self.marionette)
self.accessibility = Accessibility(self.marionette)
self.frame = None
self.entry_point = hasattr(self, 'entry_point') and self.entry_point or None
def launch(self, launch_timeout=None):
self.app = self.apps.launch(self.name, self.manifest_url, self.entry_point, launch_timeout=launch_timeout)
def is_element_present(self, by, locator):
self.marionette.set_search_timeout(0)
try:
self.marionette.find_element(by, locator)
return True
except NoSuchElementException:
return False
finally:
self.marionette.set_search_timeout(self.marionette.timeout or 10000)
def is_element_displayed(self, by, locator):
self.marionette.set_search_timeout(0)
try:
return self.marionette.find_element(by, locator).is_displayed()
except NoSuchElementException:
return False
finally:
self.marionette.set_search_timeout(self.marionette.timeout or 10000)
def find_select_item(self, match_string):
_list_item_locator = (By.CSS_SELECTOR, 'section.value-selector-container li label')
# have to go back to top level to get the B2G select box wrapper
self.marionette.switch_to_frame()
# TODO we should find something suitable to wait for, but this goes too
# fast against desktop builds causing intermittent failures
time.sleep(0.2)
Wait(self.marionette).until(expected.element_present(*_list_item_locator))
for item in self.marionette.find_elements(*_list_item_locator):
if match_string == item.text.encode('ascii','ignore').strip():
# We need to keep this because the Ok button may hang over the element and stop
# Marionette from scrolling the element entirely into view
self.marionette.execute_script('arguments[0].scrollIntoView(false);', [item])
return item
def wait_for_select_closed(self, by, locator):
Wait(self.marionette).until(expected.element_not_displayed(by, locator))
# now back to app
self.apps.switch_to_displayed_app()
# TODO we should find something suitable to wait for, but this goes too
# fast against desktop builds causing intermittent failures
# This sleep is necessary to make sure the select is completely faded out,
# see bug 1148154
time.sleep(1)
def select(self, match_string, tap_close=True):
# cheeky Select wrapper until Marionette has its own
# due to the way B2G wraps the app's select box we match on text
_close_button_locator = (By.CSS_SELECTOR, 'button.value-option-confirm')
li = self.find_select_item(match_string)
li.tap()
# Tap close and wait for it to hide
if tap_close:
self.marionette.find_element(*_close_button_locator).tap()
self.wait_for_select_closed(*_close_button_locator)
def a11y_select(self, match_string):
# Accessibility specific select method
_close_button_locator = (By.CSS_SELECTOR, 'button.value-option-confirm')
li = self.find_select_item(match_string)
self.accessibility.click(li)
# A11y click close and wait for it to hide
self.accessibility.click(self.marionette.find_element(*_close_button_locator))
self.wait_for_select_closed(*_close_button_locator)
def tap_element_from_system_app(self, element=None, add_statusbar_height=False, x=None, y=None):
# Workaround for bug 1109213, where tapping on the button inside the app itself
# makes Marionette spew out NoSuchWindowException errors, see bug 1164078
cx = element.rect['x']
cy = element.rect['y']
cx += element.rect['width']//2 if x is None else x
cy += element.rect['height']//2 if y is None else y
from gaiatest.apps.system.app import System
system = System(self.marionette)
if add_statusbar_height:
cy = cy + system.status_bar.height
system.tap(cx, cy)
@property
def keyboard(self):
from gaiatest.apps.keyboard.app import Keyboard
return Keyboard(self.marionette)
@property
def manifest_url(self):
return '{}{}{}/manifest.webapp'.format(self.DEFAULT_PROTOCOL, self.__class__.__name__.lower(), self.DEFAULT_APP_HOSTNAME)
@property
def is_displayed(self):
return (self.apps.displayed_app.manifest_url == self.manifest_url and
self.apps.displayed_app.entry_point == self.entry_point)
def wait_to_be_displayed(self):
Wait(self.marionette).until(lambda m: self.is_displayed == True)
def wait_to_not_be_displayed(self):
Wait(self.marionette).until(lambda m: self.is_displayed == False)
class PageRegion(Base):
def __init__(self, marionette, element):
self.root_element = element
Base.__init__(self, marionette)

View file

@ -0,0 +1,54 @@
from gaiatest.apps.base import Base
from marionette_driver import By, Wait, expected
class BugzillaLite(Base):
name = 'Bugzilla Lite'
manifest_url = "https://www.bzlite.com/manifest.webapp"
_given_username = (By.CSS_SELECTOR, "#login input[type='email']")
_given_password = (By.CSS_SELECTOR, "#login input[type='password']")
_button_login = (By.CSS_SELECTOR, "#login input[type='submit']")
_profile_icon = (By.CSS_SELECTOR, "a[href='/profile/']")
_button_logout = (By.CSS_SELECTOR, "a[href='/logout/']")
_dashboard_navigator_locator = (By.ID, 'navigation')
_login_form_locator = (By.ID, 'login')
_popup_intro = (By.ID, 'intro')
_button_popup_intro = (By.ID, 'intro-submit')
def login (self, username, password):
username_element = self.marionette.find_element(*self._given_username)
username_element.tap()
username_element.send_keys(username)
password_element = self.marionette.find_element(*self._given_password)
password_element.tap()
password_element.send_keys(password)
button_element = self.marionette.find_element(*self._button_login)
button_element.tap()
def logout(self):
profile_element = self.marionette.find_element(*self._profile_icon)
profile_element.tap()
logout_element = self.marionette.find_element(*self._button_logout)
logout_element.tap()
@property
def is_logged_in(self):
self.wait_for_dashboard_navigator_to_be_displayed()
return True
def wait_for_dashboard_navigator_to_be_displayed(self):
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(*self._dashboard_navigator_locator))))
@property
def is_logged_out(self):
self.wait_for_login_form_to_be_displayed()
return True
def wait_for_login_form_to_be_displayed(self):
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(*self._login_form_locator))))
def dismiss_tooltip(self):
confirm_element = Wait(self.marionette).until(expected.element_present(*self._button_popup_intro))
Wait(self.marionette).until(expected.element_displayed(confirm_element))
confirm_element.tap()

View file

@ -0,0 +1,306 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from marionette_driver.errors import (NoSuchElementException,
StaleElementException)
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
from gaiatest.apps.calendar.regions.event_details import EventDetails
class Calendar(Base):
name = 'Calendar'
_current_month_year_locator = (By.ID, 'current-month-year')
_current_month_day_agenda_locator = (By.ID, 'month-day-agenda')
_current_monthly_calendar_locator = (By.ID, 'month-view')
_add_event_button_locator = (By.XPATH, "//a[@href='/event/add/']")
_month_display_button_locator = (By.XPATH, "//a[@href='/month/']")
_week_display_button_locator = (By.XPATH, "//a[@href='/week/']")
_day_display_button_locator = (By.XPATH, "//a[@href='/day/']")
_day_view_locator = (By.ID, 'day-view')
_week_view_locator = (By.ID, 'week-view')
_time_views_locator = (By.ID, 'time-views')
_modify_event_view_locator = (By.ID, 'modify-event-view')
_event_view_locator = (By.ID, 'event-view')
_time_header_locator = (By.ID, 'time-header')
_create_account_button_locator = (By.CLASS_NAME, 'create-account')
_create_account_header_locator = (By.ID, 'create-account-header')
_create_account_view_locator = (By.ID, 'create-account-view')
_create_account_preset_locator = (By.CSS_SELECTOR,
'#create-account-presets a')
_modify_account_header_locator = (By.ID, 'modify-account-header')
_modify_account_view_locator = (By.ID, 'modify-account-view')
_settings_locator = (By.ID, 'settings')
_advanced_settings_view_locator = (By.ID, 'advanced-settings-view')
_advanced_settings_done_button_locator = (By.XPATH, "//a[@href='/settings/']")
_event_list_date_locator = (By.ID, 'event-list-date')
_event_list_empty_locator = (By.ID, 'empty-message')
_event_locator = (By.CLASS_NAME, 'event')
_today_locator = (By.CSS_SELECTOR, '.month.active .present')
_tomorrow_locator = (By.CSS_SELECTOR, '.month.active .present + li > .day')
_day_view_all_day_button = (By.CSS_SELECTOR,
'.day-view .md__allday:nth-child(2) .md__allday-events')
_day_view_event_link = (By.CSS_SELECTOR, '.day-view .md__day:nth-child(2) .md__event')
def launch(self):
Base.launch(self)
# empty message is only displayed after first MonthsDay#render,
# so we are sure app is "ready" after that
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._event_list_empty_locator))))
@property
def current_month_year(self):
return self.marionette.find_element(*self._current_month_year_locator).text
@property
def current_month_day(self):
return self.marionette.find_element(*self._current_month_day_agenda_locator).get_attribute('data-date')
@property
def event_list_date(self):
return self.marionette.find_element(*self._event_list_date_locator).text
@property
def events(self):
return [self.Event(marionette=self.marionette, element=event)
for event in self.marionette.find_elements(*self._event_locator)]
def wait_for_events(self, number_to_wait_for=1):
Wait(self.marionette).until(lambda m: len(m.find_elements(*self._event_locator)) == number_to_wait_for)
def event(self, title):
for event in self.events:
if event.title == title:
return event
@property
def accounts(self):
return [account for account in self.marionette.find_elements(
*self._create_account_preset_locator)]
def account(self, preset):
for account in self.accounts:
if account.get_attribute('data-provider') == preset:
return account
@property
def settings(self):
return self.Settings(marionette=self.marionette,
element=self.marionette.find_elements(*self._settings_locator))
def wait_for_new_event(self):
from gaiatest.apps.calendar.regions.event import NewEvent
new_event = NewEvent(self.marionette)
new_event.wait_for_panel_to_load()
return new_event
def a11y_click_add_event_button(self):
self.accessibility.click(self.marionette.find_element(*self._add_event_button_locator))
return self.wait_for_new_event()
def tap_add_event_button(self):
self.marionette.find_element(*self._add_event_button_locator).tap()
return self.wait_for_new_event()
def a11y_click_week_display_button(self):
self.accessibility.click(self.marionette.find_element(*self._week_display_button_locator))
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._week_view_locator))))
def tap_week_display_button(self):
self.marionette.find_element(*self._week_display_button_locator).tap()
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._week_view_locator))))
def a11y_click_month_display_button(self):
self.accessibility.click(self.marionette.find_element(*self._month_display_button_locator))
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._current_monthly_calendar_locator))))
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._current_month_day_agenda_locator))))
def a11y_click_day_display_button(self):
self.accessibility.click(self.marionette.find_element(*self._day_display_button_locator))
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._day_view_locator))))
def tap_day_display_button(self):
self.marionette.find_element(*self._day_display_button_locator).tap()
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._day_view_locator))))
def displayed_events_in_month_view(self):
return self.marionette.find_element(*self._get_events_locator_in_month_view()).text
def displayed_events_in_week_view(self, data_date):
return self.marionette.find_element(*self._get_events_locator_in_week_view(data_date)).text
def displayed_events_in_day_view(self, data_date):
return self.marionette.find_element(*self._get_events_locator_in_day_view(data_date)).text
def _get_events_locator_in_day_view(self, data_date):
return (By.CSS_SELECTOR,
"#day-view .md__day[data-date*='%s'] .md__event" % data_date)
def _get_events_locator_in_month_view(self):
return (By.CSS_SELECTOR,
'#event-list .event')
def _get_events_locator_in_week_view(self, data_date):
return (By.CSS_SELECTOR,
"#week-view .md__day[data-date*='%s'] .md__event" % data_date)
def a11y_click_other_day(self, next, previous):
try:
# Try clicking tomorrow
self.accessibility.click(self.marionette.find_element(*self._tomorrow_locator))
return next
except NoSuchElementException:
# Tomorrow is next week or month, try clicking yesterday.
yesterday = self.marionette.execute_script(
"return arguments[0].previousSibling.querySelector('.day');",
[self.marionette.find_element(*self._today_locator)])
self.accessibility.click(yesterday)
return previous
def a11y_click_header(self, header, selector):
self.accessibility.execute_async_script(
"Accessibility.click(arguments[0].shadowRoot.querySelector('%s'));" % selector,
[header])
def wait_fot_settings_drawer_animation(self):
el = self.marionette.find_element(*self.settings._settings_drawer_locator)
Wait(self.marionette).until(lambda m: el.get_attribute('data-animstate') == 'done')
def a11y_click_create_account_back(self):
self.a11y_click_header(self.marionette.find_element(*self._create_account_header_locator),
'button.action-button')
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._advanced_settings_view_locator))))
def a11y_click_modify_account_back(self):
self.a11y_click_header(self.marionette.find_element(*self._modify_account_header_locator),
'button.action-button')
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._create_account_view_locator))))
def a11y_click_settings(self):
self.a11y_click_header(self.marionette.find_element(*self._time_header_locator),
'button.action-button')
self.wait_fot_settings_drawer_animation()
def a11y_click_close_settings(self):
self.a11y_click_header(self.marionette.find_element(
*self.settings._settings_header_locator), 'button.action-button')
self.wait_fot_settings_drawer_animation()
def a11y_create_event(self, title):
new_event = self.a11y_click_add_event_button()
# create a new event
new_event.a11y_fill_event_title(title)
new_event.a11y_click_save_event()
self.wait_for_events(1)
def a11y_click_day_view_event(self):
self.accessibility.click(self.marionette.find_element(*self._day_view_event_link))
el = self.marionette.find_element(*self._event_view_locator)
Wait(self.marionette).until(expected.element_displayed(el))
return EventDetails(self.marionette)
def flick_to_next_month(self):
self._flick_to_month('next')
def flick_to_previous_month(self):
self._flick_to_month('previous')
def a11y_wheel_to_next_month(self):
self._a11y_wheel_to_month('left')
def a11y_wheel_to_previous_month(self):
self._a11y_wheel_to_month('right')
def _a11y_wheel_to_month(self, direction):
self.accessibility.wheel(self.marionette.find_element(
*self._current_monthly_calendar_locator), direction)
def _flick_to_month(self, direction):
"""Flick current monthly calendar to next or previous month.
@param direction: flick to next month if direction='next', else flick to previous month
"""
action = Actions(self.marionette)
month = self.marionette.find_element(
*self._current_monthly_calendar_locator)
month_year = self.current_month_year
x_start = (month.size['width'] / 100) * (direction == 'next' and 90 or 10)
x_end = (month.size['width'] / 100) * (direction == 'next' and 10 or 90)
y_start = month.size['height'] / 4
y_end = month.size['height'] / 4
action.flick(month, x_start, y_start, x_end, y_end, 200).perform()
Wait(self.marionette).until(lambda m: self.current_month_year != month_year)
class Settings(PageRegion):
_calendar_local_locator = (By.CSS_SELECTOR, '#calendar-local-first .pack-checkbox')
_calendar_local_checkbox_locator = (By.CSS_SELECTOR,
'#calendar-local-first input[type="checkbox"]')
_advanced_settings_button_locator = (By.CSS_SELECTOR, '.settings')
_settings_header_locator = (By.ID, 'settings-header')
_settings_drawer_locator = (By.CLASS_NAME, 'settings-drawer')
def wait_for_calendar_unchecked(self, timeout=None):
checkbox = self.marionette.find_element(*self._calendar_local_checkbox_locator)
Wait(self.marionette, timeout).until(expected.element_not_selected(checkbox))
def wait_for_a11y_calendar_unchecked(self, timeout=None):
Wait(self.marionette, timeout, ignored_exceptions=StaleElementException).until(
lambda m: not self.marionette.find_element(
*self._calendar_local_locator).get_attribute('aria-selected'))
class Event(PageRegion):
_title_locator = (By.TAG_NAME, 'h5')
_location_locator = (By.CLASS_NAME, 'location')
_event_view_locator = (By.ID, 'event-view')
@property
def title(self):
return self.root_element.find_element(*self._title_locator).text
@property
def location(self):
return self.root_element.find_element(*self._location_locator).text
def a11y_click(self):
self.accessibility.click(self.root_element)
el = self.marionette.find_element(*self._event_view_locator)
Wait(self.marionette).until(expected.element_displayed(el))
return EventDetails(self.marionette)

View file

@ -0,0 +1,68 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from datetime import datetime
from marionette_driver import expected, By, Wait
from gaiatest.apps.calendar.app import Calendar
class NewEvent(Calendar):
_modify_event_view_locator = (By.ID, 'modify-event-view')
_modify_event_header_locator = (By.ID, 'modify-event-header')
_event_title_input_locator = (By.XPATH, "//input[@data-l10n-id='event-title']")
_event_location_input_locator = (By.XPATH, "//input[@data-l10n-id='event-location']")
_event_start_time_value_locator = (By.ID, "start-time-locale")
_event_start_date_value_locator = (By.ID, "start-date-locale")
_edit_event_button_locator = (By.CSS_SELECTOR, 'button.edit')
_save_event_button_locator = (By.CSS_SELECTOR, 'button.save')
def wait_for_panel_to_load(self):
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._event_title_input_locator))))
def a11y_fill_event_title(self, title):
self.accessibility.click(self.marionette.find_element(*self._event_title_input_locator))
self.keyboard.send(title)
self.keyboard.dismiss()
def fill_event_title(self, title):
self.marionette.find_element(*self._event_title_input_locator).tap()
self.keyboard.send(title)
self.keyboard.dismiss()
def a11y_fill_event_location(self, location):
self.accessibility.click(self.marionette.find_element(*self._event_location_input_locator))
self.keyboard.send(location)
self.keyboard.dismiss()
def fill_event_location(self, location):
self.marionette.find_element(*self._event_location_input_locator).tap()
self.keyboard.send(location)
self.keyboard.dismiss()
def a11y_click_close_button(self):
self.accessibility.execute_async_script(
"Accessibility.click(arguments[0].shadowRoot.querySelector('button.action-button'));",
[self.marionette.find_element(*self._modify_event_header_locator)])
def a11y_click_save_event(self):
event_start_time = self.marionette.find_element(*self._event_start_time_value_locator).text
event_start_date = self.marionette.find_element(*self._event_start_date_value_locator).text
el = self.marionette.find_element(*self._modify_event_view_locator)
self.accessibility.click(self.marionette.find_element(*self._save_event_button_locator))
Wait(self.marionette).until(expected.element_not_displayed(el))
return datetime.strptime(event_start_time + event_start_date, '%I:%M %p%m/%d/%Y')
def tap_save_event(self):
date_str = self.marionette.find_element(*self._event_start_date_value_locator).get_attribute('data-date')
el = self.marionette.find_element(*self._modify_event_view_locator)
self.marionette.find_element(*self._save_event_button_locator).tap()
Wait(self.marionette).until(expected.element_not_displayed(el))
date_list = date_str.split()
return date_list[1] + ' ' + date_list[2]

View file

@ -0,0 +1,25 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import By
from gaiatest.apps.base import Base
class EventDetails(Base):
_event_header_locator = (By.ID, 'show-event-header')
_title_locator = (By.CSS_SELECTOR, '.title .content')
_location_locator = (By.CSS_SELECTOR, '.location .content')
def __init__(self, marionette):
Base.__init__(self, marionette)
@property
def title(self):
return self.marionette.find_element(*self._title_locator).text
@property
def location(self):
return self.marionette.find_element(*self._location_locator).text

View file

@ -0,0 +1,240 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
class Camera(Base):
name = 'Camera'
_secure_camera_frame_locator = (By.CSS_SELECTOR, ".secureAppWindow.active[data-manifest-name='Camera'] iframe")
# Controls View
_controls_locator = (By.CSS_SELECTOR, '.controls')
_switch_button_locator = (By.CSS_SELECTOR, '.test-switch')
_camera_switch_locator = (By.CSS_SELECTOR, ".mode-switch_bg-icon[data-icon='camera']")
_video_switch_locator = (By.CSS_SELECTOR, ".mode-switch_bg-icon[data-icon='video']")
_capture_button_locator = (By.CSS_SELECTOR, '.test-capture')
_gallery_button_locator = (By.CSS_SELECTOR, '.test-gallery')
_thumbnail_button_locator = (By.CSS_SELECTOR, 'img.test-thumbnail')
_video_timer_locator = (By.CSS_SELECTOR, '.recording-timer')
# HUD View
_hud_locator = (By.CSS_SELECTOR, '.hud')
_loading_screen_locator = (By.CSS_SELECTOR, '.loading-screen')
_toggle_flash_button_locator = (By.CSS_SELECTOR, '.test-flash-button')
# ConfirmDialog
_select_button_locator = (By.CSS_SELECTOR, '.test-confirm-select')
def launch(self):
Base.launch(self)
self.wait_for_capture_ready()
Wait(self.marionette).until(
expected.element_not_displayed(*self._loading_screen_locator))
@property
def camera_mode(self):
return self.marionette.find_element(*self._controls_locator).get_attribute('data-mode')
def take_photo(self):
self.wait_for_capture_ready()
self.tap_capture()
# Wait for thumbnail to appear
self.wait_for_thumbnail_visible()
Wait(self.marionette).until(
expected.element_not_displayed(*self._loading_screen_locator))
def record_video(self, duration):
# Start recording
self.tap_capture()
# Wait for Controls View state to indicate that recording is in-progress
self.wait_for_video_capturing()
# Wait for duration
timeout = duration + 10
timer_text = "00:%02d" % duration
timer = self.marionette.find_element(*self._video_timer_locator)
Wait(self.marionette, timeout).until(lambda m: timer.text >= timer_text)
# Stop recording
self.tap_capture()
# Wait for thumbnail to appear
self.wait_for_thumbnail_visible()
def tap_capture(self):
capture_button = self.marionette.find_element(*self._capture_button_locator)
Wait(self.marionette).until(expected.element_enabled(capture_button))
capture_button.tap()
def tap_select_button(self):
select = self.marionette.find_element(*self._select_button_locator)
Wait(self.marionette).until(expected.element_enabled(select))
self.tap_element_from_system_app(select)
# Fall back to app beneath the picker
self.wait_to_not_be_displayed()
self.apps.switch_to_displayed_app()
def tap_switch_source(self):
switch = self.marionette.find_element(*self._switch_button_locator)
Wait(self.marionette).until(expected.element_displayed(switch))
camera = switch.find_element(*self._camera_switch_locator)
video = switch.find_element(*self._video_switch_locator)
current_camera_mode = self.camera_mode
# TODO: Use marionette.tap(_switch_button_locator) to switch camera mode
Actions(self.marionette).press(camera).move(video).release().perform()
controls = self.marionette.find_element(*self._controls_locator)
Wait(self.marionette).until(lambda m: controls.get_attribute('data-enabled') == 'true')
Wait(self.marionette).until(lambda m: not current_camera_mode == self.camera_mode)
self.wait_for_capture_ready()
def tap_toggle_flash_button(self):
initial_flash_mode = self.current_flash_mode
toggle = self.marionette.find_element(*self._toggle_flash_button_locator)
Wait(self.marionette).until(expected.element_enabled(toggle))
toggle.tap()
Wait(self.marionette).until(lambda m: self.current_flash_mode != initial_flash_mode)
def wait_for_capture_ready(self):
controls = self.marionette.find_element(*self._controls_locator)
Wait(self.marionette).until(lambda m: controls.get_attribute('data-enabled') == 'true')
Wait(self.marionette).until(lambda m: controls.is_enabled())
def wait_for_video_capturing(self):
controls = self.marionette.find_element(*self._controls_locator)
Wait(self.marionette).until(lambda m: controls.get_attribute('data-recording') == 'true')
def switch_to_secure_camera_frame(self):
# Find and switch to secure camera app frame (AppWindowManager hides it)
# This is only used when accessing camera in locked phone state
self.marionette.switch_to_frame()
secure_camera_frame = self.marionette.find_element(*self._secure_camera_frame_locator)
self.marionette.switch_to_frame(secure_camera_frame)
self.wait_for_capture_ready()
def tap_switch_to_gallery(self):
switch_to_gallery_button = self.marionette.find_element(*self._gallery_button_locator)
switch_to_gallery_button.tap()
from gaiatest.apps.gallery.app import Gallery
gallery_app = Gallery(self.marionette)
gallery_app.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
return gallery_app
def wait_for_thumbnail_visible(self):
Wait(self.marionette).until(expected.element_displayed(*self._thumbnail_button_locator))
def tap_thumbnail(self):
self.marionette.find_element(*self._thumbnail_button_locator).tap()
@property
def is_thumbnail_visible(self):
return self.is_element_displayed(*self._thumbnail_button_locator)
@property
def video_timer(self):
text = self.marionette.find_element(*self._video_timer_locator).text
return time.strptime(text, '%M:%S')
@property
def is_gallery_button_visible(self):
return self.is_element_displayed(*self._gallery_button_locator)
@property
def current_flash_mode(self):
return self.marionette.find_element(*self._hud_locator).get_attribute('flash-mode')
@property
def current_image_src(self):
return self.marionette.find_element(*self._thumbnail_button_locator).get_attribute('src')
def wait_for_picture_to_change(self, image_src):
Wait(self.marionette).until(lambda m: self.current_image_src != image_src)
class ImagePreview(Base):
_media_frame_locator = (By.ID, 'preview')
_image_preview_locator = (By.CSS_SELECTOR, '#media-frame > img')
_camera_button_locator = (By.ID, 'camera-button')
_video_play_button_locator = (By.CLASS_NAME, 'videoPlayerPlayButton')
_video_pause_button_locator = (By.CLASS_NAME, 'videoPlayerPauseButton')
_video_progress_bar_locator = (By.CLASS_NAME, 'videoPlayerProgress')
_options_button_locator = (By.CLASS_NAME, 'preview-option-icon')
_delete_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="delete"]')
_gallery_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="open-gallery"]')
_confirm_delete_button = (By.ID, 'dialog-yes')
_option_menu_locator = (By.CLASS_NAME, 'js-menu')
# for Gallery app switch
_progress_bar_locator = (By.ID, 'progress')
_thumbnail_list_view_locator = (By.CSS_SELECTOR, '#thumbnail-views > footer.thumbnails-list')
@property
def is_image_preview_visible(self):
return self.is_element_displayed(*self._image_preview_locator)
@property
def is_video_play_button_visible(self):
return self.is_element_displayed(*self._video_play_button_locator)
@property
def is_progress_bar_showing(self):
return self.is_element_displayed(*self._video_progress_bar_locator)
def wait_for_media_frame(self):
media_frame = self.marionette.find_element(*self._media_frame_locator)
screen_height = int(self.marionette.execute_script('return window.screen.height;'))
Wait(self.marionette).until(lambda m: (media_frame.location['y'] + media_frame.size['height']) == screen_height)
def tap_video_player_play_button(self):
self.marionette.find_element(*self._video_play_button_locator).tap()
Wait(self.marionette).until(lambda m: self.is_progress_bar_showing is True)
def tap_video_player_pause_button(self):
self.marionette.find_element(*self._video_pause_button_locator).tap()
Wait(self.marionette).until(lambda m: self.is_progress_bar_showing is False)
def tap_options(self):
self.marionette.find_element(*self._options_button_locator).tap()
Wait(self.marionette).until(expected.element_displayed(*self._option_menu_locator))
def delete_file(self):
self.tap_options()
self.marionette.find_element(*self._delete_button_locator).tap()
element = Wait(self.marionette).until(expected.element_present(*self._confirm_delete_button))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
def tap_switch_to_gallery(self):
self.tap_options()
self.marionette.find_element(*self._gallery_button_locator).tap()
from gaiatest.apps.gallery.app import Gallery
gallery_app = Gallery(self.marionette)
gallery_app.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
Wait(self.marionette).until(expected.element_displayed(*self._thumbnail_list_view_locator))
return gallery_app
def tap_camera(self):
self.marionette.find_element(*self._camera_button_locator).tap()
camera = Camera(self.marionette)
return camera

View file

@ -0,0 +1,37 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class Clock(Base):
name = 'Clock'
_visible_clock_locator = (By.CSS_SELECTOR, '#clock-view .visible')
_clock_views = {"stopwatch": "stopwatch-tab", "alarm": "alarm-tab", "timer": "timer-tab"}
def launch(self):
Base.launch(self)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._visible_clock_locator))))
def switch_view(self, view_name):
Wait(self.marionette).until(
expected.element_present(*(By.ID, self._clock_views.get(view_name)))).tap()
if view_name == 'stopwatch':
from gaiatest.apps.clock.regions.stopwatch import StopWatch
return StopWatch(self.marionette)
elif view_name == 'timer':
from gaiatest.apps.clock.regions.timer import Timer
return Timer(self.marionette)
elif view_name == 'alarm':
from gaiatest.apps.clock.regions.alarm import Alarm
return Alarm(self.marionette)
else:
raise AttributeError('{} is not a view that you can switch to'.format(view_name))

View file

@ -0,0 +1,82 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import PageRegion
class Alarm(PageRegion):
_alarm_create_new_locator = (By.ID, 'alarm-new')
_alarm_view_locator = (By.ID, 'alarm-tab')
_edit_alarm_view_locator = (By.ID, 'edit-alarm')
_all_alarm_items_locator = (By.CSS_SELECTOR, '#alarms li')
_banner_countdown_notification_locator = (By.ID, 'banner-countdown')
_visible_clock_locator = (By.CSS_SELECTOR, '#clock-view .visible')
def __init__(self, marionette):
PageRegion.__init__(self, marionette,self._alarm_view_locator)
view = self.marionette.find_element(*self._alarm_view_locator)
Wait(self.marionette).until(lambda m: view.location['x'] == 0 and view.is_displayed())
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._alarm_create_new_locator))))
@property
def alarm_items(self):
return [self.AlarmItem(self.marionette, alarm_item) for alarm_item in
self.marionette.find_elements(*self._all_alarm_items_locator)]
def tap_new_alarm(self):
new_alarm = Wait(self.marionette).until(
expected.element_present(*self._alarm_create_new_locator))
Wait(self.marionette).until(expected.element_displayed(new_alarm))
new_alarm.tap()
from gaiatest.apps.clock.regions.new_alarm import NewAlarm
return NewAlarm(self.marionette)
@property
def banner_notification(self):
banner = Wait(self.marionette).until(
expected.element_present(*self._banner_countdown_notification_locator))
Wait(self.marionette).until(expected.element_displayed(banner))
return banner.text
def dismiss_banner(self):
banner = Wait(self.marionette).until(
expected.element_present(*self._banner_countdown_notification_locator))
Wait(self.marionette).until(expected.element_displayed(banner))
# We can't tap to clear the banner as sometimes it taps the underlying alarm changing the UI
Wait(self.marionette).until(expected.element_not_displayed(banner))
class AlarmItem(PageRegion):
_label_locator = (By.CSS_SELECTOR, '.label')
_check_box_locator = (By.CSS_SELECTOR, '.input-enable')
_enable_button_locator = (By.CSS_SELECTOR, '.alarmList.alarmEnable')
_time_locator = (By.CSS_SELECTOR, '.time')
@property
def time(self):
return self.root_element.find_element(*self._time_locator).text
@property
def label(self):
return self.root_element.find_element(*self._label_locator).text
@property
def is_alarm_active(self):
return self.root_element.find_element(*self._check_box_locator).is_selected()
def tap_checkbox(self):
self.root_element.find_element(*self._enable_button_locator).tap()
def wait_for_checkbox_to_change_state(self, value):
checkbox = self.marionette.find_element(*self._check_box_locator)
Wait(self.marionette).until(lambda m: checkbox.is_selected() == value)
def tap(self):
self.root_element.tap()
from gaiatest.apps.clock.regions.new_alarm import EditAlarm
return EditAlarm(self.marionette)

View file

@ -0,0 +1,32 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class AlarmAlertScreen(Base):
_alarm_frame_locator = (By.CSS_SELECTOR, 'iframe[mozapp*="clock"]:nth-child(1)')
_stop_button_locator = (By.ID, 'ring-button-stop')
_alarm_label_locator = (By.ID, 'ring-label')
def wait_for_alarm_to_trigger(self):
alarm_frame = Wait(self.marionette, timeout=60, interval=5).until(
expected.element_present(*self._alarm_frame_locator))
Wait(self.marionette).until(expected.element_displayed(alarm_frame))
self.marionette.switch_to_frame(alarm_frame)
def tap_stop_alarm(self):
stop_alarm_button = Wait(self.marionette).until(
expected.element_present(*self._stop_button_locator))
Wait(self.marionette).until(expected.element_displayed(stop_alarm_button))
self.tap_element_from_system_app(stop_alarm_button)
@property
def alarm_label(self):
alarm_label = Wait(self.marionette).until(
expected.element_present(*self._alarm_label_locator))
Wait(self.marionette).until(expected.element_displayed(alarm_label))
return alarm_label.text

View file

@ -0,0 +1,77 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.clock.app import Clock
class NewAlarm(Clock):
_alarm_name_locator = (By.ID, 'alarm-name')
_new_alarm_view_locator = (By.CSS_SELECTOR, 'section[data-panel-id="alarm_edit"]')
_repeat_menu_locator = (By.ID, 'repeat-menu')
_sound_menu_locator = (By.ID, 'sound-menu')
_snooze_menu_locator = (By.ID, 'snooze-menu')
_done_locator = (By.ID, 'alarm-done')
_time_button_locator = (By.XPATH, "//li[input[@id='time-select']]")
def __init__(self, marionette):
Clock.__init__(self, marionette)
view = self.marionette.find_element(*self._new_alarm_view_locator)
Wait(self.marionette).until(lambda m: view.location['x'] == 0 and view.is_displayed())
# Bug 1032852 This is to bust intermittents caused by this bug that causes keyboard not to appear upon tap
time.sleep(1.5)
def type_alarm_label(self, value):
self.marionette.find_element(*self._alarm_name_locator).tap()
self.keyboard.send(value)
self.keyboard.dismiss()
@property
def alarm_label_placeholder(self):
return self.marionette.find_element(*self._alarm_name_locator).get_attribute('placeholder')
@property
def alarm_repeat(self):
repeat = self.marionette.find_element(*self._repeat_menu_locator).get_attribute('data-l10n-id')
return repeat if repeat is not None else self.marionette.find_element(*self._repeat_menu_locator).text
def select_repeat(self, value):
self.marionette.find_element(*self._repeat_menu_locator).tap()
self.select(value)
@property
def alarm_snooze(self):
return self.marionette.find_element(*self._snooze_menu_locator).get_attribute('data-l10n-args')
def select_snooze(self, value):
self.marionette.find_element(*self._snooze_menu_locator).tap()
self.select(value)
@property
def alarm_sound(self):
return self.marionette.find_element(*self._sound_menu_locator).get_attribute('data-l10n-id')
def select_sound(self, value):
self.marionette.find_element(*self._sound_menu_locator).tap()
self.select(value)
def tap_done(self):
done = Wait(self.marionette).until(expected.element_present(*self._done_locator))
Wait(self.marionette).until(expected.element_displayed(done))
done.tap()
from gaiatest.apps.clock.regions.alarm import Alarm
return Alarm(self.marionette)
def tap_time(self):
self.marionette.find_element(*self._time_button_locator).tap()
from gaiatest.apps.system.regions.time_picker import TimePicker
return TimePicker(self.marionette)
class EditAlarm(NewAlarm):
pass

View file

@ -0,0 +1,58 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import PageRegion
class StopWatch(PageRegion):
_stopwatch_view_locator = (By.ID,'stopwatch-panel')
_stopwatch_time_locator = (By.CSS_SELECTOR, ".stopwatch-time")
_stopwatch_start_locator = (By.CSS_SELECTOR, '#stopwatch-controls .stopwatch-start')
_stopwatch_pause_locator = (By.CSS_SELECTOR, '#stopwatch-controls .stopwatch-pause')
_stopwatch_reset_locator = (By.CSS_SELECTOR, '#stopwatch-controls .stopwatch-reset')
_stopwatch_resume_locator = (By.CSS_SELECTOR, '#stopwatch-controls .stopwatch-resume')
_stopwatch_lap_locator = (By.CSS_SELECTOR, '#stopwatch-controls .stopwatch-lap')
_all_laps_locator = (By.CSS_SELECTOR, '.stopwatch-laps li')
def __init__(self, marionette):
PageRegion.__init__(self, marionette, self._stopwatch_view_locator)
view = self.marionette.find_element(*self._stopwatch_view_locator)
Wait(self.marionette).until(lambda m: view.location['x'] == 0 and view.is_displayed())
@property
def current_time(self):
current_time = Wait(self.marionette).until(expected.element_present(*self._stopwatch_time_locator))
return current_time.text
@property
def lap_items(self):
return [self.LapItem(self.marionette, lap) for lap in self.marionette.find_elements(*self._all_laps_locator)]
def tap_start(self):
Wait(self.marionette).until(expected.element_present(*self._stopwatch_start_locator)).tap()
def tap_pause(self):
Wait(self.marionette).until(expected.element_present(*self._stopwatch_pause_locator)).tap()
def tap_reset(self):
Wait(self.marionette).until(expected.element_present(*self._stopwatch_reset_locator)).tap()
def tap_resume(self):
Wait(self.marionette).until(expected.element_present(*self._stopwatch_resume_locator)).tap()
def tap_lap(self):
Wait(self.marionette).until(expected.element_present(*self._stopwatch_lap_locator)).tap()
class LapItem(PageRegion):
_lap_name = (By.CSS_SELECTOR, '.lap-name')
_lap_time = (By.CSS_SELECTOR, '.lap-duration')
@property
def name(self):
return self.root_element.find_element(*self._lap_name).text
@property
def time(self):
return self.root_element.find_element(*self._lap_time).text

View file

@ -0,0 +1,5 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View file

@ -0,0 +1,16 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import By, Wait
from gaiatest.apps.base import PageRegion
class Timer(PageRegion):
_timer_view_locator = (By.ID, 'timer-panel')
def __init__(self, marionette):
PageRegion.__init__(self, marionette, self._timer_view_locator)
view = self.marionette.find_element(*self._timer_view_locator)
Wait(self.marionette).until(lambda m: view.location['x'] == 0 and view.is_displayed())

View file

@ -0,0 +1,3 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View file

@ -0,0 +1,191 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from marionette_driver.errors import JavascriptException
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
class Contacts(Base):
name = "Contacts"
entry_point = "contacts"
manifest_url = '{}communications{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL,Base.DEFAULT_APP_HOSTNAME)
_new_contact_button_locator = (By.ID, 'add-contact-button')
_settings_button_locator = (By.ID, 'settings-button')
_favorites_list_locator = (By.ID, 'contacts-list-favorites')
_select_all_wrapper_locator = (By.ID, 'select-all-wrapper')
_select_all_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="selectAll"]')
_select_action_button_locator = (By.ID, 'select-action')
_status_message_locator = (By.ID, 'statusMsg')
_confirm_delete_locator = (By.CSS_SELECTOR, 'button.danger[data-l10n-id="delete"]')
_no_contacts_message_locator = (By.CSS_SELECTOR, '*[data-l10n-id="no-contacts"]')
_group_container_selector = "#groups-container"
_contact_locator = (By.CSS_SELECTOR, 'li[data-uuid]:not([data-group="ice"])')
_ice_contact = (By.CSS_SELECTOR, 'ol#ice-group')
_ice_list_icon_locator = (By.ID, 'contact-list-ice')
_select_ice_contact_1_locator = (By.ID, 'select-ice-contact-1')
def launch(self):
Base.launch(self)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._settings_button_locator))))
# Workaround for bug 1201211
import time
time.sleep(1)
def switch_to_contacts_frame(self):
self.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
@property
def contacts(self):
return [self.Contact(marionette=self.marionette, element=contact)
for contact in self.marionette.find_elements(*self._contact_locator)]
def wait_for_contacts(self, number_to_wait_for=1):
Wait(self.marionette).until(lambda m: len(m.find_elements(
*self._contact_locator)) == number_to_wait_for)
# we need to scroll in order to force the rendering of all the contacts
height = self.marionette.execute_script("return document.querySelector('[data-uuid]').clientHeight;")
for idx in range(number_to_wait_for):
self.marionette.execute_script("document.querySelector('{0}').scrollTop = {1}".
format(self._group_container_selector, idx * height))
def contact(self, name):
for contact in self.contacts:
if contact.name == name:
return contact
def tap_new_contact(self):
self.marionette.find_element(*self._new_contact_button_locator).tap()
from gaiatest.apps.contacts.regions.contact_form import NewContact
new_contact = NewContact(self.marionette)
new_contact.wait_for_new_contact_form_to_load()
return new_contact
def a11y_click_new_contact(self):
self.accessibility.click(self.marionette.find_element(*self._new_contact_button_locator))
from gaiatest.apps.contacts.regions.contact_form import NewContact
new_contact = NewContact(self.marionette)
new_contact.wait_for_new_contact_form_to_load()
return new_contact
def tap_settings(self):
settings = self.marionette.find_element(*self._settings_button_locator)
Wait(self.marionette).until(expected.element_displayed(settings))
settings.tap()
Wait(self.marionette).until(expected.element_not_displayed(settings))
from gaiatest.apps.contacts.regions.settings_form import SettingsForm
return SettingsForm(self.marionette)
def tap_select_all(self):
window_height = self.marionette.execute_script('return window.wrappedJSObject.innerHeight')
wrapper = self.marionette.find_element(*self._select_all_wrapper_locator)
Wait(self.marionette).until(lambda m: int(wrapper.size['height'] + wrapper.location['y']) == window_height)
self.marionette.find_element(*self._select_all_button_locator).tap()
def tap_export(self):
self._tap_action_button()
def tap_delete(self):
self._tap_action_button()
def _tap_action_button(self):
# The same button is used to do bulk operations (like delete or export). The displayed string changes though.
# Hence, let's define more semantically explicit functions.
self.marionette.find_element(*self._select_action_button_locator).tap()
def tap_confirm_delete(self):
delete_button = Wait(self.marionette).until(expected.element_present(*self._confirm_delete_locator))
Wait(self.marionette).until(expected.element_displayed(delete_button))
delete_button.tap()
@property
def is_favorites_list_displayed(self):
return self.marionette.find_element(*self._favorites_list_locator).is_displayed()
@property
def is_ice_list_icon_displayed(self):
return self.marionette.find_element(*self._ice_list_icon_locator).is_displayed()
def open_ice_contact_list(self):
ice_list = Wait(self.marionette).until(expected.element_present(*self._ice_list_icon_locator))
Wait(self.marionette).until(expected.element_displayed(ice_list))
ice_list.tap()
return Contacts(self.marionette)
def wait_for_ice_contact_shown(self):
Wait(self.marionette).until(expected.element_displayed(*self._ice_contact))
@property
def status_message(self):
status = Wait(self.marionette).until(expected.element_present(
*self._status_message_locator))
Wait(self.marionette).until(expected.element_displayed(status))
return status.text
@property
def is_no_contacts_message_displayed(self):
return self.marionette.find_element(*self._no_contacts_message_locator).is_displayed()
class Contact(PageRegion):
_name_locator = (By.CSS_SELECTOR, 'bdi > strong')
_full_name_locator = (By.CSS_SELECTOR, 'p.contact-text bdi')
_image_locator = (By.CSS_SELECTOR, 'span[data-type="img"]')
@property
def name(self):
return self.root_element.find_element(*self._name_locator).text
@property
def full_name(self):
return self.root_element.find_element(*self._full_name_locator).text
@property
def image_data_group(self):
return self.root_element.find_element(*self._image_locator).get_attribute('data-group')
def tap(self, return_class='ContactDetails'):
self.marionette.execute_script('arguments[0].scrollIntoView(false);', [self.root_element])
if return_class == None:
self.tap_element_from_system_app(
self.root_element.find_element(*self._name_locator), add_statusbar_height=True)
else:
self.root_element.find_element(*self._name_locator).tap()
return self._return_class_from_tap(return_class)
def a11y_click(self, return_class='ContactDetails'):
self.accessibility.click(self.root_element)
return self._return_class_from_tap(return_class)
def _return_class_from_tap(self, return_class='ContactDetails'):
if return_class == 'ContactDetails':
Wait(self.marionette).until(lambda m: expected.element_not_displayed(self.root_element))
from gaiatest.apps.contacts.regions.contact_details import ContactDetails
return ContactDetails(self.marionette)
elif return_class == 'EditContact':
# This may seem superfluous but we can enter EditContact from Contacts, or from ActivityPicker
Contacts(self.marionette).wait_to_be_displayed()
self.apps.switch_to_displayed_app()
from gaiatest.apps.contacts.regions.contact_form import EditContact
return EditContact(self.marionette)
elif return_class == 'SelectContact':
return None
elif return_class == 'SetIceContacts':
from gaiatest.apps.contacts.regions.set_ice_contacts import SetIceContacts
return SetIceContacts(self.marionette)
else:
# We are using contacts picker in activity - after choosing, fall back to open app
Contacts(self.marionette).wait_to_not_be_displayed()
self.apps.switch_to_displayed_app()

View file

@ -0,0 +1,3 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View file

@ -0,0 +1,90 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.form_controls.header import GaiaHeader
class ContactDetails(Base):
_contact_name_title_locator = (By.ID, 'contact-name-title')
_contact_image_locator = (By.ID, 'cover-img')
_call_phone_number_button_locator = (By.ID, 'call-or-pick-0')
_phone_numbers_locator = (By.CSS_SELECTOR, '#contact-detail-inner .icon-call')
_send_sms_button_locator = (By.ID, 'send-sms-button-0')
_edit_contact_button_locator = (By.ID, 'edit-contact-button')
_details_header_locator = (By.ID, 'details-view-header')
_add_remove_favorite_button_locator = (By.ID, 'toggle-favorite')
_comments_locator = (By.ID, 'note-details-template-0')
def __init__(self, marionette):
Base.__init__(self, marionette)
el = self.marionette.find_element(*self._details_header_locator)
Wait(self.marionette).until(lambda m: el.location['x'] == 0 and el.is_displayed())
@property
def full_name(self):
return self.marionette.find_element(*self._contact_name_title_locator).text
@property
def phone_number(self):
return self.marionette.find_element(*self._call_phone_number_button_locator).text
@property
def phone_numbers(self):
return [element.text for element in self.marionette.find_elements(*self._phone_numbers_locator)]
@property
def comments(self):
return self.marionette.find_element(*self._comments_locator).text
@property
def image_style(self):
return self.marionette.find_element(*self._contact_image_locator).get_attribute('style')
def tap_phone_number(self):
call = self.marionette.find_element(*self._call_phone_number_button_locator)
Wait(self.marionette).until(expected.element_enabled(call))
call.tap()
from gaiatest.apps.phone.regions.call_screen import CallScreen
return CallScreen(self.marionette)
def tap_send_sms(self):
self.marionette.find_element(*self._send_sms_button_locator).tap()
from gaiatest.apps.messages.regions.new_message import NewMessage
return NewMessage(self.marionette)
def tap_edit(self):
edit = Wait(self.marionette).until(expected.element_present(
*self._edit_contact_button_locator))
Wait(self.marionette).until(expected.element_displayed(edit))
edit.tap()
from gaiatest.apps.contacts.regions.contact_form import EditContact
return EditContact(self.marionette)
def a11y_click_edit(self):
edit = Wait(self.marionette).until(expected.element_present(
*self._edit_contact_button_locator))
Wait(self.marionette).until(expected.element_displayed(edit))
self.accessibility.click(edit)
from gaiatest.apps.contacts.regions.contact_form import EditContact
return EditContact(self.marionette)
def tap_back(self):
GaiaHeader(self.marionette, self._details_header_locator).go_back()
def tap_add_remove_favorite(self):
button = self.marionette.find_element(*self._add_remove_favorite_button_locator)
# Capture the current state of the element
initial_state = button.get_attribute('data-l10n-id')
self.marionette.execute_script("arguments[0].scrollIntoView(false);", [button])
button.tap()
# Wait for it to have toggled
Wait(self.marionette).until(lambda m: button.get_attribute('data-l10n-id') != initial_state)
@property
def add_remove_text(self):
return self.marionette.find_element(*self._add_remove_favorite_button_locator).text

View file

@ -0,0 +1,227 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class ContactForm(Base):
name = 'Contacts'
_given_name_locator = (By.ID, 'givenName')
_family_name_locator = (By.ID, 'familyName')
_phone_locator = (By.ID, 'number_0')
_email_locator = (By.ID, 'email_0')
_street_locator = (By.ID, 'streetAddress_0')
_zip_code_locator = (By.ID, 'postalCode_0')
_city_locator = (By.ID, 'locality_0')
_country_locator = (By.ID, 'countryName_0')
_comment_locator = (By.ID, 'note_0')
_add_new_email_locator = (By.ID, 'add-new-email')
_add_new_address_locator = (By.ID, 'add-new-address')
_add_new_note_locator = (By.ID, 'add-new-note')
_add_new_phone_locator = (By.ID, 'add-new-phone')
_thumbnail_photo_locator = (By.ID, 'thumbnail-photo')
@property
def given_name(self):
return self.marionette.find_element(*self._given_name_locator).text
def type_given_name(self, value):
element = self.marionette.find_element(*self._given_name_locator)
element.clear()
element.send_keys(value)
@property
def family_name(self):
return self.marionette.find_element(*self._family_name_locator).text
def type_family_name(self, value):
element = self.marionette.find_element(*self._family_name_locator)
element.clear()
element.send_keys(value)
@property
def phone(self):
return self.marionette.find_element(*self._phone_locator).text
def _type_in_field(self, add_locator, field_locator, value):
Wait(self.marionette).until(expected.element_present(*add_locator)).tap()
element = Wait(self.marionette).until(expected.element_present(*field_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.clear()
element.send_keys(value)
def type_phone(self, value):
self._type_in_field(self._add_new_phone_locator, self._phone_locator, value)
@property
def email(self):
return self.marionette.find_element(*self._email_locator).text
def type_email(self, value):
self._type_in_field(self._add_new_email_locator, self._email_locator, value)
@property
def street(self):
return self.marionette.find_element(*self._street_locator).text
def type_street(self, value):
self._type_in_field(self._add_new_address_locator, self._street_locator, value)
@property
def zip_code(self):
return self.marionette.find_element(*self._zip_code_locator).text
def type_zip_code(self, value):
element = self.marionette.find_element(*self._zip_code_locator)
element.clear()
element.send_keys(value)
@property
def city(self):
return self.marionette.find_element(*self._city_locator).text
def type_city(self, value):
element = self.marionette.find_element(*self._city_locator)
element.clear()
element.send_keys(value)
@property
def country(self):
return self.marionette.find_element(*self._country_locator).text
def type_country(self, value):
element = self.marionette.find_element(*self._country_locator)
element.clear()
element.send_keys(value)
@property
def comment(self):
return self.marionette.find_element(*self._comment_locator).text
def type_comment(self, value):
if self.is_element_present(*self._comment_locator):
element = self.marionette.find_element(*self._comment_locator)
element.clear()
element.send_keys(value)
else:
self._type_in_field(self._add_new_note_locator, self._comment_locator, value)
def tap_comment(self):
self.marionette.find_element(*self._comment_locator).tap()
@property
def picture_style(self):
return self.marionette.find_element(*self._thumbnail_photo_locator).get_attribute('style')
def tap_picture(self):
self.marionette.find_element(*self._thumbnail_photo_locator).tap()
from gaiatest.apps.system.regions.activities import Activities
return Activities(self.marionette)
def wait_for_image_to_load(self):
el = self.marionette.find_element(*self._thumbnail_photo_locator)
Wait(self.marionette).until(lambda m: 'background-image' in el.get_attribute('style'))
class EditContact(ContactForm):
_update_locator = (By.ID, 'save-button')
_cancel_locator = (By.ID, 'cancel-edit')
_delete_locator = (By.ID, 'delete-contact')
_delete_form_locator = (By.ID, 'confirmation-message')
_cancel_delete_locator = (By.CSS_SELECTOR, 'form#confirmation-message button:not(.danger)')
_confirm_delete_locator = (By.CSS_SELECTOR, 'form#confirmation-message button.danger')
def __init__(self, marionette):
ContactForm.__init__(self, marionette)
update = Wait(self.marionette).until(expected.element_present(*self._update_locator))
Wait(self.marionette).until(lambda m: update.location['y'] == 0 and update.is_displayed())
def tap_update(self, return_class='ContactDetails'):
self.wait_for_update_button_enabled()
update = self.marionette.find_element(*self._update_locator)
update.tap()
if return_class == 'ContactDetails':
from gaiatest.apps.contacts.regions.contact_details import ContactDetails
return ContactDetails(self.marionette)
elif return_class == 'Merge':
from gaiatest.apps.contacts.regions.merge import Merge
return Merge(self.marionette)
else:
# else we drop back to the underlying app
from gaiatest.apps.contacts.app import Contacts
Contacts(self.marionette).wait_to_not_be_displayed()
self.apps.switch_to_displayed_app()
def tap_cancel(self):
self.marionette.find_element(*self._cancel_locator).tap()
from gaiatest.apps.contacts.regions.contact_details import ContactDetails
return ContactDetails(self.marionette)
def tap_delete(self):
delete_item = self.marionette.find_element(*self._delete_locator)
self.marionette.execute_script(
'arguments[0].scrollIntoView(true);', [delete_item])
delete_item.tap()
def tap_cancel_delete(self):
Wait(self.marionette).until(expected.element_displayed(*self._delete_form_locator))
self.marionette.find_element(*self._cancel_delete_locator).tap()
def tap_confirm_delete(self):
Wait(self.marionette).until(expected.element_displayed(*self._delete_form_locator))
self.marionette.find_element(*self._confirm_delete_locator).tap()
def wait_for_update_button_enabled(self):
update = self.marionette.find_element(*self._update_locator)
Wait(self.marionette).until(expected.element_enabled(update))
class NewContact(ContactForm):
_src = 'app://communications.gaiamobile.org/contacts/views/form/form.html'
_done_button_locator = (By.ID, 'save-button')
def __init__(self, marionette):
ContactForm.__init__(self, marionette)
def switch_to_new_contact_form(self):
# When NewContact form is called as an ActivityWindow
Wait(self.marionette).until(lambda m: self.apps.displayed_app.src == self._src)
self.apps.switch_to_displayed_app()
self.wait_for_new_contact_form_to_load()
def wait_for_new_contact_form_to_load(self):
done = self.marionette.find_element(*self._done_button_locator)
Wait(self.marionette).until(lambda m: done.location['y'] == 0)
def tap_done(self, return_contacts=True):
element = self.marionette.find_element(*self._done_button_locator)
self.tap_element_from_system_app(element, add_statusbar_height=True)
return self.wait_for_done(return_contacts)
def a11y_click_done(self, return_contacts=True):
element = self.marionette.find_element(*self._done_button_locator)
self.accessibility.click(element)
Wait(self.marionette).until(expected.element_not_displayed(element))
return self.wait_for_done(return_contacts)
def wait_for_done(self, return_contacts=True):
# NewContact can be opened as an Activity from other apps. In this scenario we don't return Contacts
if return_contacts:
self.apps.switch_to_displayed_app()
from gaiatest.apps.contacts.app import Contacts
return Contacts(self.marionette)
else:
from gaiatest.apps.contacts.app import Contacts
Contacts(self.marionette).wait_to_not_be_displayed()
# Fall back to the underlying app
self.apps.switch_to_displayed_app()

View file

@ -0,0 +1,47 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class ContactImportPicker(Base):
_contact_import_picker_frame_locator = (By.ID, 'iframe_extensions')
_import_button_locator = (By.ID, 'import-action')
_friends_list_locator = (By.ID, 'friends-list')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(
expected.element_displayed(*self._contact_import_picker_frame_locator))
select_contacts = self.marionette.find_element(*self._contact_import_picker_frame_locator)
self.marionette.switch_to_frame(select_contacts)
def tap_import_button(self, wait_for_import = True):
self.marionette.execute_script('window.wrappedJSObject.importer.ui.importAll();')
# TODO uncomment this when Bug 932804 is resolved
# self.marionette.find_element(*self._import_button_locator).tap()
self.apps.switch_to_displayed_app()
if wait_for_import:
Wait(self.marionette, timeout=60).until(
expected.element_not_displayed(*self._contact_import_picker_frame_locator))
from gaiatest.apps.contacts.regions.settings_form import SettingsForm
return SettingsForm(self.marionette)
def tap_first_friend(self):
element = self.marionette.find_element(*self._friends_list_locator)
# TODO replace this with proper tap when Bug 932804 is resolved
self.marionette.execute_script("""
window.wrappedJSObject.document.getElementById("friends-list")
.getElementsByTagName("a")[1].click()
""")
Wait(self.marionette).until(expected.element_not_displayed(element))
self.apps.switch_to_displayed_app()
def tap_select_all(self):
# TODO replace this with proper tap when Bug 932804 is resolved
self.marionette.execute_script('window.wrappedJSObject.importer.ui.selectAll();')
el = self.marionette.find_element(*self._import_button_locator)
Wait(self.marionette).until(expected.element_enabled(el))

View file

@ -0,0 +1,47 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class GmailLogin(Base):
_gmail_sign_in_frame_locator = (By.CSS_SELECTOR, '.popupWindow.active iframe[data-url*="google"]')
_email_locator = (By.ID, 'Email')
_password_locator = (By.ID, 'Passwd')
_sign_in_locator = (By.ID, 'signIn')
_grant_access_button_locator = (By.ID, 'submit_approve_access')
_next_locator = (By.ID, 'next')
def switch_to_gmail_login_frame(self):
self.marionette.switch_to_frame()
gmail_sign_in = self.marionette.find_element(*self._gmail_sign_in_frame_locator)
self.marionette.switch_to_frame(gmail_sign_in)
def gmail_login(self, user, passwd):
email = Wait(self.marionette).until(
expected.element_present(*self._email_locator))
Wait(self.marionette).until(expected.element_displayed(email))
email.tap()
email.send_keys(user)
self.marionette.find_element(*self._next_locator).tap()
password = Wait(self.marionette).until(
expected.element_present(*self._password_locator))
Wait(self.marionette).until(expected.element_displayed(password))
password.tap()
password.send_keys(passwd)
self.keyboard.dismiss()
self.marionette.find_element(*self._sign_in_locator).tap()
def tap_grant_access(self):
grant_access = self.marionette.find_element(*self._grant_access_button_locator)
Wait(self.marionette).until(expected.element_enabled(grant_access))
grant_access.tap()
# Go back to displayed Contacts app before waiting for the picker
from gaiatest.apps.contacts.app import Contacts
Contacts(self.marionette).wait_to_be_displayed()
self.apps.switch_to_displayed_app()
from gaiatest.apps.contacts.regions.contact_import_picker import ContactImportPicker
return ContactImportPicker(self.marionette)

View file

@ -0,0 +1,30 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class Merge(Base):
_iframe_locator = (By.CSS_SELECTOR, '.popupWindow.active iframe[data-url*="matching"]')
_body_locator = (By.TAG_NAME, 'body')
_merge_button = (By.ID, 'merge-action')
def __init__(self, marionette):
Base.__init__(self, marionette)
marionette.switch_to_frame()
merge_iframe = Wait(marionette).until(
expected.element_present(*self._iframe_locator))
Wait(marionette).until(expected.element_displayed(merge_iframe))
marionette.switch_to_frame(merge_iframe)
body = marionette.find_element(*self._body_locator)
Wait(marionette).until(lambda m: body.rect['y'] == 0)
def tap_on_merge(self):
Wait(self.marionette).until(
expected.element_present(*self._merge_button)).tap()

View file

@ -0,0 +1,37 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest import GaiaApps
from gaiatest.apps.base import Base
from gaiatest.apps.contacts.app import Contacts
from gaiatest.form_controls.header import GaiaHeader
from gaiatest.form_controls.binarycontrol import GaiaBinaryControl
class SetIceContacts(Base):
_toggle_locator = (By.CSS_SELECTOR, 'gaia-switch[name="ice-contact-1-enabled"]')
_select_ice_contact_1_locator = (By.ID, 'select-ice-contact-1')
_ice_settings_header_locator = (By.ID, 'ice-settings-header')
def __init__(self, marionette):
Base.__init__(self, marionette)
GaiaApps(marionette).switch_to_displayed_app()
header = self.marionette.find_element(*self._ice_settings_header_locator)
Wait(self.marionette).until(lambda m: header.rect['x'] == 0 and header.is_displayed())
def enable_set_ice_contact(self):
GaiaBinaryControl(self.marionette, self._toggle_locator).enable()
return SetIceContacts(self.marionette)
def select_ice_contact(self):
self.marionette.find_element(*self._select_ice_contact_1_locator).tap()
from gaiatest.apps.contacts.app import Contacts
return Contacts(self.marionette)
def go_back(self):
GaiaHeader(self.marionette, self._ice_settings_header_locator).go_back()
from gaiatest.apps.contacts.regions.settings_form import SettingsForm
return SettingsForm(self.marionette)

View file

@ -0,0 +1,179 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest import GaiaApps
from gaiatest.apps.base import Base
from gaiatest.form_controls.binarycontrol import GaiaBinaryControl
from gaiatest.apps.contacts.regions.set_ice_contacts import SetIceContacts
class SettingsForm(Base):
_settings_view_locator = (By.ID, 'view-settings')
_loading_overlay_locator = (By.ID, 'loading-overlay')
_settings_close_button_locator = (By.ID, 'settings-close')
_order_by_last_name_switch_locator = (By.CSS_SELECTOR, 'gaia-switch[name="order.lastname"]')
_import_from_sim_button_locator = (By.CSS_SELECTOR, "li[id*='import-sim-option'] button")
_import_from_sdcard_locator = (By.CSS_SELECTOR, 'button.icon-sd')
_import_from_gmail_button_locator = (By.CSS_SELECTOR, 'button.icon-gmail')
_import_from_windows_live_button_locator = (By.CSS_SELECTOR, 'button.icon-live')
_import_settings_header = (By.ID, 'import-settings-header')
_export_to_sd_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="memoryCard"]')
_export_to_sim_button_locator = (By.CSS_SELECTOR, '#export-options button.icon-sim')
_import_contacts_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="importContactsButton"]')
_export_contacts_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="exportContactsButton"]')
_delete_contacts_locator = (By.ID, 'bulkDelete')
_gmail_contacts_imported_locator = (By.CSS_SELECTOR, '.icon.icon-gmail > p > span')
_gmail_import_option_locator = (By.ID, 'import-gmail-option')
_import_settings_locator = (By.ID, 'import-settings')
_select_contacts_locator = (By.ID, 'selectable-form')
_import_error_message_locator = (By.CSS_SELECTOR, '#import-live-option > p.error-message')
_outlook_import_option_locator = (By.ID, 'import-live-option')
_import_from_outlook_button_locator = (By.CSS_SELECTOR, 'button.icon-live')
_set_ice_contact_button_locator = (By.ID, 'set-ice')
def __init__(self, marionette):
Base.__init__(self, marionette)
view = self.marionette.find_element(*self._settings_view_locator)
Wait(self.marionette).until(lambda m: view.rect['y'] == 0)
def enable_order_by_last_name(self):
self._order_by_last_name_switch.enable()
def disable_order_by_last_name(self):
self._order_by_last_name_switch.disable()
@property
def _order_by_last_name_switch(self):
return GaiaBinaryControl(self.marionette, self._order_by_last_name_switch_locator)
def tap_import_contacts(self):
import_contacts = Wait(self.marionette).until(
expected.element_present(*self._import_contacts_locator))
Wait(self.marionette).until(expected.element_displayed(import_contacts))
import_contacts.tap()
import_settings = self.marionette.find_element(*self._import_settings_locator)
Wait(self.marionette).until(lambda m: import_settings.location['x'] == 0)
def tap_export_contacts(self):
export_contacts = Wait(self.marionette).until(
expected.element_present(*self._export_contacts_locator))
Wait(self.marionette).until(expected.element_displayed(export_contacts))
export_contacts.tap()
import_settings = self.marionette.find_element(*self._import_settings_locator)
Wait(self.marionette).until(lambda m: import_settings.location['x'] == 0)
def tap_delete_contacts(self):
delete_contacts = Wait(self.marionette).until(
expected.element_present(*self._delete_contacts_locator))
Wait(self.marionette).until(expected.element_displayed(delete_contacts))
delete_contacts.tap()
select_contacts = self.marionette.find_element(*self._select_contacts_locator)
Wait(self.marionette).until(lambda m: select_contacts.location['y'] == 0)
def tap_import_from_sim(self):
import_from_sim = Wait(self.marionette).until(
expected.element_present(*self._import_from_sim_button_locator))
Wait(self.marionette).until(expected.element_displayed(import_from_sim))
import_from_sim.tap()
from gaiatest.apps.contacts.app import Contacts
status_message = Wait(self.marionette).until(
expected.element_present(*Contacts._status_message_locator))
Wait(self.marionette).until(expected.element_displayed(status_message))
Wait(self.marionette).until(expected.element_not_displayed(status_message))
@property
def gmail_imported_contacts(self):
return self.marionette.find_element(*self._gmail_contacts_imported_locator).text
def tap_import_from_gmail(self):
import_from_gmail = Wait(self.marionette).until(
expected.element_present(*self._import_from_gmail_button_locator))
Wait(self.marionette).until(expected.element_displayed(import_from_gmail))
import_from_gmail.tap()
from gaiatest.apps.contacts.regions.gmail import GmailLogin
return GmailLogin(self.marionette)
def tap_import_from_sdcard(self):
import_from_sdcard = Wait(self.marionette).until(
expected.element_present(*self._import_from_sdcard_locator))
Wait(self.marionette).until(expected.element_displayed(import_from_sdcard))
import_from_sdcard.tap()
from gaiatest.apps.contacts.app import Contacts
status_message = Wait(self.marionette).until(
expected.element_present(*Contacts._status_message_locator))
Wait(self.marionette).until(expected.element_displayed(status_message))
Wait(self.marionette).until(expected.element_not_displayed(status_message))
def tap_export_to_sd(self):
export_to_sdcard = Wait(self.marionette).until(
expected.element_present(*self._export_to_sd_button_locator))
Wait(self.marionette).until(expected.element_displayed(export_to_sdcard))
export_to_sdcard.tap()
select_contacts = self.marionette.find_element(*self._select_contacts_locator)
Wait(self.marionette).until(lambda m: select_contacts.location['y'] == 0)
def tap_export_to_sim(self):
export_to_sim = Wait(self.marionette).until(
expected.element_present(*self._export_to_sim_button_locator))
Wait(self.marionette).until(expected.element_displayed(export_to_sim))
export_to_sim.tap()
select_contacts = self.marionette.find_element(*self._select_contacts_locator)
Wait(self.marionette).until(
lambda m: select_contacts.rect['y'] == 0 and select_contacts.is_displayed())
def tap_done(self):
close = self.marionette.find_element(*self._settings_close_button_locator)
close.tap()
Wait(self.marionette).until(expected.element_not_displayed(close))
from gaiatest.apps.contacts.app import Contacts
return Contacts(self.marionette)
def tap_back_from_import_contacts(self):
header = self.marionette.find_element(*self._import_settings_header)
# TODO: remove tap with coordinates after Bug 1061698 is fixed
header.tap(25, 25)
Wait(self.marionette).until(expected.element_not_displayed(header))
def tap_set_ice_contact(self):
self.marionette.find_element(*self._set_ice_contact_button_locator).tap()
return SetIceContacts(self.marionette)
@property
def is_gmail_import_service_in_error(self):
gmail_import_service = self.marionette.find_element(*self._gmail_import_option_locator)
return 'error' in gmail_import_service.get_attribute('class')
@property
def is_gmail_import_enabled(self):
return self.marionette.find_element(*self._import_from_gmail_button_locator).is_enabled()
@property
def is_error_message_displayed(self):
return self.is_element_displayed(*self._import_error_message_locator)
@property
def is_outlook_import_service_in_error(self):
outlook_import_service = self.marionette.find_element(*self._outlook_import_option_locator)
return 'error' in outlook_import_service.get_attribute('class')
@property
def is_outlook_import_enabled(self):
return self.marionette.find_element(*self._import_from_outlook_button_locator).is_enabled()
class ConfirmationView(Base):
_confirmation_locator = (By.ID, 'confirmation-message')
_error_message_locator = (By.CSS_SELECTOR, '#confirmation-message p')
def __init__(self, marionette):
Base.__init__(self, marionette)
view = self.marionette.find_element(*self._confirmation_locator)
Wait(self.marionette).until(lambda m: view.location['y'] == 0)
@property
def error_message(self):
return self.marionette.find_element(*self._error_message_locator).text

View file

@ -0,0 +1,82 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.form_controls.binarycontrol import GaiaBinaryControl
class CostControl(Base):
name = 'Usage'
_usage_app_main_locator = (By.ID, 'datausage-tab')
_usage_app_title_locator = (By.CSS_SELECTOR, 'h1[data-l10n-id="usage"]')
_settings_button_locator = (By.CSS_SELECTOR, 'button.settings-button')
_mobile_data_item_locator = (By.ID, 'mobileItem')
_mobile_data_tracking_locator = (By.ID, 'mobileCheck')
_mobile_data_label_locator = (By.CSS_SELECTOR, '#mobileItem label')
_mobile_data_usage_figure_locator = (By.ID, 'mobileOverview')
_wifi_data_item_locator = (By.ID, 'mobileItem')
_wifi_data_tracking_locator = (By.ID, 'wifiCheck')
_wifi_data_label_locator = (By.CSS_SELECTOR, '#wifiItem label')
_wifi_data_usage_figure_locator = (By.ID, 'wifiOverview')
# FTE
_ftu_frame_locator = (By.ID, 'fte_view')
_ftu_section_locator = (By.ID, 'firsttime-view')
@property
def is_mobile_data_tracking_on(self):
return self._mobile_tracking_switch.is_checked
def disable_mobile_data_tracking(self):
self._mobile_tracking_switch.disable()
@property
def _mobile_tracking_switch(self):
return GaiaBinaryControl(self.marionette, self._mobile_data_tracking_locator)
@property
def is_wifi_data_tracking_on(self):
return self._wifi_tracking_switch.is_checked
def enable_wifi_data_tracking(self):
self._wifi_tracking_switch.enable()
@property
def _wifi_tracking_switch(self):
return GaiaBinaryControl(self.marionette, self._wifi_data_tracking_locator)
@property
def mobile_data_usage_figure(self):
return self.marionette.find_element(*self._mobile_data_usage_figure_locator).text
@property
def wifi_data_usage_figure(self):
return self.marionette.find_element(*self._wifi_data_usage_figure_locator).text
def run_ftu_accepting_defaults(self):
"""Complete the 3 steps of the Usage app's FTU accepting all default values."""
from gaiatest.apps.cost_control.regions.ftu_step1 import FTUStep1
ftu_step1 = FTUStep1(self.marionette)
ftu_step2 = ftu_step1.tap_next()
ftu_step3 = ftu_step2.tap_next()
ftu_step3.tap_lets_go()
def tap_settings(self):
settings = Wait(self.marionette).until(
expected.element_present(*self._settings_button_locator))
Wait(self.marionette).until(expected.element_displayed(settings))
settings.tap()
from gaiatest.apps.cost_control.regions.settings import Settings
return Settings(self.marionette)
def switch_to_ftu(self):
ftu_iframe = self.marionette.find_element(*self._ftu_frame_locator)
Wait(self.marionette).until(
lambda m: 'non-ready' not in ftu_iframe.get_attribute('class') and
ftu_iframe.is_displayed())
self.marionette.switch_to_frame(ftu_iframe)

View file

@ -0,0 +1,29 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.cost_control.regions.ftu_step2 import FTUStep2
class FTUStep1(Base):
_welcome_title_locator = (By.CSS_SELECTOR, 'h1[data-l10n-id="fte-welcome-title"]')
_next_button_locator = (By.CSS_SELECTOR, '#step-1 button[data-l10n-id="next"]')
def __init__(self, marionette):
Base.__init__(self, marionette)
title = self.marionette.find_element(*self._welcome_title_locator)
Wait(self.marionette).until(lambda m: title.location['x'] == 0)
def tap_next(self):
# TODO Remove the sleep when Bug 1013249 is fixed
time.sleep(2)
next = Wait(self.marionette).until(
expected.element_present(*self._next_button_locator))
Wait(self.marionette).until(expected.element_displayed(next))
next.tap()
return FTUStep2(self.marionette)

View file

@ -0,0 +1,30 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import By, Wait
from gaiatest.apps.cost_control.app import CostControl
from gaiatest.apps.cost_control.regions.ftu_step3 import FTUStep3
class FTUStep2(CostControl):
_view_locator = (By.ID, 'non-vivo-step-1')
_reset_report_period_select_locator = (By.CSS_SELECTOR, '#non-vivo-step-1 gaia-select')
_next_button_locator = (By.CSS_SELECTOR, '#non-vivo-step-1 button[data-l10n-id="next"]')
def __init__(self, marionette):
CostControl.__init__(self, marionette)
view = self.marionette.find_element(*self._view_locator)
Wait(self.marionette).until(lambda m: view.location['x'] == 0)
def select_reset_report_value(self, value):
self.marionette.find_element(
*self._reset_report_period_select_locator).tap()
self.select(value)
self.switch_to_ftu()
def tap_next(self):
self.marionette.find_element(*self._next_button_locator).tap()
return FTUStep3(self.marionette)

View file

@ -0,0 +1,75 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.cost_control.app import CostControl
from gaiatest.form_controls.binarycontrol import GaiaBinaryControl
class FTUStep3(CostControl):
_view_locator = (By.ID, 'non-vivo-step-2')
_data_alert_switch_locator = (By.CSS_SELECTOR, '#non-vivo-step-2 gaia-switch[data-option="dataLimit"]')
_data_alert_selector_locator = (By.CSS_SELECTOR, '#non-vivo-step-2 button[data-widget-type="data-limit"]')
# Data limit popup for changing limit volume and unit
_data_limit_view_locator = (By.ID, 'data-limit-dialog')
_data_limit_dialog_input_locator = (By.ID, 'data-limit-input')
_data_limit_dialog_done_locator = (By.ID, 'data-usage-done-button')
_data_limit_switch_unit_locator = (By.CSS_SELECTOR, '#data-limit-dialog .switch-unit-button')
_go_button_locator = (By.CSS_SELECTOR, '#non-vivo-step-2 button.recommend')
def __init__(self, marionette):
CostControl.__init__(self, marionette)
view = self.marionette.find_element(*self._view_locator)
Wait(self.marionette).until(lambda m: view.rect['x'] == 0)
def enable_data_alert(self):
self._data_alert_switch.enable()
# Wait for Usage section to hide/display as required
Wait(self.marionette).until(expected.element_displayed(*self._data_alert_selector_locator))
@property
def is_data_alert_switch_checked(self):
return self._data_alert_switch.is_checked
@property
def _data_alert_switch(self):
return GaiaBinaryControl(self.marionette, self._data_alert_switch_locator)
def select_when_use_is_above_unit_and_value(self, unit, value):
self.marionette.find_element(*self._data_alert_selector_locator).tap()
data_limit_view = self.marionette.find_element(
*self._data_limit_view_locator)
Wait(self.marionette).until(lambda m: data_limit_view.location['y'] == 0)
current_unit = Wait(self.marionette).until(
expected.element_present(*self._data_limit_switch_unit_locator))
Wait(self.marionette).until(expected.element_displayed(current_unit))
if current_unit.text != unit:
current_unit.tap()
Wait(self.marionette).until(lambda m: current_unit.text == unit)
# clear the original assigned value and set it to the new value
self.marionette.find_element(*self._data_limit_dialog_input_locator).clear()
self.keyboard.send(value)
self.switch_to_ftu()
self.marionette.find_element(*self._data_limit_dialog_done_locator).tap()
data_limit_view = self.marionette.find_element(
*self._data_limit_view_locator)
Wait(self.marionette).until(lambda m: int(data_limit_view.location['y']) == int(data_limit_view.size['height']))
def tap_lets_go(self):
self.marionette.find_element(*self._go_button_locator).tap()
Wait(self.marionette).until(expected.element_not_displayed(*self._ftu_frame_locator))
self.apps.switch_to_displayed_app()
# TODO Some wait for Usage to fully initialize
time.sleep(2)

View file

@ -0,0 +1,108 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.form_controls.binarycontrol import GaiaBinaryControl
class Settings(Base):
_settings_iframe_locator = (By.ID, 'settings-view-placeholder')
_settings_title_locator = (By.CSS_SELECTOR, 'section#settings-view h1')
_data_alert_switch_locator = (By.CSS_SELECTOR, 'gaia-switch[data-option="dataLimit"]')
_when_use_is_above_button_locator = (By.CSS_SELECTOR, 'button[data-widget-type="data-limit"]')
_unit_button_locator = (By.CSS_SELECTOR, '#data-limit-dialog form button')
_size_input_locator = (By.CSS_SELECTOR, '#data-limit-dialog form input')
_usage_done_button_locator = (By.ID, 'data-usage-done-button')
_reset_button_locator = (By.ID, 'reset-data-usage')
_reset_dialog_locator = (By.ID, 'reset-data-dialog')
_reset_wifi_usage_button_locator = (By.ID, 'reset-wifi-data-usage')
_reset_mobile_usage_button_locator = (By.ID, 'reset-mobile-data-usage')
_done_button_locator = (By.ID, 'close-settings')
_confirm_reset_button_locator = (By.CSS_SELECTOR, '#reset-dialog button.danger')
def __init__(self, marionette):
Base.__init__(self, marionette)
self.switch_to_settings_iframe()
def switch_to_settings_iframe(self):
# go into iframe of usage app settings
frame = Wait(self.marionette).until(expected.element_present(
*self._settings_iframe_locator))
Wait(self.marionette).until(expected.element_displayed(frame))
self.marionette.switch_to_frame(frame)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._settings_title_locator))))
def enable_data_alert_switch(self):
GaiaBinaryControl(self.marionette, self._data_alert_switch_locator).enable()
@property
def is_data_alert_switch_checked(self):
return GaiaBinaryControl(self.marionette, self._data_alert_switch_locator).is_checked
def select_when_use_is_above_unit_and_value(self, unit, value):
when_use_is_above_button = self.marionette.find_element(*self._when_use_is_above_button_locator)
Wait(self.marionette).until(lambda m: when_use_is_above_button.get_attribute('disabled') == 'false')
when_use_is_above_button.tap()
current_unit = Wait(self.marionette).until(
expected.element_present(*self._unit_button_locator))
Wait(self.marionette).until(expected.element_displayed(current_unit))
if current_unit.text != unit:
current_unit.tap()
# We need to wait for the javascript to do its stuff
Wait(self.marionette).until(lambda m: current_unit.text == unit)
# clear the original assigned value and set it to the new value
size = Wait(self.marionette).until(expected.element_present(*self._size_input_locator))
Wait(self.marionette).until(expected.element_displayed(size))
size.clear()
size.send_keys(value)
self.marionette.find_element(*self._usage_done_button_locator).tap()
def tap_confirm_reset(self):
confirm_reset_button = Wait(self.marionette).until(
expected.element_present(*self._confirm_reset_button_locator))
Wait(self.marionette).until(expected.element_displayed(confirm_reset_button))
self.tap_element_from_system_app(confirm_reset_button)
# go back to iframe of usage app settings
self.apps.switch_to_displayed_app()
self.switch_to_settings_iframe()
def reset_wifi_usage(self):
self.marionette.find_element(*self._reset_button_locator).tap()
reset_wifi_usage = Wait(self.marionette).until(
expected.element_present(*self._reset_wifi_usage_button_locator))
Wait(self.marionette).until(expected.element_displayed(reset_wifi_usage))
reset_dialog = self.marionette.find_element(*self._reset_dialog_locator)
reset_wifi_usage.tap()
self.tap_confirm_reset()
def reset_mobile_usage(self):
self.marionette.find_element(*self._reset_button_locator).tap()
reset_mobile_usage = Wait(self.marionette).until(
expected.element_present(*self._reset_mobile_usage_button_locator))
Wait(self.marionette).until(expected.element_displayed(reset_mobile_usage))
reset_dialog = self.marionette.find_element(*self._reset_dialog_locator)
reset_mobile_usage.tap()
self.tap_confirm_reset()
def tap_done(self):
done_button = Wait(self.marionette).until(
expected.element_present(*self._done_button_locator))
Wait(self.marionette).until(expected.element_displayed(done_button))
done_button.tap()
# Switch back to Cost Control app frame
self.apps.switch_to_displayed_app()

View file

@ -0,0 +1,318 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
from gaiatest.apps.email.regions.setup import SetupEmail
from gaiatest.apps.email.regions.setup import ManualSetupEmail
from gaiatest.apps.email.regions.settings import Settings
from gaiatest.apps.email.regions.google import GoogleLogin
class Email(Base):
name = 'E-Mail'
_email_locator = (By.CSS_SELECTOR, '#cardContainer .msg-header-item:not([data-index="-1"])')
_syncing_locator = (By.CSS_SELECTOR, '#cardContainer .msg-messages-syncing > .small')
_manual_setup_locator = (By.CSS_SELECTOR, '#cardContainer .sup-manual-config-btn')
_message_list_locator = (By.CSS_SELECTOR, 'cards-message-list')
_setup_account_info = (By.TAG_NAME, 'cards-setup-account-info')
_setup_manual_config = (By.TAG_NAME, 'cards-setup-manual-config')
_folder_picker_locator = (By.TAG_NAME, 'cards-folder-picker')
_settings_main_locator = (By.TAG_NAME, 'cards-settings-main')
_settings_account_locator = (By.TAG_NAME, 'cards-settings-account')
_confirm_dialog_locator = (By.TAG_NAME, 'cards-confirm-dialog')
_refresh_button_locator = (By.CLASS_NAME, 'msg-refresh-btn')
_search_textbox_locator = (By.CSS_SELECTOR, 'form[role="search"]')
_email_subject_locator = (By.XPATH, '//a[@data-index!="-1"]/div/span[text()="%s"]')
_back_button_locator = (By.CLASS_NAME, 'sup-back-btn')
emails_list_header_locator = (By.CSS_SELECTOR, '.msg-list-header')
def basic_setup_email(self, name, email, password):
setup = SetupEmail(self.marionette)
setup.type_name(name)
setup.type_email(email)
setup.tap_next()
google_login = GoogleLogin(self.marionette)
# check if the google autocomplete on email field works as expected
assert google_login.email == email
# dismiss the keyboard and return to the correct frame
google_login.keyboard.dismiss()
google_login.switch_to_frame()
google_login.tap_next()
google_login.type_password(password)
google_login.tap_sign_in()
# approve access to your account
google_login.wait_for_approve_access()
google_login.tap_approve_access()
self.apps.switch_to_displayed_app()
setup.tap_account_prefs_next()
setup.wait_for_setup_complete()
setup.tap_continue()
self.wait_for_message_list()
def setup_IMAP_email(self, imap, smtp):
basic_setup = SetupEmail(self.marionette)
basic_setup.type_name('IMAP account')
basic_setup.type_email(imap['email'])
setup = self.tap_manual_setup()
setup.select_account_type('IMAP+SMTP')
setup.type_imap_hostname(imap['hostname'])
setup.type_imap_name(imap['username'])
setup.type_imap_password(imap['password'])
setup.type_imap_port(imap['port'])
setup.type_smtp_hostname(smtp['hostname'])
setup.type_smtp_name(smtp['username'])
setup.type_smtp_password(smtp['password'])
setup.type_smtp_port(smtp['port'])
setup.tap_next()
setup.check_for_emails_interval('20000')
setup.tap_account_prefs_next()
setup.wait_for_setup_complete()
setup.tap_continue()
self.wait_for_message_list()
def setup_active_sync_email(self, active_sync):
basic_setup = SetupEmail(self.marionette)
basic_setup.type_name('ActiveSync account')
basic_setup.type_email(active_sync['email'])
setup = self.tap_manual_setup()
setup.select_account_type('ActiveSync')
setup.type_password(active_sync['password'])
setup.type_activesync_hostname(active_sync['hostname'])
setup.type_activesync_name(active_sync['username'])
setup.tap_next()
setup.check_for_emails_interval('20000')
setup.tap_account_prefs_next()
setup.wait_for_setup_complete()
setup.tap_continue()
self.wait_for_message_list()
def delete_email_account(self, index):
toolbar = self.header.tap_menu()
toolbar.tap_settings()
settings = Settings(self.marionette)
account_settings = settings.email_accounts[index].tap()
delete_confirmation = account_settings.tap_delete()
delete_confirmation.tap_delete()
def tap_manual_setup(self):
manual_setup = Wait(self.marionette).until(
expected.element_present(*self._manual_setup_locator))
Wait(self.marionette).until(expected.element_displayed(manual_setup))
manual_setup.tap()
return ManualSetupEmail(self.marionette)
def a11y_click_manual_setup(self):
manual_setup = Wait(self.marionette).until(
expected.element_present(*self._manual_setup_locator))
Wait(self.marionette).until(expected.element_displayed(manual_setup))
self.accessibility.click(manual_setup)
return ManualSetupEmail(self.marionette)
def a11y_navigate_to_manual_setup(self, name, email):
setup = SetupEmail(self.marionette)
setup.type_name(name)
setup.type_email(email)
setup = self.a11y_click_manual_setup()
@property
def header(self):
return Header(self.marionette, self.marionette.find_element(*self.emails_list_header_locator))
@property
def toolbar(self):
return ToolBar(self.marionette)
@property
def mails(self):
return [Message(self.marionette, mail) for mail in self.marionette.find_elements(*self._email_locator)]
def wait_for_emails_to_sync(self):
element = self.marionette.find_element(*self._refresh_button_locator)
Wait(self.marionette, timeout=60).until(
lambda m: element.get_attribute(
'data-state') == 'synchronized')
def wait_for_message_list(self):
element = self.marionette.find_element(*self._message_list_locator)
Wait(self.marionette).until(
lambda m: element.is_displayed() and
element.location['x'] == 0)
def wait_for_search_textbox_displayed(self):
Wait(self.marionette).until(expected.element_displayed(*self._search_textbox_locator))
def tap_email_subject(self, subject):
subject_locator = (
self._email_subject_locator[0],
self._email_subject_locator[1] % subject
)
self.marionette.find_element(*subject_locator).tap()
from gaiatest.apps.email.regions.read_email import ReadEmail
return ReadEmail(self.marionette)
def wait_for_email(self, subject, timeout=120):
Wait(self.marionette, timeout, interval=5).until(
self.email_exists(self, subject))
class email_exists(object):
def __init__(self, app, subject):
self.app = app
self.subject = subject
def __call__(self, marionette):
if self.subject in [mail.subject for mail in self.app.mails]:
return True
else:
self.app.toolbar.tap_refresh()
self.app.wait_for_emails_to_sync()
self.app.mails[0].scroll_to_message()
return False
class Header(PageRegion):
_menu_button_locator = (By.CSS_SELECTOR, '.msg-folder-list-btn')
_compose_button_locator = (By.CSS_SELECTOR, '.msg-compose-btn')
_label_locator = (By.CSS_SELECTOR, '.msg-list-header-folder-label.header-label')
def a11y_click_menu(self):
self.accessibility.click(self.root_element.find_element(*self._menu_button_locator))
toolbar = ToolBar(self.marionette)
Wait(self.marionette).until(lambda m: toolbar.is_a11y_visible)
return toolbar
def tap_menu(self):
self.root_element.find_element(*self._menu_button_locator).tap()
toolbar = ToolBar(self.marionette)
Wait(self.marionette).until(lambda m: toolbar.is_visible)
return toolbar
def tap_compose(self):
self.root_element.find_element(*self._compose_button_locator).tap()
from gaiatest.apps.email.regions.new_email import NewEmail
return NewEmail(self.marionette)
@property
def label(self):
return self.root_element.find_element(*self._label_locator).text
@property
def is_menu_visible(self):
return self.is_element_displayed(*self._menu_button_locator)
@property
def is_compose_visible(self):
return self.is_element_displayed(*self._compose_button_locator)
class ToolBar(Base):
_toolbar_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .fld-nav-toolbar')
_refresh_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .msg-refresh-btn')
_search_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .msg-search-btn')
_edit_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .msg-edit-btn')
_settings_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .fld-nav-settings-btn')
_settings_a11y_locator = (By.CSS_SELECTOR,
'#cardContainer .card.center .fld-nav-toolbar.bottom-toolbar')
def tap_refresh(self):
refresh = Wait(self.marionette).until(
expected.element_present(*self._refresh_locator))
Wait(self.marionette).until(expected.element_displayed(refresh))
refresh.tap()
def tap_search(self):
search = Wait(self.marionette).until(
expected.element_present(*self._search_locator))
Wait(self.marionette).until(expected.element_displayed(search))
search.tap()
def tap_edit(self):
edit = Wait(self.marionette).until(
expected.element_present(*self._edit_locator))
Wait(self.marionette).until(expected.element_displayed(edit))
edit.tap()
def a11y_click_settings(self):
settings = Wait(self.marionette).until(
expected.element_present(*self._settings_a11y_locator))
Wait(self.marionette).until(lambda m: self.accessibility.is_visible(settings))
self.accessibility.click(settings)
return Settings(self.marionette)
def tap_settings(self):
settings = Wait(self.marionette).until(
expected.element_present(*self._settings_locator))
Wait(self.marionette).until(expected.element_displayed(settings))
settings.tap()
@property
def is_visible(self):
return self.marionette.find_element(*self._toolbar_locator).location['x'] == 0
@property
def is_a11y_visible(self):
return self.accessibility.is_visible(self.marionette.find_element(*self._toolbar_locator))
@property
def is_refresh_visible(self):
return self.is_element_displayed(*self._refresh_locator)
@property
def is_search_visible(self):
return self.is_element_displayed(*self._search_locator)
@property
def is_edit_visible(self):
return self.is_element_displayed(*self._edit_locator)
@property
def is_settings_visible(self):
return self.is_element_displayed(*self._settings_locator)
class Message(PageRegion):
_subject_locator = (By.CSS_SELECTOR, '.msg-header-subject')
_senders_email_locator = (By.CSS_SELECTOR, '.msg-header-author')
@property
def subject(self):
return self.root_element.find_element(*self._subject_locator).text
@property
def senders_email(self):
return self.root_element.find_element(*self._senders_email_locator).text
def scroll_to_message(self):
self.marionette.execute_script("arguments[0].scrollIntoView(false);", [self.root_element])

View file

@ -0,0 +1,56 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class GoogleLogin(Base):
_iframe_locator = (By.CSS_SELECTOR, "iframe.sup-oauth2-browser[src *= 'google']")
_email_locator = (By.ID, 'Email')
_password_locator = (By.ID, 'Passwd')
_sign_in_locator = (By.ID, 'signIn')
_approve_access_locator = (By.CSS_SELECTOR, '#submit_approve_access.goog-buttonset-action')
_next_locator = (By.ID, 'next')
def __init__(self, marionette):
Base.__init__(self, marionette)
self.switch_to_frame()
# wait for the page to load
email = Wait(self.marionette).until(
expected.element_present(*self._email_locator))
Wait(self.marionette).until(lambda m: email.get_attribute('value') != '')
@property
def email(self):
return self.marionette.find_element(*self._email_locator).get_attribute('value')
def type_password(self, password):
element = Wait(self.marionette).until(expected.element_present(*self._password_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.send_keys(password)
def tap_sign_in(self):
self.marionette.find_element(*self._sign_in_locator).tap()
def tap_next(self):
self.marionette.find_element(*self._next_locator).tap()
def wait_for_approve_access(self):
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._approve_access_locator))))
def tap_approve_access(self):
approve_access = Wait(self.marionette).until(
expected.element_present(*self._approve_access_locator))
Wait(self.marionette).until(expected.element_enabled(approve_access))
approve_access.tap()
def switch_to_frame(self):
# wait for the pop up screen to open
view = Wait(self.marionette).until(
expected.element_present(*self._iframe_locator))
self.marionette.switch_to_frame(view)

View file

@ -0,0 +1,56 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import By, Wait
from gaiatest.apps.base import Base
class NewEmail(Base):
# Write new email
_view_locator = (By.CSS_SELECTOR, '#cardContainer cards-compose')
_to_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .cmp-to-text.cmp-addr-text')
_cc_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .cmp-cc-text.cmp-addr-text')
_bcc_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .cmp-bcc-text.cmp-addr-text')
_subject_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .cmp-subject-text')
_body_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .cmp-body-text')
_send_locator = (By.CSS_SELECTOR, '#cardContainer .card.center .icon.icon-send')
def __init__(self, marionette):
Base.__init__(self, marionette)
view = self.marionette.find_element(*self._view_locator)
Wait(self.marionette).until(lambda m: view.location['x'] == 0)
def type_to(self, value):
self.marionette.find_element(*self._to_locator).tap()
self.keyboard.send(value)
self.keyboard.dismiss()
def type_cc(self, value):
self.marionette.find_element(*self._cc_locator).tap()
self.keyboard.send(value)
self.keyboard.dismiss()
def type_bcc(self, value):
self.marionette.find_element(*self._bcc_locator).tap()
self.keyboard.send(value)
self.keyboard.dismiss()
def type_subject(self, value):
self.marionette.find_element(*self._subject_locator).tap()
self.keyboard.send(value)
self.keyboard.dismiss()
def type_body(self, value):
self.marionette.find_element(*self._body_locator).tap()
self.keyboard.send(value)
self.keyboard.dismiss()
def tap_send(self):
self.marionette.find_element(*self._send_locator).tap()
from gaiatest.apps.email.app import Email
email = Email(self.marionette)
email.wait_for_message_list()
return email

View file

@ -0,0 +1,69 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import By, Wait
from gaiatest.apps.base import Base
class ReadEmail(Base):
manifest_url = '{}email{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL,Base.DEFAULT_APP_HOSTNAME)
_body_locator = (By.CSS_SELECTOR, '.card.center .msg-body-content')
_subject_locator = (By.CSS_SELECTOR, '.card.center .msg-envelope-subject')
_senders_email_locator = (By.CSS_SELECTOR, '.msg-reader-header-label')
_delete_button_locator = (By.CSS_SELECTOR, '.msg-reader-action-toolbar .icon.msg-delete-btn')
_star_button_locator = (By.CSS_SELECTOR, '.msg-reader-action-toolbar .icon.msg-star-btn')
_mark_button_locator = (By.CSS_SELECTOR, '.msg-reader-action-toolbar .icon.msg-mark-read-btn')
_move_button_locator = (By.CSS_SELECTOR, '.msg-reader-action-toolbar .icon.msg-move-btn')
_reply_button_locator = (By.CSS_SELECTOR, '.msg-reader-action-toolbar .icon.msg-reply-btn')
_delete_approve_button_locator = (By.ID, 'msg-delete-ok')
_delete_cancel_button_locator = (By.ID, 'msg-delete-cancel')
_move_cancel_button_locator = (By.CSS_SELECTOR, '.full')
_reply_cancel_button_locator = (By.CSS_SELECTOR, '.msg-reply-menu-cancel')
@property
def body(self):
return self.marionette.find_element(*self._body_locator).text
@property
def subject(self):
return self.marionette.find_element(*self._subject_locator).text
def wait_for_senders_email_displayed(self):
Wait(self.marionette).until(lambda m: m.find_element(*self._senders_email_locator).text != '')
@property
def senders_email(self):
return self.marionette.find_element(*self._senders_email_locator).text
def tap_delete_button(self):
self.marionette.find_element(*self._delete_button_locator).tap()
def tap_star_button(self):
self.marionette.find_element(*self._star_button_locator).tap()
def tap_mark_button(self):
self.marionette.find_element(*self._mark_button_locator).tap()
def tap_move_button(self):
self.marionette.find_element(*self._move_button_locator).tap()
def tap_reply_button(self):
self.marionette.find_element(*self._reply_button_locator).tap()
def approve_delete(self):
self.marionette.find_element(*self._delete_approve_button_locator).tap()
def cancel_delete(self):
self.marionette.find_element(*self._delete_cancel_button_locator).tap()
def cancel_move(self):
self.marionette.find_element(*self._move_cancel_button_locator).tap()
def cancel_reply(self):
self.marionette.find_element(*self._reply_cancel_button_locator).tap()

View file

@ -0,0 +1,73 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
class Settings(Base):
#general email settings
_email_account_locator = (By.CSS_SELECTOR, '.tng-account-item')
_account_add_locator = (By.CLASS_NAME, 'tng-account-add')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._email_account_locator))))
@property
def email_accounts(self):
return [self.Account(self.marionette, email_account) for email_account in self.marionette.find_elements(*self._email_account_locator)]
class Account(PageRegion):
_name_locator = (By.CSS_SELECTOR, 'a.tng-account-item-label')
def tap(self):
self.root_element.tap()
return EmailAccountSettings(self.marionette)
def a11y_click(self):
self.accessibility.click(self.root_element)
return EmailAccountSettings(self.marionette)
class EmailAccountSettings(Base):
#settings for a specific email account
_delete_account_locator = (By.CSS_SELECTOR, '.tng-account-delete')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._delete_account_locator))))
def tap_delete(self):
self.marionette.find_element(*self._delete_account_locator).tap()
return DeleteConfirmation(self.marionette)
def a11y_click_delete(self):
self.accessibility.click(self.marionette.find_element(*self._delete_account_locator))
return DeleteConfirmation(self.marionette)
class DeleteConfirmation(Base):
_delete_locator = (By.CSS_SELECTOR, '.tng-account-delete-confirm #account-delete-ok')
_cancel_locator = (By.CSS_SELECTOR, '.tng-account-delete-confirm #account-delete-cancel')
_message_locator = (By.CSS_SELECTOR, '.tng-account-delete-confirm > section > p')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._delete_locator))))
def tap_delete(self):
self.marionette.find_element(*self._delete_locator).tap()
def a11y_click_delete(self):
self.accessibility.click(self.marionette.find_element(*self._delete_locator))

View file

@ -0,0 +1,201 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class SetupEmail(Base):
_name_locator = (By.CSS_SELECTOR, 'cards-setup-account-info input.sup-info-name')
_email_locator = (By.CSS_SELECTOR, 'cards-setup-account-info input.sup-info-email')
_next_locator = (By.CSS_SELECTOR, '.sup-info-next-btn')
_continue_button_locator = ('class name', 'sup-show-mail-btn sup-form-btn recommend')
_check_for_new_messages_locator = (By.CSS_SELECTOR, '.tng-account-check-interval.mail-select')
_account_prefs_section_locator = (By.CSS_SELECTOR, 'cards-setup-account-prefs section')
_account_prefs_next_locator = (By.CSS_SELECTOR, 'cards-setup-account-prefs .sup-info-next-btn')
_done_section_locator = (By.CSS_SELECTOR, 'cards-setup-done section')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._name_locator))))
def type_name(self, value):
self.marionette.find_element(*self._name_locator).send_keys(value)
def type_email(self, value):
self.marionette.find_element(*self._email_locator).send_keys(value)
def tap_next(self):
self.marionette.find_element(*self._next_locator).tap()
def tap_account_prefs_next(self):
next = Wait(self.marionette, timeout=120).until(
expected.element_present(*self._account_prefs_next_locator))
Wait(self.marionette, timeout=120).until(
expected.element_displayed(next))
next.tap()
def wait_for_setup_complete(self):
element = Wait(self.marionette).until(
expected.element_present(*self._done_section_locator))
Wait(self.marionette).until(lambda m: element.location['x'] == 0)
def tap_continue(self):
self.marionette.find_element(*self._continue_button_locator).tap()
class ManualSetupEmail(Base):
name = 'E-Mail' # hack to be able to use select
_name_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config input.sup-info-name')
_email_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config input.sup-info-email')
_password_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config input.sup-info-password')
_account_type_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-account-type')
_imap_username_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-composite-username')
_imap_password_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-composite-password')
_imap_hostname_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-composite-hostname')
_imap_port_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-composite-port')
_smtp_username_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-smtp-username')
_smtp_password_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-smtp-password')
_smtp_hostname_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-smtp-hostname')
_smtp_port_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-smtp-port')
_activesync_hostname_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-activesync-hostname')
_activesync_username_locator = (By.CSS_SELECTOR, 'cards-setup-manual-config .sup-manual-activesync-username')
_next_locator = (By.CSS_SELECTOR, '.sup-manual-next-btn')
_continue_button_locator = (By.CLASS_NAME, 'sup-show-mail-btn sup-form-btn recommend')
_check_for_new_messages_locator = (By.CSS_SELECTOR, '.tng-account-check-interval.mail-select')
_account_prefs_section_locator = (By.CSS_SELECTOR, 'cards-setup-account-prefs')
_account_prefs_next_locator = (By.CSS_SELECTOR, 'cards-setup-account-prefs .sup-info-next-btn')
_done_section_locator = (By.CSS_SELECTOR, 'cards-setup-done section')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._name_locator))))
def type_name(self, value):
el = self.marionette.find_element(*self._name_locator)
el.clear()
el.send_keys(value)
self.keyboard.dismiss()
def type_email(self, value):
el = self.marionette.find_element(*self._email_locator)
el.clear()
el.send_keys(value)
self.keyboard.dismiss()
def type_password(self, value):
el = self.marionette.find_element(*self._password_locator)
el.clear()
el.send_keys(value)
self.keyboard.dismiss()
def select_account_type(self, value):
account_type = self.marionette.find_element(*self._account_type_locator)
account_type.click()
self.marionette.switch_to_frame()
self.select(value)
def type_imap_name(self, value):
el = self.marionette.find_element(*self._imap_username_locator)
el.clear()
el.send_keys(value)
def type_imap_hostname(self, value):
el = self.marionette.find_element(*self._imap_hostname_locator)
el.clear()
el.send_keys(value)
def type_imap_password(self, value):
el = self.marionette.find_element(*self._imap_password_locator)
el.clear()
el.send_keys(value)
def type_imap_port(self, value):
el = self.marionette.find_element(*self._imap_port_locator)
el.clear()
el.send_keys(value)
def type_smtp_name(self, value):
el = self.marionette.find_element(*self._smtp_username_locator)
el.clear()
el.send_keys(value)
def type_smtp_hostname(self, value):
el = self.marionette.find_element(*self._smtp_hostname_locator)
el.clear()
el.send_keys(value)
def type_smtp_password(self, value):
el = self.marionette.find_element(*self._smtp_password_locator)
el.clear()
el.send_keys(value)
def type_smtp_port(self, value):
el = self.marionette.find_element(*self._smtp_port_locator)
el.clear()
el.send_keys(value)
self.keyboard.dismiss()
def type_activesync_name(self, value):
el = self.marionette.find_element(*self._activesync_username_locator)
el.clear()
el.send_keys(value)
self.keyboard.dismiss()
def type_activesync_hostname(self, value):
el = self.marionette.find_element(*self._activesync_hostname_locator)
el.clear()
el.send_keys(value)
def tap_next(self):
next = Wait(self.marionette).until(expected.element_present(*self._next_locator))
Wait(self.marionette).until(lambda m: next.get_attribute('disabled') != 'true')
next.tap()
account = Wait(self.marionette).until(
expected.element_present(*self._account_prefs_section_locator))
Wait(self.marionette).until(lambda m: account.location['x'] == 0)
Wait(self.marionette, timeout=120).until(expected.element_displayed(
Wait(self.marionette, timeout=120).until(expected.element_present(
*self._account_prefs_next_locator))))
def check_for_emails_interval(self, value):
# The following pref change allows us to check the mail within 1 second or longer,
# rather than the default value of 100 seconds
# The UI data layer of the UI is changed, because the minimum check mail time value is 5 min,
# which is far too long to check for in a test. This allows us to check earlier
with self.marionette.using_context('chrome'):
self.marionette.execute_script(
"Services.prefs.setIntPref('dom.requestSync.minInterval', 1);")
self.marionette.execute_script("""
document.querySelector("[data-l10n-id = settings-check-every-5min]").value = '%s';
""" % value)
self.marionette.find_element(*self._check_for_new_messages_locator).tap()
self.select('Every 5 minutes')
def tap_account_prefs_next(self):
self.marionette.find_element(*self._account_prefs_next_locator).tap()
def wait_for_setup_complete(self):
done = Wait(self.marionette).until(
expected.element_present(*self._done_section_locator))
Wait(self.marionette).until(lambda m: done.location['x'] == 0)
def tap_continue(self):
self.marionette.find_element(*self._continue_button_locator).tap()

View file

@ -0,0 +1,34 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class EmergencyCall(Base):
_emergency_frame_locator = (By.CSS_SELECTOR, ".secureAppWindow.active[data-manifest-name='EmergencyCall'] iframe")
_emergency_dialer_keypad_locator = (By.ID, 'keypad')
_emergency_call_only_title_locator = (By.CSS_SELECTOR, 'h1[data-l10n-id="emergency-call-only"]')
@property
def keypad(self):
from gaiatest.apps.phone.regions.keypad import BaseKeypad
return BaseKeypad(self.marionette)
def switch_to_emergency_call_frame(self):
frame = Wait(self.marionette).until(
expected.element_present(*self._emergency_frame_locator))
Wait(self.marionette).until(expected.element_displayed(frame))
self.marionette.switch_to_frame(frame)
@property
def is_emergency_dialer_keypad_displayed(self):
return self.marionette.find_element(
*self._emergency_dialer_keypad_locator).is_displayed()
@property
def is_emergency_call_only_title_displayed(self):
return self.marionette.find_element(*self._emergency_call_only_title_locator).is_displayed()

View file

@ -0,0 +1,111 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import re
from marionette_driver import expected, By, Wait
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
class FmRadio(Base):
name = 'FM Radio'
manifest_url = '{}fm{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL,Base.DEFAULT_APP_HOSTNAME)
_power_button_locator = (By.ID, 'power-switch')
_favorite_list_locator = (By.CSS_SELECTOR, 'div.fav-list-item')
_frequency_display_locator = (By.ID, 'frequency')
_frequency_dialer_locator = (By.ID, 'dialer-bar')
_favorite_button_locator = (By.ID, 'bookmark-button')
_next_button_locator = (By.ID, 'frequency-op-seekup')
_prev_button_locator = (By.ID, 'frequency-op-seekdown')
_airplane_mode_title_locator = (By.CSS_SELECTOR, 'div[data-l10n-id="airplaneModeHeader"]')
_airplane_mode_text_locator = (By.CSS_SELECTOR, 'div[data-l10n-id="airplaneModeMsg"]')
def launch(self, airplane_mode=False):
Base.launch(self)
power = Wait(self.marionette).until(
expected.element_present(*self._power_button_locator))
if not airplane_mode:
Wait(self.marionette).until(lambda m: power.get_attribute('data-enabled') == 'true')
@property
def airplane_warning_title(self):
return self.marionette.find_element(*self._airplane_mode_title_locator).text
@property
def airplane_warning_text(self):
return self.marionette.find_element(*self._airplane_mode_text_locator).text
def flick_frequency_dialer_up(self):
dialer = self.marionette.find_element(*self._frequency_dialer_locator)
dialer_x_center = int(dialer.size['width'] / 2)
dialer_y_center = int(dialer.size['height'] / 2)
Actions(self.marionette).flick(dialer, dialer_x_center, dialer_y_center, 0, 800, 800).perform()
def tap_next(self):
frequency = Wait(self.marionette).until(
expected.element_present(*self._frequency_display_locator))
current = frequency.text
self.marionette.find_element(*self._next_button_locator).tap()
Wait(self.marionette).until(lambda m: frequency.text != current)
def tap_previous(self):
frequency = Wait(self.marionette).until(
expected.element_present(*self._frequency_display_locator))
current = frequency.text
self.marionette.find_element(*self._prev_button_locator).tap()
Wait(self.marionette).until(lambda m: frequency.text != current)
def tap_power_button(self):
self.marionette.find_element(*self._power_button_locator).tap()
def wait_for_radio_off(self):
power = Wait(self.marionette).until(
expected.element_present(*self._power_button_locator))
Wait(self.marionette).until(
lambda m: not power.get_attribute('data-enabled') == 'true')
def tap_add_favorite(self):
current = len(self.favorite_channels)
self.marionette.find_element(*self._favorite_button_locator).tap()
Wait(self.marionette).until(
lambda m: len(self.favorite_channels) == current + 1)
@property
def is_power_button_on(self):
return self.marionette.find_element(*self._power_button_locator).get_attribute('data-enabled') == 'true'
@property
def frequency(self):
raw_frequency = self.marionette.find_element(*self._frequency_display_locator).text
return float(self._crop_trailing_mhz_and_invisible_characters(raw_frequency))
@staticmethod
def _crop_trailing_mhz_and_invisible_characters(raw_frequency):
match = re.search(r'\d+\.\d', raw_frequency)
frequency = match.group()
return frequency
@property
def favorite_channels(self):
return [self.FavoriteChannel(self.marionette, channel) for channel in self.marionette.find_elements(*self._favorite_list_locator)]
class FavoriteChannel(PageRegion):
_remove_locator = (By.CSS_SELECTOR, 'div.fav-list-remove-button')
_frequency_locator = (By.CSS_SELECTOR, 'div.fav-list-frequency')
@property
def text(self):
raw_frequency = self.root_element.find_element(*self._frequency_locator).text
return float(FmRadio._crop_trailing_mhz_and_invisible_characters(raw_frequency))
def remove(self):
frequency = self.marionette.find_element(*self._frequency_locator)
self.root_element.find_element(*self._remove_locator).tap()
Wait(self.marionette).until(expected.element_not_displayed(frequency))

View file

@ -0,0 +1,465 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import re
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.form_controls.binarycontrol import GaiaBinaryControl
class Ftu(Base):
name = 'FTU'
_next_button_locator = (By.ID, 'forward')
# Step Languages section
_section_languages_locator = (By.ID, 'languages')
_listed_languages_locator = (By.CSS_SELECTOR, "#languages ul li")
_language_locator = (By.CSS_SELECTOR, "#languages ul li[data-value='%s']")
_language_input_locator = (By.CSS_SELECTOR,
"#languages ul li gaia-radio[name='language.current'][value='%s']")
_selected_language_input_locator = (By.CSS_SELECTOR, "#languages ul li gaia-radio:checked")
# Step Cell data section
_section_cell_data_locator = (By.ID, 'data_3g')
_enable_data_checkbox_locator = (By.ID, 'data-connection-switch')
# Step Wifi
_section_wifi_locator = (By.ID, 'wifi')
_found_wifi_networks_locator = (By.CSS_SELECTOR, 'ul#networks-list li')
_password_input_locator = (By.ID, 'wifi_password')
_join_network_locator = (By.ID, 'wifi-join-button')
_loading_overlay_locator = (By.ID, 'loading-overlay')
# Step Date & Time
_section_date_time_locator = (By.ID, 'date_and_time')
_timezone_continent_locator = (By.CSS_SELECTOR, '#time-form li:nth-child(1) > .change.icon.icon-dialog')
_timezone_city_locator = (By.CSS_SELECTOR, '#time-form li:nth-child(2) > .change.icon.icon-dialog')
_time_zone_title_locator = (By.ID, 'time-zone-title')
# Step Geolocation
_section_geolocation_locator = (By.ID, 'geolocation')
_enable_geolocation_checkbox_locator = (By.ID, 'geolocation-switch')
# Section Import contacts
_section_import_contacts_locator = (By.ID, 'import_contacts')
_import_from_sim_locator = (By.ID, 'sim-import-button')
_sim_import_feedback_locator = (By.ID, 'statusMsg')
# Step Firefox Accounts
_section_firefox_accounts_locator = (By.ID, 'firefox_accounts')
# Section late customization
_section_late_customization_locator = (By.ID, 'late_customization')
# Section Welcome Browser
_section_welcome_browser_locator = (By.ID, 'welcome_browser')
_metrics_basic_locator = (By.ID, 'metrics-basic')
_metrics_enhanced_locator = (By.ID, 'metrics-enhanced')
_metrics_none_locator = (By.ID, 'metrics-none')
# Section Privacy Choices
_section_browser_privacy_locator = (By.ID, 'browser_privacy')
_email_field_locator = (By.CSS_SELECTOR, 'input[type="email"]')
# Section Finish
_section_finish_locator = (By.ID, 'finish-screen')
_skip_tour_button_locator = (By.ID, 'skip-tutorial-button')
_take_tour_button_locator = (By.ID, 'lets-go-button')
# Section Tour
_step_header_locator = (By.ID, 'tutorial-step-title')
_tour_next_button_locator = (By.ID, 'forward-tutorial')
_tour_back_button_locator = (By.ID, 'back-tutorial')
# Section Tutorial Finish
_section_tutorial_finish_locator = (By.ID, 'tutorial-finish-tiny')
_lets_go_button_locator = (By.ID, 'tutorialFinished')
# Pattern for import sim contacts message
_pattern_contacts = re.compile("^No contacts detected on SIM to import$|^Imported one contact$|^Imported [0-9]+ contacts$")
_pattern_contacts_0 = re.compile("^No contacts detected on SIM to import$")
_pattern_contacts_1 = re.compile("^Imported one contact$")
_pattern_contacts_N = re.compile("^Imported ([0-9]+) contacts$")
def launch(self):
Base.launch(self)
Wait(self.marionette).until(expected.element_displayed(*self._section_languages_locator))
@property
def languages_list(self):
return len(self.marionette.find_elements(*self._listed_languages_locator))
@property
def selected_language(self):
return self.marionette.find_element(*self._selected_language_input_locator).get_attribute(
'value')
def tap_language(self, language):
self.marionette.find_element(self._language_locator[0], self._language_locator[1] % language).tap()
def a11y_click_language(self, language):
self.accessibility.click(self.marionette.find_element(self._language_input_locator[0],
self._language_input_locator[1] % language))
@property
def is_take_tour_button_visible(self):
return self.is_element_displayed(*self._take_tour_button_locator)
@property
def is_lets_go_button_visible(self):
return self.is_element_displayed(*self._lets_go_button_locator)
def tap_next(self):
next_button = Wait(self.marionette).until(
expected.element_present(*self._next_button_locator))
Wait(self.marionette).until(expected.element_displayed(next_button))
Wait(self.marionette).until(expected.element_enabled(next_button))
# In b2g desktop builds, this sleep prevents intermittent failures
time.sleep(0.2)
next_button.tap()
def a11y_click_next(self):
self.accessibility.click(self.marionette.find_element(*self._next_button_locator))
def tap_next_to_cell_data_section(self):
self.tap_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_cell_data_locator))
def a11y_click_next_to_cell_data_section(self):
self.a11y_click_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_cell_data_locator))
def enable_data(self):
checkbox = Wait(self.marionette).until(
expected.element_present(*self._enable_data_checkbox_locator))
Wait(self.marionette).until(expected.element_displayed(checkbox))
checkbox.tap()
def a11y_enable_data(self):
checkbox = Wait(self.marionette).until(
expected.element_present(*self._enable_data_checkbox_locator))
Wait(self.marionette).until(expected.element_displayed(checkbox))
self.accessibility.click(checkbox)
def tap_next_to_wifi_section(self):
progress = self.marionette.find_element(*self._loading_overlay_locator)
self.tap_next()
Wait(self.marionette).until(expected.element_not_displayed(progress))
Wait(self.marionette).until(expected.element_displayed(*self._section_wifi_locator))
def a11y_click_next_to_wifi_section(self):
self.a11y_click_next()
Wait(self.marionette).until(
expected.element_not_displayed(*self._loading_overlay_locator))
Wait(self.marionette).until(expected.element_displayed(*self._section_wifi_locator))
def wait_for_networks_available(self):
Wait(self.marionette).until(lambda m: len(m.find_elements(
*self._found_wifi_networks_locator)) > 0,
message='No networks listed on screen')
def find_wifi_network(self, network_ssid):
wifi_network_locator = (By.CSS_SELECTOR, '#networks-list li[data-ssid="%s"]' % network_ssid)
wifi_network = Wait(self.marionette).until(
expected.element_present(*wifi_network_locator))
self.marionette.execute_script("arguments[0].scrollIntoView(false);", [wifi_network])
Wait(self.marionette).until(expected.element_displayed(wifi_network))
return wifi_network
def connect_to_wifi(self, network_ssid, password, key_management=None):
wifi_network = self.find_wifi_network(network_ssid)
wifi_network.tap()
# This is in the event we are using a Wifi Network that requires a password
# We cannot be sure of this thus need the logic
if key_management:
password_element = Wait(self.marionette).until(
expected.element_present(*self._password_input_locator))
Wait(self.marionette).until(expected.element_displayed(password_element))
password_element.send_keys(password)
self.marionette.find_element(*self._join_network_locator).tap()
def a11y_connect_to_wifi(self, network_ssid, password, key_management=None):
wifi_network = self.find_wifi_network(network_ssid)
self.accessibility.click(wifi_network)
# This is in the event we are using a Wifi Network that requires a password
# We cannot be sure of this thus need the logic
if key_management:
password_element = Wait(self.marionette).until(
expected.element_present(*self._password_input_locator))
Wait(self.marionette).until(expected.element_displayed(password_element))
password_element.send_keys(password)
self.accessibility.click(self.marionette.find_element(*self._join_network_locator))
def tap_next_to_timezone_section(self):
self.tap_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_date_time_locator))
def a11y_click_next_to_timezone_section(self):
self.a11y_click_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_date_time_locator))
def set_timezone_continent(self, continent):
element = Wait(self.marionette).until(
expected.element_present(*self._timezone_continent_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
self.select(continent)
def a11y_set_timezone_continent(self, continent):
element = Wait(self.marionette).until(
expected.element_present(*self._timezone_continent_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
self.a11y_select(continent)
def set_timezone_city(self, city):
element = Wait(self.marionette).until(
expected.element_present(*self._timezone_city_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
self.select(city)
def a11y_set_timezone_city(self, city):
element = Wait(self.marionette).until(
expected.element_present(*self._timezone_city_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
self.a11y_select(city)
@property
def timezone_title(self):
return self.marionette.find_element(*self._time_zone_title_locator).text
def tap_next_to_geolocation_section(self):
self.tap_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_geolocation_locator))
def a11y_click_next_to_geolocation_section(self):
self.a11y_click_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_geolocation_locator))
def disable_geolocation(self):
self._geolocation_switch.disable()
@property
def is_geolocation_enabled(self):
return self._geolocation_switch.is_checked
@property
def _geolocation_switch(self):
return GaiaBinaryControl(self.marionette, self._enable_geolocation_checkbox_locator)
def a11y_disable_geolocation(self):
element = Wait(self.marionette).until(
expected.element_present(*self._enable_geolocation_checkbox_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
def tap_next_to_import_contacts_section(self):
self.tap_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_import_contacts_locator))
def a11y_click_next_to_import_contacts_section(self):
self.a11y_click_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_import_contacts_locator))
def tap_import_from_sim(self):
self.marionette.find_element(*self._import_from_sim_locator).tap()
def wait_for_contacts_imported(self):
feedback = self.marionette.find_element(
*self._sim_import_feedback_locator)
Wait(self.marionette).until(
lambda m: self._pattern_contacts.match(feedback.text) is not None,
message='Contact did not import from sim before timeout')
@property
def count_imported_contacts(self):
import_sim_message = self.marionette.find_element(*self._sim_import_feedback_locator).text
import_sim_count = None
if self._pattern_contacts_0.match(import_sim_message) is not None:
import_sim_count = 0
elif self._pattern_contacts_1.match(import_sim_message) is not None:
import_sim_count = 1
elif self._pattern_contacts_N.match(import_sim_message) is not None:
count = self._pattern_contacts_N.match(import_sim_message).group(1)
import_sim_count = int(count)
return import_sim_count
def tap_next_to_firefox_accounts_section(self):
self.tap_next()
Wait(self.marionette).until(
expected.element_displayed(*self._section_firefox_accounts_locator))
def a11y_click_next_to_firefox_accounts_section(self):
self.a11y_click_next()
Wait(self.marionette).until(
expected.element_displayed(*self._section_firefox_accounts_locator))
def tap_next_to_late_customization_section(self):
self.tap_next()
Wait(self.marionette).until(
expected.element_displayed(*self._section_late_customization_locator))
def tap_next_to_welcome_browser_section(self):
self.tap_next()
Wait(self.marionette).until(
expected.element_displayed(*self._section_welcome_browser_locator))
def a11y_click_next_to_welcome_browser_section(self):
self.a11y_click_next()
Wait(self.marionette).until(
expected.element_displayed(*self._section_welcome_browser_locator))
@property
def is_metrics_basic_enabled(self):
return self._metrics_basic_radio.is_checked
def enable_metrics_basic(self):
self._metrics_basic_radio.enable()
@property
def _metrics_basic_radio(self):
return GaiaBinaryControl(self.marionette, self._metrics_basic_locator)
@property
def is_metrics_enhanced_enabled(self):
return self._metrics_enhanced_radio.is_checked
def enable_metrics_enhanced(self):
self._metrics_enhanced_radio.enable()
@property
def _metrics_enhanced_radio(self):
return GaiaBinaryControl(self.marionette, self._metrics_enhanced_locator)
@property
def is_metrics_none_enabled(self):
return self._metrics_none_radio.is_checked
def enable_metrics_none(self):
self._metrics_none_radio.enable()
@property
def _metrics_none_radio(self):
return GaiaBinaryControl(self.marionette, self._metrics_none_locator)
def tap_next_to_privacy_browser_section(self):
self.tap_next()
Wait(self.marionette).until(
expected.element_displayed(*self._section_browser_privacy_locator))
def a11y_click_next_to_privacy_browser_section(self):
self.a11y_click_next()
Wait(self.marionette).until(
expected.element_displayed(*self._section_browser_privacy_locator))
def enter_email_address(self, email):
# TODO assert that this is preserved in the system somewhere. Currently it is not used
self.marionette.find_element(*self._email_field_locator).send_keys(email)
self.marionette.switch_to_frame()
Wait(self.marionette).until(lambda m: self.keyboard.is_keyboard_displayed)
self.apps.switch_to_displayed_app()
def tap_next_to_finish_section(self):
self.tap_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_finish_locator))
def a11y_click_next_to_finish_section(self):
self.a11y_click_next()
Wait(self.marionette).until(expected.element_displayed(*self._section_finish_locator))
def tap_skip_tour(self):
element = self.marionette.find_element(*self._skip_tour_button_locator)
self.tap_element_from_system_app(element, add_statusbar_height=True)
def a11y_click_skip_tour(self):
self.accessibility.click(self.marionette.find_element(*self._skip_tour_button_locator))
def run_ftu_setup_with_default_values(self):
count = 0
while not self.is_element_displayed(*self._take_tour_button_locator):
if self.is_element_displayed(*self._next_button_locator):
self.tap_next()
else:
count = count + 1
if count > 5:
break
def tap_take_tour(self):
take_tour = Wait(self.marionette).until(
expected.element_present(*self._take_tour_button_locator))
Wait(self.marionette).until(expected.element_displayed(take_tour))
Wait(self.marionette).until(expected.element_enabled(take_tour))
take_tour.tap()
@property
def step1_header_text(self):
header = Wait(self.marionette).until(
expected.element_present(*self._step_header_locator))
Wait(self.marionette).until(expected.element_displayed(header))
return header.text
def tap_tour_next(self):
next = Wait(self.marionette).until(
expected.element_present(*self._tour_next_button_locator))
Wait(self.marionette).until(expected.element_displayed(next))
Wait(self.marionette).until(expected.element_enabled(next))
next.tap()
def tap_back(self):
back = Wait(self.marionette).until(
expected.element_present(*self._tour_back_button_locator))
Wait(self.marionette).until(expected.element_displayed(back))
back.tap()
@property
def step2_header_text(self):
header = Wait(self.marionette).until(
expected.element_present(*self._step_header_locator))
Wait(self.marionette).until(expected.element_displayed(header))
return header.text
@property
def step3_header_text(self):
header = Wait(self.marionette).until(
expected.element_present(*self._step_header_locator))
Wait(self.marionette).until(expected.element_displayed(header))
return header.text
@property
def step4_header_text(self):
header = Wait(self.marionette).until(
expected.element_present(*self._step_header_locator))
Wait(self.marionette).until(expected.element_displayed(header))
return header.text
@property
def step5_header_text(self):
header = Wait(self.marionette).until(
expected.element_present(*self._step_header_locator))
Wait(self.marionette).until(expected.element_displayed(header))
return header.text
@property
def step6_header_text(self):
header = Wait(self.marionette).until(
expected.element_present(*self._step_header_locator))
Wait(self.marionette).until(expected.element_displayed(header))
return header.text
def wait_for_finish_tutorial_section(self):
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._section_tutorial_finish_locator))))
def tap_lets_go_button(self):
element = self.marionette.find_element(*self._lets_go_button_locator)
self.tap_element_from_system_app(element, add_statusbar_height=True)

View file

@ -0,0 +1,109 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
import gaiatest.apps.camera.app
class Gallery(Base):
name = 'Gallery'
_gallery_items_locator = (By.CSS_SELECTOR, 'div.thumbnail')
_overlay_locator = (By.ID, 'overlay')
_empty_gallery_title_locator = (By.ID, 'overlay-title')
_empty_gallery_text_locator = (By.ID, 'overlay-text')
_progress_bar_locator = (By.ID, 'progress')
_thumbnail_list_view_locator = (By.CSS_SELECTOR, '#thumbnail-views > footer.thumbnails-list')
_switch_to_camera_button_locator = (By.ID, 'thumbnails-camera-button')
_switch_to_multiple_selection_view_locator = (By.ID, 'thumbnails-select-button')
def launch(self, empty=False):
Base.launch(self)
if empty:
self.wait_for_overlay_to_show()
else:
self.wait_for_thumbnail_view_to_load()
def wait_for_thumbnail_view_to_load(self):
Wait(self.marionette).until(expected.element_displayed(*self._thumbnail_list_view_locator))
def wait_for_overlay_to_show(self):
Wait(self.marionette).until(expected.element_displayed(*self._overlay_locator))
def wait_for_files_to_load(self, files_number):
Wait(self.marionette).until(lambda m: m.execute_script(
'return window.wrappedJSObject.files.length;') == files_number)
def wait_for_thumbnails_to_load(self):
Wait(self.marionette).until(expected.element_displayed(*self._gallery_items_locator))
@property
def gallery_items_number(self):
return len(self.thumbnails)
def tap_first_gallery_item(self):
first_gallery_item = self.thumbnails[0]
if self.is_element_displayed(*self._thumbnail_list_view_locator):
from gaiatest.apps.gallery.regions.fullscreen_image import FullscreenImage as NextView
else:
from gaiatest.apps.gallery.regions.crop_view import CropView as NextView
first_gallery_item.tap()
return NextView(self.marionette)
@property
def empty_gallery_title(self):
return self.marionette.find_element(*self._empty_gallery_title_locator).text
@property
def empty_gallery_text(self):
return self.marionette.find_element(*self._empty_gallery_text_locator).text
@property
def are_gallery_items_displayed(self):
return self.is_element_displayed(*self._gallery_items_locator)
@property
def thumbnails(self):
if self.are_gallery_items_displayed:
return [self.Thumbnail(self.marionette, thumbnail, index)
for index, thumbnail in enumerate(self.marionette.find_elements(*self._gallery_items_locator))]
else:
return []
def switch_to_camera(self):
switch_to_camera_button = self.marionette.find_element(*self._switch_to_camera_button_locator)
switch_to_camera_button.tap()
camera_app = gaiatest.apps.camera.app.Camera(self.marionette)
camera_app.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
return camera_app
def switch_to_multiple_selection_view(self):
self.marionette.find_element(*self._switch_to_multiple_selection_view_locator).tap()
from gaiatest.apps.gallery.regions.multiple_selection_view import MultipleSelectionView
return MultipleSelectionView(self.marionette)
class Thumbnail(PageRegion):
def __init__(self, marionette, element, index):
self.root_element = element
# Save the index of the element so we can match it to the js files object
self.index = index
Base.__init__(self, marionette)
@property
def absolute_image_size(self):
'''
The dimensions of the image on disk, is drawn from the image's metadata
'''
return {"height": self.marionette.execute_script('return window.wrappedJSObject.files[%s].metadata.height' % self.index),
"width": self.marionette.execute_script('return window.wrappedJSObject.files[%s].metadata.width' % self.index)}
def tap(self):
self.root_element.tap()

View file

@ -0,0 +1,35 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class CropView(Base):
_src = 'app://gallery.gaiamobile.org/index.html#pick'
_crop_done_button_locator = (By.ID, 'crop-done-button')
_edit_preview_canvas_locator = (By.ID, 'edit-preview-canvas')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._edit_preview_canvas_locator))))
# I have tried waiting for all the elements in the UI, but the crash
# still occurs so the only solution I found was the hardcoded sleep - Bug 1111981
time.sleep(3)
done = self.marionette.find_element(*self._crop_done_button_locator)
Wait(self.marionette).until(expected.element_enabled(done))
def tap_crop_done(self):
element = self.marionette.find_element(*self._crop_done_button_locator)
self.tap_element_from_system_app(element)
# Fall back to the app underneath
Wait(self.marionette).until(lambda m: self.apps.displayed_app.src != self._src)
self.apps.switch_to_displayed_app()

View file

@ -0,0 +1,73 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
class EditPhoto(Base):
_canvas_locator = (By.ID, 'edit-preview-canvas')
_edit_effect_button_locator = (By.ID, 'edit-effect-button')
_effect_options_locator = (By.CSS_SELECTOR, '#edit-effect-options a')
_edit_save_locator = (By.ID, 'edit-save-button')
_edit_crop_button_locator = (By.ID, 'edit-crop-button')
_crop_portrait_locator = (By.ID, 'edit-crop-aspect-portrait')
_save_progress_bar_locator = (By.ID, 'save-progress')
_edit_tool_apply_button_locator = (By.ID, 'edit-tool-apply-button')
def __init__(self, marionette):
Base.__init__(self, marionette)
# <canvas> is dynamically inserted
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._canvas_locator))))
def tap_edit_effects_button(self):
self.marionette.find_element(*self._edit_effect_button_locator).tap()
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._effect_options_locator))))
def tap_edit_crop_button(self):
self.marionette.find_element(*self._edit_crop_button_locator).tap()
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._crop_portrait_locator))))
def tap_edit_tool_apply_button(self):
element = Wait(self.marionette).until(
expected.element_present(*self._edit_tool_apply_button_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
def tap_edit_save_button(self):
element = Wait(self.marionette).until(
expected.element_present(*self._edit_save_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
progress = self.marionette.find_element(*self._save_progress_bar_locator)
Wait(self.marionette).until(expected.element_not_displayed(progress))
from gaiatest.apps.gallery.regions.fullscreen_image import FullscreenImage
return FullscreenImage(self.marionette)
def tap_portrait_crop(self):
element = Wait(self.marionette).until(
expected.element_present(*self._crop_portrait_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
Wait(self.marionette).until(lambda m: 'selected' in element.get_attribute('class'))
@property
def effects(self):
return [self.Effect(marionette=self.marionette, element=effect)
for effect in self.marionette.find_elements(*self._effect_options_locator)]
class Effect(PageRegion):
def tap(self):
self.root_element.tap()
Wait(self.marionette).until(lambda m: 'selected' in self.root_element.get_attribute('class'))

View file

@ -0,0 +1,120 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
class FullscreenImage(Base):
'''
This is not the actual image file - it is a blob of the image file in storage
'''
_current_image_locator = (By.CSS_SELECTOR, '#frames .current > .image-view')
_current_frame_locator = (By.CSS_SELECTOR, '#frames .current')
_photos_toolbar_locator = (By.ID, 'fullscreen-toolbar')
_delete_image_locator = (By.ID, 'fullscreen-delete-button-tiny')
_confirm_delete_locator = (By.ID, 'confirm-ok')
_edit_photo_locator = (By.ID, 'fullscreen-edit-button-tiny')
_go_back_locator = (By.ID, 'fullscreen-back-button-tiny')
_camera_locator = (By.ID, 'fullscreen-camera-button-tiny')
_photo_toolbar_options_locator = (By.CSS_SELECTOR, '#fullscreen-toolbar > a')
# for camera app switch
_loading_screen_locator = (By.CSS_SELECTOR, '.loading-screen')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._current_image_locator))))
Wait(self.marionette).until(lambda m: self.marionette.find_element(*self._current_image_locator).get_attribute('src') != '')
@property
def is_photo_toolbar_displayed(self):
return self.marionette.find_element(*self._photos_toolbar_locator).is_displayed()
@property
def current_image_source(self):
return self.marionette.find_element(*self._current_image_locator).get_attribute('src')
@property
def current_image_size_width(self):
return self.marionette.find_element(*self._current_image_locator).size['width']
@property
def current_image_size_height(self):
return self.marionette.find_element(*self._current_image_locator).size['height']
@property
def current_image_frame(self):
return self.marionette.find_element(*self._current_frame_locator)
def flick_to_next_image(self):
self._flick_to_image('next')
def flick_to_previous_image(self):
self._flick_to_image('previous')
def _flick_to_image(self, direction):
image = self.marionette.find_element(*self._current_image_locator)
action = Actions(self.marionette)
x_start = (image.size['width'] / 100) * (direction == 'next' and 90 or 10)
x_end = (image.size['width'] / 100) * (direction == 'next' and 10 or 90)
y_start = image.size['height'] / 4
y_end = image.size['height'] / 4
action.flick(image, x_start, y_start, x_end, y_end, 200).perform()
Wait(self.marionette).until(
lambda m: abs(image.location['x']) >= image.size['width'])
# Workaround for bug 1161441, the transitionend event is not firing in this
# case with a Marionette flick action, as opposed to a manual flick action
self.marionette.execute_script("""
arguments[0].dispatchEvent(new CustomEvent("transitionend"));
""", [self.current_image_frame])
def tap_delete_button(self):
self.marionette.find_element(*self._delete_image_locator).tap()
Wait(self.marionette).until(expected.element_displayed(*self._confirm_delete_locator))
def tap_confirm_deletion_button(self):
element = self.marionette.find_element(*self._confirm_delete_locator)
element.tap()
Wait(self.marionette).until(expected.element_not_displayed(element))
def tap_edit_button(self):
self.marionette.find_element(*self._edit_photo_locator).tap()
from gaiatest.apps.gallery.regions.edit_photo import EditPhoto
return EditPhoto(self.marionette)
def go_back(self):
element = self.marionette.find_element(*self._go_back_locator)
element.tap()
Wait(self.marionette).until(expected.element_not_displayed(element))
from gaiatest.apps.gallery.app import Gallery
return Gallery(self.marionette)
def tap_switch_to_camera(self):
self.marionette.find_element(*self._camera_locator).tap()
from gaiatest.apps.camera.app import Camera
camera_app = Camera(self.marionette)
camera_app.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
camera_app.wait_for_capture_ready()
Wait(self.marionette).until(expected.element_not_displayed(*self._loading_screen_locator))
return camera_app
def double_tap_image(self):
image = self.marionette.find_element(*self._current_image_locator)
action = Actions(self.marionette)
action.double_tap(image)
action.perform()
@property
def photo_toolbar_width(self):
return self.marionette.find_element(*self._photos_toolbar_locator).rect['width']
@property
def photo_toolbar_options(self):
return self.marionette.find_elements(*self._photo_toolbar_options_locator)

View file

@ -0,0 +1,68 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class MultipleSelectionView(Base):
_select_picture_header_locator = (By.ID, 'thumbnails-number-selected')
_thumbnail_list_view_locator = (By.CSS_SELECTOR, '.thumbnail')
_share_thumbnail_locator = (By.ID, 'thumbnails-share-button')
_delete_thumbnail_locator = (By.ID, 'thumbnails-delete-button')
_delete_confirm_locator = (By.ID, 'confirm-ok')
_delete_cancel_locator = (By.ID, 'confirm-cancel')
def __init__(self, marionette):
Base.__init__(self, marionette)
select_picture_header = Wait(self.marionette).until(
expected.element_present(*self._select_picture_header_locator))
Wait(self.marionette).until(expected.element_displayed(select_picture_header))
def select_first_picture(self):
self.thumbnails[0].tap()
def select_nth_picture(self, n):
assert len(self.thumbnails) > n
self.thumbnails[n].tap()
@property
def number_of_selected_images(self):
select_picture_header = Wait(self.marionette).until(
expected.element_present(*self._select_picture_header_locator))
if 'selected' in select_picture_header.text:
return int(select_picture_header.text[:1])
else:
return 0
@property
def thumbnails(self):
return self.marionette.find_elements(*self._thumbnail_list_view_locator)
def tap_share_button(self):
share_button = Wait(self.marionette).until(
expected.element_present(*self._share_thumbnail_locator))
Wait(self.marionette).until(expected.element_displayed(share_button))
share_button.tap()
from gaiatest.apps.system.regions.activities import Activities
return Activities(self.marionette)
# one can choose to cancel deletion by entering optional parameter
def tap_delete_button(self, confirm=True):
delete_button = Wait(self.marionette).until(
expected.element_present(*self._delete_thumbnail_locator))
Wait(self.marionette).until(expected.element_displayed(delete_button))
delete_button.tap()
if confirm:
confirm_decision_button = Wait(self.marionette).until(
expected.element_present(*self._delete_confirm_locator))
else:
confirm_decision_button = Wait(self.marionette).until(
expected.element_present(*self._delete_cancel_locator))
Wait(self.marionette).until(expected.element_displayed(confirm_decision_button))
confirm_decision_button.tap()

View file

@ -0,0 +1,43 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.form_controls.header import GaiaHeader
class ViewImage(Base):
_header_locator = (By.ID, 'header')
_banner_message_locator = (By.ID, 'message')
_save_image_button_locator = (By.ID, 'save')
_image_locator = (By.CSS_SELECTOR, 'img.image-view')
manifest_url = '{}gallery{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL,Base.DEFAULT_APP_HOSTNAME)
def __init__(self, marionette):
Base.__init__(self, marionette)
from gaiatest.apps.gallery.app import Gallery
Gallery(self.marionette).wait_to_be_displayed()
self.apps.switch_to_displayed_app()
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._image_locator))))
@property
def is_image_visible(self):
return self.is_element_displayed(*self._image_locator)
@property
def banner_message(self):
element = Wait(self.marionette).until(
expected.element_present(*self._banner_message_locator))
Wait(self.marionette).until(expected.element_displayed(element))
return element.text
def tap_back_button(self):
GaiaHeader(self.marionette, self._header_locator).go_back(exit_app=True, app=self)
def tap_save_image(self):
self.marionette.find_element(*self._save_image_button_locator).tap()

View file

@ -0,0 +1,161 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from marionette_driver.marionette import Actions
from marionette_driver.errors import NoSuchElementException
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
from gaiatest.apps.homescreen.regions.bottom_bar import BottomBar
class Homescreen(Base):
name = 'Default Home Screen'
_homescreen_icon_locator = (By.CSS_SELECTOR, 'gaia-grid .icon')
_all_icons_locator = (By.TAG_NAME, 'gaia-app-icon')
_edit_mode_locator = (By.CSS_SELECTOR, 'body.edit-mode')
_search_bar_icon_locator = (By.ID, 'search-input')
_landing_page_locator = (By.ID, 'icons')
_bookmark_icons_locator = (By.CSS_SELECTOR, 'gaia-grid .bookmark')
_divider_locator = (By.CSS_SELECTOR, 'section.divider')
_divider_separator_locator = (By.CSS_SELECTOR, 'section.divider .separator > span')
_exit_edit_mode_locator = (By.ID, 'exit-edit-mode')
_body_dragging_locator = (By.CSS_SELECTOR, 'body.dragging')
_apps_locator = (By.ID, 'apps')
_app_icon_locator = (By.CSS_SELECTOR, 'gaia-app-icon[data-identifier="%s"]')
_scollable_div_locator = (By.CSS_SELECTOR, '#apps-panel div.scrollable')
def wait_for_app_icon_present(self, app_manifest):
Wait(self.marionette, timeout=30).until(lambda m: self.installed_app(app_manifest))
def wait_for_app_icon_not_present(self, app_manifest):
def _app_is_not_found(_):
try:
self.installed_app(app_manifest)
except NoSuchElementException:
return True
return False
Wait(self.marionette).until(_app_is_not_found)
def wait_for_bookmark_icon_not_present(self, bookmark_title):
Wait(self.marionette).until(lambda m: self.bookmark(bookmark_title) is None)
def is_app_installed(self, app_manifest):
"""Checks whether app is installed"""
return self.installed_app(app_manifest) is not None
@property
def is_at_topmost_position(self):
position = self.marionette.execute_script("""
return {x:arguments[0].scrollLeft, y: arguments[0].scrollTop}
""", [self.marionette.find_element(*self._scollable_div_locator)])
return (position['x'] == 0 and position['y'] == 0)
def scroll_to_icon(self, icon_position=0):
app_icon = self.visible_apps[icon_position]
self.marionette.execute_script(
'arguments[0].scrollIntoView(true);', [app_icon.root_element])
def move_app_to_position(self, app_position, to_position):
app = self.app_elements[app_position]
destination_element = self.app_elements[to_position].root_element
app.move_to(destination_element)
@property
def app_elements(self):
return [self.GaiaAppIcon(self.marionette, app_element)
for app_element in self.marionette.find_elements(*self._all_icons_locator)]
@property
def visible_apps(self):
return [homescreen_element for homescreen_element in self.app_elements if homescreen_element.is_displayed]
def wait_for_number_of_apps(self, number_of_apps=1):
Wait(self.marionette).until(lambda m: len(self.app_elements) >= number_of_apps)
def installed_app(self, app_manifest):
apps_container = self.marionette.find_elements(*self._apps_locator)
self.marionette.switch_to_shadow_root(apps_container)
icon_locator = (self._app_icon_locator[0], self._app_icon_locator[1] % app_manifest)
app_icon = self.marionette.find_element(*icon_locator)
return self.GaiaAppIcon(self.marionette, app_icon)
def bookmark(self, bookmark_title):
for root_el in self.marionette.find_elements(*self._bookmark_icons_locator):
if root_el.text == bookmark_title:
return self.GaiaAppIcon(self.marionette, root_el)
@property
def number_of_columns(self):
_icon_container_text = 'div.gaia-container-child:nth-child(%s)'
#first element is always on the first column
_first_icon_locator = (By.CSS_SELECTOR, _icon_container_text % str(1))
# wait until the icons are fully drawn
Wait(self.marionette).until(lambda m: self.marionette.find_element(*_first_icon_locator).rect['x'] != 0)
base_x_axis = self.marionette.find_element(*_first_icon_locator).rect['x']
has_row_changed = False
column = 1
while not has_row_changed:
_icon_locator = (By.CSS_SELECTOR, _icon_container_text % str(column+1))
if self.marionette.find_element(*_icon_locator).rect['x'] == base_x_axis:
has_row_changed = True
else:
column += 1
return column
def delete_app(self, app_manifest):
app = self.installed_app(app_manifest)
app.activate_edit_mode()
return BottomBar(self.marionette).tap_remove()
class GaiaAppIcon(PageRegion):
@property
def manifest_url(self):
return self.root_element.get_attribute('data-identifier')
@property
def is_app_installed(self):
self.marionette.switch_to_shadow_root(self.root_element)
app_installed = self.root_element.get_attribute('data-test-icon-url') == 'app-icon'
self.marionette.switch_to_shadow_root()
return app_installed
@property
def is_displayed(self):
return self.root_element.is_displayed()
def tap_icon(self):
expected_manifest_url = self.manifest_url
self.root_element.tap()
Wait(self.marionette).until(lambda m: self.apps.displayed_app.manifest_url == expected_manifest_url)
self.apps.switch_to_displayed_app()
def activate_edit_mode(self):
self._start_long_press_action().\
release().\
perform()
def move_to(self, html_element):
self._start_long_press_action().\
move(html_element).\
wait(1).\
release().\
wait(1).\
perform()
def _start_long_press_action(self):
self.marionette.execute_script('arguments[0].scrollIntoView(false);', [self.root_element])
return Actions(self.marionette).\
press(self.root_element).\
wait(3)

View file

@ -0,0 +1,48 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class BookmarkMenu(Base):
name = "Bookmark"
manifest_url = '{}bookmark{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL,Base.DEFAULT_APP_HOSTNAME)
# System app - add bookmark to homescreen dialog
_add_bookmark_to_home_screen_frame_locator = (By.CSS_SELECTOR, 'iframe[src^="app://bookmark"][src$="save.html"]')
_add_bookmark_to_home_screen_dialog_button_locator = (By.ID, 'done-button')
_bookmark_title_input_locator = (By.ID, 'bookmark-title')
def __init__(self, marionette):
Base.__init__(self, marionette)
self.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
def tap_add_bookmark_to_home_screen_dialog_button(self):
element = Wait(self.marionette).until(expected.element_present(
*self._add_bookmark_to_home_screen_dialog_button_locator))
Wait(self.marionette).until(expected.element_displayed(element))
# This sleep is necessary for the button to react to the tap call
time.sleep(0.2)
self.tap_element_from_system_app(element)
# Wait for the Add to bookmark frame to be dismissed
self.wait_to_not_be_displayed()
self.apps.switch_to_displayed_app()
def type_bookmark_title(self, value):
element = self.marionette.find_element(
*self._bookmark_title_input_locator)
# Wait for the default value to load into the input field
Wait(self.marionette).until(lambda m: element.get_attribute('value') != '')
element.clear()
self.keyboard.send(value)
self.keyboard.dismiss()

View file

@ -0,0 +1,35 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest import GaiaApps
from gaiatest.apps.base import PageRegion
class BottomBar(PageRegion):
_bottom_bar_locator = (By.ID, 'bottombar')
_remove_locator = (By.ID, 'remove')
_done_locator = (By.ID, 'done')
def __init__(self, marionette):
GaiaApps(marionette).switch_to_displayed_app()
root = Wait(marionette).until(
expected.element_present(*self._bottom_bar_locator))
Wait(marionette).until(expected.element_displayed(root))
window_height = marionette.execute_script('return window.wrappedJSObject.innerHeight')
Wait(marionette).until(lambda m: int(root.rect['y'] + root.size['height']) == window_height)
PageRegion.__init__(self, marionette, root)
def tap_remove(self):
self.root_element.find_element(*self._remove_locator).tap()
from gaiatest.apps.homescreen.regions.confirm_dialog import ConfirmDialog
return ConfirmDialog(self.marionette)
def tap_exit_edit_mode(self):
self.apps.switch_to_displayed_app()
self.root_element = self.marionette.find_element(*self._bottom_bar_locator)
self.root_element.find_element(*self._done_locator).tap()

View file

@ -0,0 +1,27 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.homescreen.regions.bottom_bar import BottomBar
class ConfirmDialog(Base):
_confirm_button_locator = (By.CSS_SELECTOR, 'gaia-confirm .confirm')
def tap_confirm(self, bookmark=False):
# TODO add a good wait here when Bug 1008961 is resolved
time.sleep(1)
if not bookmark:
self.marionette.switch_to_frame()
confirm = Wait(self.marionette).until(expected.element_present(
*self._confirm_button_locator))
Wait(self.marionette).until(expected.element_displayed(confirm))
confirm.tap()
return BottomBar(self.marionette)

View file

@ -0,0 +1,23 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class ConfirmInstall(Base):
_confirm_install_button_locator = (By.ID, 'app-install-install-button')
def tap_confirm(self):
# TODO add a good wait here when Bug 1008961 is resolved
self.marionette.switch_to_frame()
confirm = Wait(self.marionette).until(expected.element_present(
*self._confirm_install_button_locator))
Wait(self.marionette).until(expected.element_displayed(confirm))
confirm.tap()
self.apps.switch_to_displayed_app()

View file

@ -0,0 +1,32 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class PermissionDialog(Base):
_permission_dialog_locator = (By.ID, 'permission-dialog')
_permission_dialog_message_locator = (By.ID, 'permission-message')
_permission_confirm_button_locator = (By.ID, 'permission-yes')
_permission_dismiss_button_locator = (By.ID, 'permission-no')
def wait_for_permission_dialog_displayed(self):
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._permission_dialog_locator))))
@property
def permission_dialog_message(self):
return self.marionette.find_element(*self._permission_dialog_message_locator).text
def tap_to_confirm_permission(self):
self.marionette.find_element(*self._permission_confirm_button_locator).tap()
from gaiatest.apps.system.app import System
return System(self.marionette)
def tap_to_dismiss_permission(self):
self.marionette.find_element(*self._permission_dismiss_button_locator).tap()

View file

@ -0,0 +1,3 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View file

@ -0,0 +1,430 @@
# -*- coding: UTF-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
from gaiatest.apps.system.app import System
class Keyboard(Base):
'''
There are two underlying strategies in this class;
* send() method which uses logic to traverse the keyboard to type the
string sent to it. Send should be used in tests where the layout of the
keyboard is not tested and only string input is important
* tap_x() or anything not send() methods which do not use logic to change
keyboard panels. Tap should be used where the keyboard is expected to
open with that key visible
The methods in this class employ a lot of aggressive frame switching to the keyboard and back to the
displayed app because it predominantly acts as a utility class and thus it works best when the main focus
of the test is on the web app rather than the keyboard itself.
'''
name = "Keyboard"
# special characters look-up table in English standard keyboard
lookup_table = {'0': 'º',
'?': '¿',
'$': '€£¥',
'!': '¡',
'a': 'áàâäåãāæ',
'c': 'çćč',
'e': 'éèêëēę€ɛ',
'i': 'įīîìíï',
'l': '£ł',
'n': 'ńñ',
'o': 'ɵøœōôòóö',
's': 'ßśš$',
'u': 'ūûùúü',
'y': '¥ÿ',
'z': 'žźż',
'A': 'ÁÀÂÄÅÃĀÆ',
'C': 'ÇĆČ',
'E': 'ÉÈÊËĒĘ€Ɛ',
'I': 'ĮĪÎÌÍÏ',
'L': '£Ł',
'N': 'ŃÑ',
'O': 'ƟØŒŌÔÒÓÖ',
'S': 'ŚŠŞ',
'U': 'ŪÛÙÚÜ',
'Y': '¥Ÿ',
'Z': 'ŽŹŻ'}
# keyboard table
keyboard_table = ['english',
'dvorak',
'otherlatins',
'cyrillic',
'arabic',
'hebrew',
'zhuyin',
'pinyin',
'greek',
'japanese',
'portuguese',
'spanish']
# special keys locators
_language_key_locator = (By.CSS_SELECTOR, ".keyboard-type-container[data-active='true'] .keyboard-row button[data-keycode='-3']")
_dotcom_key_locator = (By.CSS_SELECTOR, ".keyboard-row button[data-compositekey='.com']")
_page_switching_key_locator = (By.CSS_SELECTOR, '.keyboard-type-container[data-active] button.keyboard-key[data-target-page="%s"]')
_backspace_key = '8'
_enter_key = '13'
_alt_key = '18'
_upper_case_key = '20'
_space_key = '32'
# keyboard app locators
_keyboard_active_frame_locator = (By.CSS_SELECTOR, '#keyboards .inputWindow.active iframe')
_input_window_locator = (By.CSS_SELECTOR, '#keyboards .inputWindow')
_button_locator = (By.CSS_SELECTOR, '.keyboard-type-container[data-active] button.keyboard-key[data-keycode="%s"], .keyboard-type-container[data-active] button.keyboard-key[data-keycode-upper="%s"]')
_highlight_key_locator = (By.CSS_SELECTOR, '#keyboard-accent-char-menu button')
_predicted_word_locator = (By.CSS_SELECTOR, '.autocorrect')
_candidate_panel_locator = (By.CSS_SELECTOR, '.keyboard-candidate-panel')
_suggestions_container_locator = (By.CSS_SELECTOR, '.suggestions-container')
_dismiss_suggestions_button_locator = (By.CSS_SELECTOR, '.dismiss-suggestions-button')
# find the key to long press and return
def _find_key_for_longpress(self, input_value):
for key_to_press, extended_values in self.lookup_table.iteritems():
if input_value in extended_values:
return key_to_press
# Try to switch to the correct layout. There are 3 keyboard layers:
# Basic (layoutPage = 0), Alternate (layoutPage = 1) and Symbol (layoutPage = 2)
def _switch_to_correct_layout(self, val):
if val.isspace() or val in [',', '.']:
# certain keys are available on every keyboard panel
pass
# Alphabetic keys available on the basic page
elif val.isalpha():
is_upper_case = self._is_upper_case
# If the key to press isalpha and the keyboard layout is not, go back to Basic
if not self._layout_page == 0:
self._tap_page_switching_key(0)
Wait(self.marionette).until(lambda m: self._layout_page == 0)
# If the key to press isupper and the keyboard is not (or vice versa) then press shift
if not val.isupper() == is_upper_case:
self._tap(self._upper_case_key)
Wait(self.marionette).until(lambda m: is_upper_case != self._is_upper_case)
# Numbers and symbols are in other keyboard panels
else:
# If it's not space or alpha then it must be in Alternate or Symbol.
# It can't be in Basic so let's go into Alternate and then try to find it
if self._layout_page == 0 and not self._current_input_type == 'number' and not self._current_input_mode == 'numeric':
self._tap_page_switching_key(1)
page_0_key_locator = (self._page_switching_key_locator[0], self._page_switching_key_locator[1] % (0))
Wait(self.marionette).until(expected.element_displayed(*page_0_key_locator))
# If it is not present here then it must be in the other non-Basic page
# (since we must be in either Alternate or Symbol at this stage)
if not self.is_element_present(*self._key_locator(val)):
if self._layout_page == 1:
self._tap_page_switching_key(2)
page_1_key_locator = (self._page_switching_key_locator[0], self._page_switching_key_locator[1] % (1))
Wait(self.marionette).until(expected.element_displayed(*page_1_key_locator))
else:
self._tap_page_switching_key(1)
page_2_key_locator = (self._page_switching_key_locator[0], self._page_switching_key_locator[1] % (2))
Wait(self.marionette).until(expected.element_displayed(*page_2_key_locator))
@property
def _is_upper_case(self):
return self.marionette.execute_script('return window.wrappedJSObject.app.upperCaseStateManager.isUpperCase;')
@property
def _is_upper_case_locked(self):
return self.marionette.execute_script('return window.wrappedJSObject.app.upperCaseStateManager.isUpperCaseLocked;')
@property
def _current_input_type(self):
return self.marionette.execute_script('return window.wrappedJSObject.app.getBasicInputType();')
@property
def _layout_page(self):
return self.marionette.execute_script('return window.wrappedJSObject.app.layoutManager.currentPageIndex;')
@property
def _current_input_mode(self):
return self.marionette.execute_script('return window.wrappedJSObject.app.inputContext.inputMode;')
# this is to switch to the frame of keyboard
def switch_to_keyboard(self, focus=False):
self.marionette.switch_to_frame()
Wait(self.marionette).until(lambda m: self.is_keyboard_displayed is True)
keybframe = self.marionette.find_element(*self._keyboard_active_frame_locator)
self.marionette.switch_to_frame(keybframe, focus)
@property
def current_keyboard(self):
self.marionette.switch_to_frame()
keyboard = self.marionette.find_element(*self._keyboard_active_frame_locator).get_attribute('data-frame-name')
return keyboard
# this is to get the locator of desired key on keyboard
def _key_locator(self, val):
if len(val) == 1:
val = ord(val)
return (self._button_locator[0], self._button_locator[1] % (val, val))
# this is to tap on desired key on keyboard
def _tap(self, val):
key = Wait(self.marionette).until(expected.element_present(*self._key_locator(val)))
Wait(self.marionette).until(expected.element_displayed(key))
Actions(self.marionette).press(key).release().perform()
# These two tap cases are most important because they cause the keyboard to change state which affects next step
if val.isspace():
# Space switches back to Default layout
Wait(self.marionette).until(lambda m: self._layout_page == 0)
if val.isupper() and not self._is_upper_case_locked:
# Tapping key with shift enabled causes the keyboard to switch back to lower
Wait(self.marionette).until(lambda m: not self._is_upper_case)
def _tap_page_switching_key(self, val):
locator = (self._page_switching_key_locator[0], self._page_switching_key_locator[1] % val)
key = Wait(self.marionette).until(expected.element_present(*locator))
Wait(self.marionette).until(expected.element_displayed(key))
Actions(self.marionette).press(key).release().perform()
# This is for selecting special characters after long pressing
# "selection" is the nth special element you want to select (n>=1)
def choose_extended_character(self, long_press_key, selection, movement=True):
self.switch_to_keyboard()
action = Actions(self.marionette)
# after switching to correct keyboard, set long press if the key is there
self._switch_to_correct_layout(long_press_key)
key = Wait(self.marionette).until(expected.element_present(*self._key_locator(long_press_key)))
Wait(self.marionette).until(expected.element_displayed(key))
action.press(key).wait(1).perform()
# find the extended key and perform the action chain
extend_keys = self.marionette.find_elements(*self._highlight_key_locator)
if movement is True:
action.move(extend_keys[selection - 1]).perform()
action.release().perform()
self.apps.switch_to_displayed_app()
def enable_caps_lock(self):
self.switch_to_keyboard()
key_obj = self.marionette.find_element(*self._key_locator(self._upper_case_key))
self.marionette.double_tap(key_obj)
self.apps.switch_to_displayed_app()
# this would go through fastest way to tap/click through a string
def send(self, string):
self.switch_to_keyboard()
for val in string:
if ord(val) > 127:
# this would get the right key to long press and switch to the right keyboard
middle_key_val = self._find_key_for_longpress(val.encode('UTF-8'))
self._switch_to_correct_layout(middle_key_val)
# find the key to long press and press it to get the extended characters list
middle_key = self.marionette.find_element(*self._key_locator(middle_key_val))
action = Actions(self.marionette)
action.press(middle_key).wait(1).perform()
# find the targeted extended key to send
key = Wait(self.marionette).until(
expected.element_present(*self._key_locator(val)))
Wait(self.marionette).until(expected.element_displayed(key))
action.move(key).release().perform()
else:
# after switching to correct keyboard, tap/click if the key is there
self._switch_to_correct_layout(val)
self._tap(val)
# when we tap on '@' the layout switches to the default keyboard - Bug 996332
if val == '@':
Wait(self.marionette).until(
lambda m: self._layout_page == 0)
self.apps.switch_to_displayed_app()
# Switch keyboard language
# Mapping of language code => {
# "ar":"ﺎﻠﻋﺮﺒﻳﺓ",
# "cz":"Česká",
# "de":"Deutsch",
# "el":"Greek"
# "en":"English",
# "en-Dvorak":"Dvorak",
# "es":"Español",
# "fr":"français",
# "he":"עִבְרִית",
# "nb":"Norsk",
# "pt_BR":"Português",
# "pl":"polski",
# "ru":"русский",
# "sk":"Slovenčina",
# "sr-Cyrl":"српска ћирилица",
# "sr-Latn":"srpski",
# "tr":"Türkçe"}
def switch_keyboard_language(self, lang_code):
# TODO At the moment this doesn't work because the UI has changed
# An attempted repair ran into https://bugzilla.mozilla.org/show_bug.cgi?id=779284 (Modal dialog)
keyboard_language_locator = (By.CSS_SELECTOR, ".keyboard-row button[data-keyboard='%s']" % lang_code)
self.switch_to_keyboard()
language_key = self.marionette.find_element(*self._language_key_locator)
action = Actions(self.marionette)
action.press(language_key).wait(1).perform()
target_kb_layout = self.marionette.find_element(*keyboard_language_locator)
action.move(target_kb_layout).release().perform()
self.apps.switch_to_displayed_app()
def tap_keyboard_language_key(self):
self.switch_to_keyboard()
key = Wait(self.marionette).until(
expected.element_present(*self._language_key_locator))
Wait(self.marionette).until(expected.element_displayed(key))
key.tap()
self.apps.switch_to_displayed_app()
# following are "5 functions" to substitute finish switch_to_frame()s and tap() for you
def tap_shift(self):
self.switch_to_keyboard()
self._tap(self._upper_case_key)
self.apps.switch_to_displayed_app()
def tap_backspace(self):
self.switch_to_keyboard()
backspace = self.marionette.find_element(self._button_locator[0], self._button_locator[1] % (self._backspace_key, self._backspace_key))
backspace.tap()
self.apps.switch_to_displayed_app()
def tap_space(self):
self.switch_to_keyboard()
self._tap(self._space_key)
self.apps.switch_to_displayed_app()
def tap_enter(self):
self.switch_to_keyboard()
self._tap(self._enter_key)
self.apps.switch_to_displayed_app()
def tap_dotcom(self):
self.switch_to_keyboard()
dotcom = self.marionette.find_element(*self._dotcom_key_locator)
dotcom.tap()
self.apps.switch_to_displayed_app()
def dismiss(self):
# Make sure that keyboard is focused, otherwise dismissing it doesn't work
self.switch_to_keyboard(focus=True)
self.marionette.switch_to_frame()
# navigator.mozKeyboard is needed for v1.3 support
self.marionette.execute_script("""
var keyboard = navigator.mozKeyboard || navigator.mozInputMethod;
keyboard.removeFocus();""")
Wait(self.marionette).until(lambda m: self.is_keyboard_displayed is False)
self.apps.switch_to_displayed_app()
def tap_first_predictive_word(self):
self.switch_to_keyboard()
key = Wait(self.marionette).until(
expected.element_present(*self._predicted_word_locator))
Wait(self.marionette).until(expected.element_displayed(key))
key.tap()
self.apps.switch_to_displayed_app()
def tap_suggestion(self, word):
self.switch_to_keyboard()
# find the requested suggestion
selector = (By.CSS_SELECTOR, '.suggestions-container span[data-data=\"%s\"]' % word)
key = Wait(self.marionette).until(expected.element_present(*selector))
Wait(self.marionette).until(expected.element_displayed(key))
key.tap()
self.apps.switch_to_displayed_app()
# Accessibility related properties and methods
def _a11y_get_role(self, locator_args):
element = Wait(self.marionette).until(
expected.element_present(*locator_args))
Wait(self.marionette).until(expected.element_displayed(element))
return self.accessibility.get_role(element)
def _a11y_get_name(self, locator_args):
element = Wait(self.marionette).until(
expected.element_present(*locator_args))
Wait(self.marionette).until(expected.element_displayed(element))
return self.accessibility.get_name(element)
@property
def a11y_first_predictive_word_name(self):
return self._a11y_get_name(self._predicted_word_locator)
@property
def a11y_first_predictive_word_role(self):
return self._a11y_get_role(self._predicted_word_locator)
@property
def a11y_candidate_panel_name(self):
return self._a11y_get_name(self._candidate_panel_locator)
@property
def a11y_suggestions_container_role(self):
return self._a11y_get_role(self._suggestions_container_locator)
@property
def a11y_dismiss_suggestions_button_role(self):
return self._a11y_get_role(self._dismiss_suggestions_button_locator)
@property
def a11y_dismiss_suggestions_button_name(self):
return self._a11y_get_name(self._dismiss_suggestions_button_locator)
@property
def a11y_enter_key_role(self):
return self._a11y_get_role(self._key_locator(self._enter_key))
@property
def a11y_enter_key_name(self):
return self._a11y_get_name(self._key_locator(self._enter_key))
@property
def a11y_space_key_role(self):
return self._a11y_get_role(self._key_locator(self._space_key))
@property
def a11y_space_key_name(self):
return self._a11y_get_name(self._key_locator(self._space_key))
@property
def a11y_backspace_key_name(self):
return self._a11y_get_name([self._button_locator[0],
self._button_locator[1] % (self._backspace_key, self._backspace_key)])
@property
def a11y_backspace_key_role(self):
return self._a11y_get_role([self._button_locator[0],
self._button_locator[1] % (self._backspace_key, self._backspace_key)])
def a11y_first_predictive_word_click(self):
self.switch_to_keyboard()
element = Wait(self.marionette).until(
expected.element_present(*self._predicted_word_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
self.apps.switch_to_displayed_app()
@property
def is_keyboard_displayed(self):
input_window = self.marionette.find_element(*self._input_window_locator)
# if we have software buttons, keyboard's y will not be 0 but the minus height of the button container.
expected_y = -System(self.marionette).software_buttons_height
return (input_window.is_displayed() and (int(input_window.location['y']) == expected_y))

View file

@ -0,0 +1,140 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from datetime import datetime
from marionette_driver import expected, By, Wait
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
from gaiatest.apps.homescreen.app import Homescreen
from gaiatest.apps.camera.app import Camera
from gaiatest.apps.lockscreen.regions.passcode_pad import PasscodePad
class LockScreen(Base):
_lockscreen_window_locator = (By.CLASS_NAME, 'lockScreenWindow')
_lockscreen_locator = (By.ID, 'lockscreen')
_lockscreen_handle_locator = (By.ID, 'lockscreen-area-slide')
_lockscreen_passcode_code_locator = (By.ID, 'lockscreen-passcode-code')
_lockscreen_passcode_pad_locator = (By.ID, 'lockscreen-passcode-pad')
_unlock_button_locator = (By.ID, 'lockscreen-area-unlock')
_camera_button_locator = (By.ID, 'lockscreen-area-camera')
_lockscreen_container_locator = (By.ID, 'notifications-lockscreen-container')
_notification_locator = (By.CSS_SELECTOR, '#notifications-lockscreen-container > div.notification')
_time_locator = (By.ID, 'lockscreen-clock-time')
def switch_to_frame(self):
# XXX: Because we're not in frame yet. LockScreen team now is
# trying hard to do decoupling & as-iframe at the same time,
# but iframe now stuck at weird test failures, so the team decide
# to land decoupling part first, with some dummy functions that
# can be modified later to fit the implementation.
#
# If we finished to make it as an iframe, to this to switch
# to the real frame:
#
# self.marionette.switch_to_frame(
# self.marionette.find_element(*self._lockscreen_frame_locator));
#
# But now we're not ready to do that yet.
self.marionette.switch_to_frame()
@property
def time(self):
# The lockscreen doesn't display AM/PM since 2.0. It's still present behind a hidden span though.
lockscreen_time = self.marionette.execute_script(
"return document.getElementById('%s').innerHTML" % self._time_locator[1])
return lockscreen_time.replace('<span>', '').replace('</span>', '')
@property
def time_in_datetime(self):
return datetime.strptime(self.time, '%I:%M %p')
def unlock(self):
self._slide_to_unlock('homescreen')
self.wait_for_lockscreen_not_visible()
return Homescreen(self.marionette)
def unlock_to_camera(self):
Wait(self.marionette).until(expected.element_displayed(*self._lockscreen_handle_locator))
self._slide_to_unlock('camera')
return Camera(self.marionette)
def unlock_to_emergency_call(self):
passcode_pad = self.unlock_to_passcode_pad()
return passcode_pad.tap_emergency_call()
def unlock_to_passcode_pad(self):
Wait(self.marionette).until(expected.element_displayed(*self._lockscreen_handle_locator))
self._slide_to_unlock('homescreen')
Wait(self.marionette).until(
expected.element_displayed(*self._lockscreen_passcode_code_locator))
return PasscodePad(self.marionette)
def unlock_to_homescreen_using_passcode(self, passcode):
passcode_pad = self.unlock_to_passcode_pad()
homescreen = passcode_pad.type_passcode(passcode)
self.wait_for_lockscreen_not_visible()
return homescreen
def _slide_to_unlock(self, destination):
lockscreen_handle = self.marionette.find_element(*self._lockscreen_handle_locator)
lockscreen_handle_x_centre = int(lockscreen_handle.size['width'] / 2)
lockscreen_handle_y_centre = int(lockscreen_handle.size['height'] / 2)
handle_destination = lockscreen_handle.size['width']
if destination == 'camera':
handle_destination *= -1
# Flick lockscreen handle to the destination
Actions(self.marionette).flick(
lockscreen_handle, lockscreen_handle_x_centre, lockscreen_handle_y_centre, handle_destination, 0
).perform()
def wait_for_lockscreen_not_visible(self):
Wait(self.marionette).until(expected.element_not_displayed(*self._lockscreen_locator))
def wait_for_notification(self):
Wait(self.marionette).until(expected.element_displayed(*self._notification_locator))
def a11y_click_unlock_button(self):
self.accessibility.click(self.marionette.find_element(*self._unlock_button_locator))
return Homescreen(self.marionette)
def a11y_click_camera_button(self):
self.accessibility.click(self.marionette.find_element(*self._camera_button_locator))
return Camera(self.marionette)
@property
def notifications(self):
container = self.marionette.find_element(*self._lockscreen_container_locator)
# Avoid the search timeout when there are no notifications
if not container.is_displayed() or container.text == '':
return []
return [Notification(self.marionette, element)
for element in self.marionette.find_elements(*self._notification_locator)]
class Notification(PageRegion):
_body_locator = (By.CSS_SELECTOR, 'div.detail')
_title_locator = (By.CSS_SELECTOR, 'div.title')
@property
def is_visible(self):
return self.root_element.is_displayed()
@property
def content(self):
return self.root_element.find_element(*self._body_locator).text
@property
def title(self):
return self.root_element.find_element(*self._title_locator).text

View file

@ -0,0 +1,42 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import By, Wait
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
from gaiatest.apps.homescreen.app import Homescreen
from gaiatest.apps.emergency_call.app import EmergencyCall
class PasscodePad(Base):
_lockscreen_passcode_code_locator = (By.ID, 'lockscreen-passcode-code')
_lockscreen_passcode_pad_locator = (By.ID, 'lockscreen-passcode-pad')
_numeric_button_locator = (By.CSS_SELECTOR, '#lockscreen-passcode-pad a[data-key="%s"]')
_emergency_button_locator = (By.CSS_SELECTOR, '#lockscreen-passcode-pad a[data-key="e"]')
def __init__(self, marionette):
Base.__init__(self, marionette)
lockscreen_passcode_pad = self.marionette.find_element(*self._lockscreen_passcode_pad_locator)
emergency_button = self.marionette.find_element(*self._emergency_button_locator)
# wait button * 4 rows === the pad's height
Wait(self.marionette).until(
lambda m: lockscreen_passcode_pad.size['height'] ==
(4 * emergency_button.size['height']))
def type_passcode(self, passcode):
for digit in passcode:
button_locator = (self._numeric_button_locator[0],
self._numeric_button_locator[1] % digit)
# Workaround for bug 1203269 where the Marionette tap() method somehow enters
# 2 digits in the passcode pad
Actions(self.marionette).press(
self.marionette.find_element(*button_locator)).wait(time=0.5).release().perform()
return Homescreen(self.marionette)
def tap_emergency_call(self):
self.marionette.find_element(*self._emergency_button_locator).tap()
emergency_call = EmergencyCall(self.marionette)
return emergency_call

View file

@ -0,0 +1,122 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from marionette_driver.keys import Keys
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
from gaiatest.apps.homescreen.regions.confirm_install import ConfirmInstall
from gaiatest.apps.system.app import System
class Marketplace(Base):
_loading_fragment_locator = (By.ID, 'splash-overlay')
_search_locator = (By.ID, 'search-q')
_filter_locator = (By.ID, 'compat-filter')
_marketplace_iframe_locator = (By.CSS_SELECTOR, 'iframe[src*="marketplace"]')
_search_toggle_locator = (By.CLASS_NAME, 'mkt-search-btn')
name = 'Marketplace'
manifest_url = 'https://marketplace.firefox.com/packaged.webapp'
def _switch_to_app(self):
iframe = Wait(self.marionette).until(
expected.element_present(*self._marketplace_iframe_locator))
Wait(self.marionette).until(expected.element_displayed(iframe))
self.marionette.switch_to_frame(iframe)
def search(self, term):
self._switch_to_app()
# This sleep seems necessary, otherwise on device we get timeout failure on display search_box sometimes, see bug 1136791
import time
time.sleep(10)
search_toggle = Wait(self.marionette).until(
expected.element_present(*self._search_toggle_locator))
Wait(self.marionette).until(expected.element_displayed(search_toggle))
search_toggle.tap()
search_box = Wait(self.marionette).until(
expected.element_present(*self._search_locator))
Wait(self.marionette).until(expected.element_displayed(search_box))
# This sleep is necessary, otherwise the search results are not shown on desktop b2g
import time
time.sleep(0.5)
# search for the app
search_box.send_keys(term)
search_box.send_keys(Keys.RETURN)
return SearchResults(self.marionette)
def get_current_displayed_result(self):
self._switch_to_app()
return SearchResults(self.marionette)
class SearchResults(Base):
_search_results_loading_locator = (By.CSS_SELECTOR, '.loading')
_search_result_locator = (By.CSS_SELECTOR, '#search-results li.item')
@property
def search_results(self):
results = Wait(self.marionette).until(
lambda m: m.find_elements(*self._search_result_locator))
Wait(self.marionette).until(expected.element_displayed(results[0]))
return [Result(self.marionette, result) for result in results]
class Result(PageRegion):
_install_button_locator = (By.CSS_SELECTOR, '.button.install')
_name_locator = (By.CSS_SELECTOR, 'h3[itemprop="name"]')
def tap_install_button(self):
self.root_element.find_element(*self._install_button_locator).tap()
self.marionette.switch_to_frame()
def tap_open_app_button(self, _app_locator):
button = self.root_element.find_element(*self._install_button_locator)
Wait(self.marionette).until(lambda m: button.text == 'Open app')
button.tap()
self.apps.switch_to_displayed_app()
app_element = self.marionette.find_element(*_app_locator)
Wait(self.marionette).until(lambda m: app_element.is_displayed())
def get_app_name(self):
return self.root_element.find_element(*self._name_locator).text
class MarketplaceDev(Marketplace):
name = 'Dev'
manifest_url = 'https://marketplace.firefox.com/app/bee10fbb-7829-4d99-8a9f-a6d0a905138e/manifest.webapp'
def install_addon(self, addon_name):
# Shortcut, go directly to the Addon page
self.marionette.navigate('https://marketplace-dev.allizom.org/addon/%s' % addon_name)
addon_page = AddonPage(self.marionette)
addon_page.install_link()
class AddonPage(Base):
_addon_install_locator = (By.CSS_SELECTOR, 'button.install')
def install_link(self):
element = Wait(self.marionette, timeout=20, interval=1).until(
expected.element_present(*self._addon_install_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
confirm_install = ConfirmInstall(self.marionette)
confirm_install.tap_confirm()
system = System(self.marionette)
system.wait_for_system_banner_displayed()
system.wait_for_system_banner_not_displayed()
self.apps.switch_to_displayed_app()

View file

@ -0,0 +1,122 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
class Messages(Base):
name = 'Messages'
manifest_url = '{}sms{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL, Base.DEFAULT_APP_HOSTNAME)
_create_new_message_locator = (By.ID, 'threads-composer-link')
_first_message_locator = (By.ID, 'thread-1')
_messages_frame_locator = (By.CSS_SELECTOR, 'iframe[data-url*=sms]')
_settings_icon_locator = (By.ID, 'threads-settings-button')
_app_ready_locator = (By.CLASS_NAME, 'js-app-ready')
_thread_items_locator = (By.CSS_SELECTOR, 'li.threadlist-item')
_draft_message_locator = (By.CSS_SELECTOR, 'li.draft')
_thread_options_button = (By.ID, 'threads-options-button')
_delete_thread_button = (By.ID, 'threads-delete-button')
_edit_thread_header = (By.ID, 'threads-edit-header')
_banner_locator = (By.ID, 'threads-draft-saved-banner')
def launch(self):
Base.launch(self)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._app_ready_locator))))
def create_new_message(self, recipients, message):
new_message = self.tap_create_new_message()
for recipient in recipients:
new_message.type_phone_number(recipient)
new_message.type_message(message)
return new_message
def tap_create_new_message(self):
self.marionette.find_element(*self._create_new_message_locator).tap()
from gaiatest.apps.messages.regions.new_message import NewMessage
return NewMessage(self.marionette)
def tap_settings(self):
self.marionette.find_element(*self._settings_icon_locator).tap()
from gaiatest.apps.settings.app import Settings
Settings(self.marionette).wait_to_be_displayed()
self.apps.switch_to_displayed_app()
from gaiatest.apps.messages.regions.messaging_settings import MessagingSettings
return MessagingSettings(self.marionette)
def wait_for_message_list(self):
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._create_new_message_locator))))
def wait_for_message_received(self, timeout=180):
Wait(self.marionette, timeout).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._first_message_locator))))
def tap_first_received_message(self):
self.marionette.find_element(*self._first_message_locator).tap()
from gaiatest.apps.messages.regions.message_thread import MessageThread
return MessageThread(self.marionette)
def tap_thread_options(self):
self.marionette.find_element(*self._thread_options_button).tap()
from gaiatest.apps.messages.regions.options import ThreadOptions
return ThreadOptions(self.marionette)
def enter_select_mode(self):
thread_options = self.tap_thread_options()
thread_options.select_threads()
self.wait_for_header_to_fully_appear()
def wait_for_header_to_fully_appear(self):
edit_header = self.marionette.find_element(*self._edit_thread_header)
Wait(self.marionette).until(lambda m: edit_header.is_displayed() and edit_header.rect['y'] == 0)
@property
def is_in_select_mode(self):
return self.marionette.find_element(*self._edit_thread_header).is_displayed()
def delete_selection(self):
confirm_panel = self.tap_delete_button()
confirm_panel.confirm_delete()
def tap_delete_button(self):
self.marionette.find_element(*self._delete_thread_button).tap()
from gaiatest.apps.messages.regions.options import ConfirmDeletionOptions
return ConfirmDeletionOptions(self.marionette)
def wait_for_banner_to_hide(self):
Wait(self.marionette).until(expected.element_not_displayed(*self._banner_locator))
@property
def threads(self):
return [self.Thread(self.marionette, message)
for message in self.marionette.find_elements(*self._thread_items_locator)]
@property
def draft_threads(self):
return [self.Thread(self.marionette, draft_message)
for draft_message in self.marionette.find_elements(*self._draft_message_locator)]
class Thread(PageRegion):
_draft_icon_locator = (By.CSS_SELECTOR, '.icon-draft[data-l10n-id="is-draft"]')
@property
def is_draft_icon_displayed(self):
return self.root_element.find_element(*self._draft_icon_locator).is_displayed()
def open(self):
self.root_element.tap()
from gaiatest.apps.messages.regions.new_message import NewMessage
return NewMessage(self.marionette)
def choose(self):
self.root_element.tap()

View file

@ -0,0 +1,78 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class Activities(Base):
_actions_menu_locator = (By.CSS_SELECTOR, 'body > form[data-type="action"]')
_settings_button_locator = (By.XPATH, '//*[text()="Settings"]')
_add_subject_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="add-subject"]')
_add_to_contact_button_locator = (By.XPATH, '//*[text()="Add to an existing contact"]')
_create_new_contact_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="createNewContact"]')
_forward_message_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="forward"]')
_delete_message_button_locator = (By.CSS_SELECTOR, 'form[data-type="action"] button[data-l10n-id="delete"]')
_confirm_delete_message_locator = (By.CSS_SELECTOR, 'button.danger')
_view_message_report_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="view-message-report"]')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._actions_menu_locator))))
# TODO Difficult intermittent bug 977052
time.sleep(1)
def tap_report(self):
self.marionette.find_element(*self._view_message_report_locator).tap()
from gaiatest.apps.messages.regions.report import Report
return Report(self.marionette)
def tap_settings(self):
self.marionette.find_element(*self._settings_button_locator).tap()
from gaiatest.apps.Settings.app import Settings
Settings(self.marionette).wait_to_be_displayed()
self.apps.switch_to_displayed_app()
from gaiatest.apps.messages.regions.messaging_settings import MessagingSettings
return MessagingSettings(self.marionette)
def tap_add_subject(self):
self.marionette.find_element(*self._add_subject_button_locator).tap()
def tap_add_to_contact(self):
self.marionette.find_element(*self._add_to_contact_button_locator).tap()
from gaiatest.apps.contacts.app import Contacts
contacts = Contacts(self.marionette)
contacts.switch_to_contacts_frame()
return contacts
def tap_create_new_contact(self):
self.marionette.find_element(*self._create_new_contact_button_locator).tap()
from gaiatest.apps.contacts.regions.contact_form import NewContact
new_contact = NewContact(self.marionette)
new_contact.switch_to_new_contact_form()
return new_contact
def tap_forward_message(self):
self.marionette.find_element(*self._forward_message_button_locator).tap()
from gaiatest.apps.messages.regions.new_message import NewMessage
return NewMessage(self.marionette)
def tap_delete_message(self):
delete_message_button = self.marionette.find_element(*self._delete_message_button_locator)
Wait(self.marionette).until(expected.element_displayed(delete_message_button))
delete_message_button.tap()
def confirm_delete_message(self):
confirm_delete_message = self.marionette.find_element(*self._confirm_delete_message_locator)
Wait(self.marionette).until(expected.element_displayed(confirm_delete_message))
confirm_delete_message.tap()
Wait(self.marionette).until(expected.element_not_displayed(confirm_delete_message))

View file

@ -0,0 +1,33 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class AttachmentOptions(Base):
_attachment_options_locator = (By.CSS_SELECTOR, 'form.visible[role="dialog"][data-subtype="menu"]')
_view_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="view-attachment-image"]')
_cancel_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="cancel"]')
def __init__(self, marionette):
Base.__init__(self, marionette)
root_element = self.root_element
Wait(self.marionette).until(lambda m: root_element.location['y'] == 0)
def tap_cancel(self):
root_element = self.root_element
root_element.find_element(*self._cancel_button_locator).tap()
Wait(self.marionette).until(expected.element_not_displayed(root_element))
def tap_view_button(self):
self.root_element.find_element(*self._view_button_locator).tap()
from gaiatest.apps.gallery.regions.view_image import ViewImage
return ViewImage(self.marionette)
@property
def root_element(self):
return self.marionette.find_element(*self._attachment_options_locator)

View file

@ -0,0 +1,115 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from marionette_driver.marionette import Actions
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
from gaiatest.apps.messages.app import Messages
class MessageThread(Base):
_all_messages_locator = (By.CSS_SELECTOR, '#messages-container li.message')
_received_message_content_locator = (By.CSS_SELECTOR, "#messages-container li.message.received")
_sent_message_locator = (By.CSS_SELECTOR, '#messages-container li.message.sent')
_header_link_locator = (By.ID, 'messages-header')
_message_header_locator = (By.ID, 'messages-header-text')
_call_button_locator = (By.ID, 'messages-call-number-button')
def wait_for_received_messages(self, interval=5, timeout=300):
Wait(self.marionette, timeout).until(expected.element_displayed(
Wait(self.marionette, timeout).until(expected.element_present(
*self._received_message_content_locator))))
def tap_back_button(self):
# In a message thread, tap the back button to return to main message list
# TODO: remove tap with coordinates after Bug 1061698 is fixed
self.marionette.find_element(*self._header_link_locator).tap(25, 25)
messages = Messages(self.marionette)
messages.wait_for_message_list()
return messages
@property
def received_messages(self):
return [Message(self.marionette, message) for message in self.marionette.find_elements(*self._received_message_content_locator)]
@property
def sent_messages(self):
return [Message(self.marionette, message) for message in self.marionette.find_elements(*self._sent_message_locator)]
@property
def all_messages(self):
return [Message(self.marionette, message) for message in self.marionette.find_elements(*self._all_messages_locator)]
@property
def header_text(self):
header = Wait(self.marionette).until(
expected.element_present(*self._message_header_locator))
Wait(self.marionette).until(expected.element_displayed(header))
return header.text
def tap_header(self):
element = self.marionette.find_element(*self._header_link_locator)
Wait(self.marionette).until(lambda m: element.location['x'] == 0)
self.marionette.find_element(*self._message_header_locator).tap()
from gaiatest.apps.messages.regions.activities import Activities
return Activities(self.marionette)
def tap_call(self):
call = Wait(self.marionette).until(expected.element_present(*self._call_button_locator))
Wait(self.marionette).until(expected.element_displayed(call))
call.tap()
from gaiatest.apps.phone.regions.keypad import Keypad
keypad = Keypad(self.marionette)
keypad.wait_for_phone_number_ready()
return keypad
class Message(PageRegion):
_text_locator = (By.CSS_SELECTOR, '.bubble p')
_attachments_locator = (By.CSS_SELECTOR, '.bubble .attachment-container.preview')
_message_section_locator = (By.CSS_SELECTOR, '.bubble')
def open_report(self):
activity = self.long_press_message()
return activity.tap_report()
@property
def text(self):
return self.root_element.find_element(*self._text_locator).text
@property
def has_attachments(self):
try:
self.root_element.find_element(*self._attachments_locator)
except:
return False
return True
def tap_attachment(self):
self.root_element.find_element(*self._attachments_locator).tap()
from gaiatest.apps.gallery.regions.view_image import ViewImage
return ViewImage(self.marionette)
@property
def id(self):
return self.root_element.get_attribute('id')
def long_press_message(self):
message = self.root_element.find_element(*self._message_section_locator)
Actions(self.marionette).\
press(message).\
wait(3).\
release().\
perform()
from gaiatest.apps.messages.regions.activities import Activities
return Activities(self.marionette)

View file

@ -0,0 +1,21 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class MessagingSettings(Base):
_messaging_settings_locator = (By.CSS_SELECTOR, "h1[data-l10n-id='messagingSettings-header']")
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(
*self._messaging_settings_locator))))
def is_messaging_settings_displayed(self):
return self.is_element_displayed(*self._messaging_settings_locator)

View file

@ -0,0 +1,156 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.contacts.app import Contacts
from gaiatest.apps.messages.app import Messages
from gaiatest.form_controls.header import GaiaHeader
class NewMessage(Messages):
_recipient_section_locator = (By.ID, 'messages-recipients-list')
_receiver_input_locator = (By.CSS_SELECTOR, '#messages-recipients-list span.recipient')
_add_recipient_button_locator = (By.ID, 'messages-contact-pick-button')
_message_field_locator = (By.ID, 'messages-input')
_send_message_button_locator = (By.ID, 'messages-send-button')
_attach_button_locator = (By.ID, 'messages-attach-button')
_options_button_locator = (By.ID, 'messages-options-button')
_message_sending_locator = (By.CSS_SELECTOR, "li.message.outgoing.sending")
_panel_conversationview_locator = (By.CSS_SELECTOR, '.panel-ConversationView')
_message_resize_notice_locator = (By.ID, 'messages-resize-notice')
_subject_input_locator = (By.CSS_SELECTOR, '.subject-composer-input')
_image_attachment_locator = (By.CSS_SELECTOR, '.attachment-container.preview')
_recipients_locator = (By.CSS_SELECTOR, '#messages-recipients-list span')
_header_locator = (By.ID, 'messages-header')
def __init__(self, marionette):
Base.__init__(self, marionette)
self.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
element = Wait(self.marionette).until(
expected.element_present(*self._panel_conversationview_locator))
Wait(self.marionette, interval=1).until(
lambda m: element.rect['x'] == 0 and element.is_displayed())
def type_phone_number(self, value):
# tap on the parent element to activate editable
self.marionette.find_element(*self._recipient_section_locator).tap()
self.keyboard.send(value)
def type_message(self, value):
# change the focus to the message field to enable the send button
self.tap_message()
self.keyboard.send(value)
def tap_message(self):
message = Wait(self.marionette).until(
expected.element_present(*self._message_field_locator))
Wait(self.marionette).until(expected.element_displayed(message))
message.tap()
def tap_image_attachment(self):
self.marionette.find_element(*self._image_attachment_locator).tap()
from gaiatest.apps.messages.regions.attachment_options import AttachmentOptions
return AttachmentOptions(self.marionette)
def tap_send(self, timeout=120):
send = Wait(self.marionette).until(
expected.element_present(*self._send_message_button_locator))
Wait(self.marionette).until(expected.element_enabled(send))
send.tap()
Wait(self.marionette, timeout=timeout).until(
expected.element_not_present(*self._message_sending_locator))
from gaiatest.apps.messages.regions.message_thread import MessageThread
return MessageThread(self.marionette)
def tap_attachment(self):
self.marionette.find_element(*self._attach_button_locator).tap()
from gaiatest.apps.system.regions.activities import Activities
return Activities(self.marionette)
def tap_add_recipient(self):
self.marionette.find_element(*self._add_recipient_button_locator).tap()
contacts_app = Contacts(self.marionette)
contacts_app.switch_to_contacts_frame()
return contacts_app
def tap_options(self):
self.marionette.find_element(*self._options_button_locator).tap()
from gaiatest.apps.messages.regions.activities import Activities
return Activities(self.marionette)
def wait_for_recipients_displayed(self):
Wait(self.marionette).until(expected.element_displayed(*self._receiver_input_locator))
def wait_for_resizing_to_finish(self):
Wait(self.marionette).until(
expected.element_not_displayed(*self._message_resize_notice_locator))
def wait_for_subject_input_displayed(self):
Wait(self.marionette).until(expected.element_displayed(*self._subject_input_locator))
def wait_for_message_input_displayed(self):
Wait(self.marionette).until(expected.element_displayed(*self._message_field_locator))
@property
def first_recipient_name(self):
element = Wait(self.marionette).until(
expected.element_present(*self._receiver_input_locator))
Wait(self.marionette).until(expected.element_displayed(element))
return element.text
@property
def number_of_recipients(self):
# we need to subtract one as the last element is the current editable element
return len(self.marionette.find_elements(*self._receiver_input_locator)) - 1
@property
def recipient_css_class(self):
return self.marionette.find_element(*self._receiver_input_locator).get_attribute('class')
@property
def is_recipient_name_editable(self):
return self.marionette.find_element(*self._receiver_input_locator).get_attribute('contenteditable')
@property
def first_recipient_number_attribute(self):
return self.marionette.find_element(*self._receiver_input_locator).get_attribute('data-number')
def tap_recipient_section(self):
self.marionette.find_element(*self._recipient_section_locator).tap()
from gaiatest.apps.keyboard.app import Keyboard
return Keyboard(self.marionette)
@property
def is_send_button_enabled(self):
return self.marionette.find_element(*self._send_message_button_locator).is_enabled()
@property
def message(self):
return self.marionette.find_element(*self._message_field_locator).text
def tap_recipient_name(self):
self.marionette.find_element(*self._receiver_input_locator).tap()
@property
def recipients(self):
return self.marionette.find_elements(*self._recipients_locator)
@property
def has_attachment(self):
return self.is_element_displayed(*self._image_attachment_locator)
def save_as_draft(self):
draft_options = self.go_back()
messages = draft_options.save()
messages.wait_for_banner_to_hide()
return messages
def go_back(self):
GaiaHeader(self.marionette, self._header_locator).go_back()
from gaiatest.apps.messages.regions.options import DraftOptions
return DraftOptions(self.marionette)

View file

@ -0,0 +1,52 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import PageRegion
class SmsOptions(PageRegion):
_root_locator = (By.CSS_SELECTOR, 'form.visible')
def __init__(self, marionette):
root = marionette.find_element(*self._root_locator)
PageRegion.__init__(self, marionette, root)
self.wait_for_panel_to_appear()
def wait_for_panel_to_appear(self):
Wait(self.marionette).until(lambda m: self.root_element.is_displayed() and self.root_element.rect['y'] == 0)
def wait_for_panel_to_hide(self):
Wait(self.marionette).until(expected.element_not_displayed(self.root_element))
def _tap_and_wait_until_hidden(self, locator):
self.root_element.find_element(*locator).tap()
self.wait_for_panel_to_hide()
from gaiatest.apps.messages.app import Messages
return Messages(self.marionette)
class ThreadOptions(SmsOptions):
_select_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="selectThreads-label"]')
def select_threads(self):
return self._tap_and_wait_until_hidden(self._select_button_locator)
class DraftOptions(SmsOptions):
_save_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="save-as-draft"]')
def save(self):
return self._tap_and_wait_until_hidden(self._save_button_locator)
class ConfirmDeletionOptions(SmsOptions):
_confirm_delete_button_locator = (By.CSS_SELECTOR, 'button[data-l10n-id="delete"]')
def confirm_delete(self):
return self._tap_and_wait_until_hidden(self._confirm_delete_button_locator)

View file

@ -0,0 +1,43 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.form_controls.header import GaiaHeader
class Report(Base):
_text_type = (By.CSS_SELECTOR, 'p[data-l10n-id="message-type-sms"]')
_time_sent_locator = (By.CSS_SELECTOR, 'span.sent-timestamp')
_time_received_locator = (By.CSS_SELECTOR, 'span.received-timestamp')
_header_locator = (By.ID, 'information-report-header')
_sender_locator = (By.CSS_SELECTOR, 'span[data-l10n-id="report-from-title"] ~ .contact-list .js-contact-info')
_receiver_locator = (By.CSS_SELECTOR, 'span[data-l10n-id="report-to-title"] ~ .contact-list .js-contact-info')
def close(self):
GaiaHeader(self.marionette, self._header_locator).go_back()
from gaiatest.apps.messages.regions.message_thread import MessageThread
return MessageThread(self.marionette)
def is_message_an_sms(self):
return self.marionette.find_element(*self._text_type).get_attribute('data-l10n-id') == 'message-type-sms'
@property
def sent_date(self):
timestamp_in_milliseconds = self.marionette.find_element(*self._time_sent_locator).get_attribute('data-l10n-date')
return ((int)(timestamp_in_milliseconds) // 1000)
@property
def received_date(self):
timestamp_in_milliseconds = self.marionette.find_element(*self._time_received_locator).get_attribute('data-l10n-date')
return ((int)(timestamp_in_milliseconds) // 1000)
@property
def sender(self):
return self.marionette.find_element(*self._sender_locator).text
@property
def receiver(self):
return self.marionette.find_element(*self._receiver_locator).text

View file

@ -0,0 +1,93 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.music.regions.list_view import AlbumsView, ArtistsView, SongsView
class Music(Base):
name = 'Music'
manifest_url = '{}music{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL, Base.DEFAULT_APP_HOSTNAME)
_loading_spinner_locator = (By.ID, 'spinner-overlay')
_active_view_locator = (By.CSS_SELECTOR, 'iframe.active[src*="/views/home/index.html"]')
_music_tiles_locator = (By.ID, 'tiles')
_views_locator = (By.ID, 'views')
_tabs_locator = (By.ID, 'tabs')
_empty_overlay_shadow_DOM_locator = (By.ID, 'empty-overlay')
_empty_music_title_locator = (By.CSS_SELECTOR, '[data-l10n-id="empty-title"]')
_empty_music_text_locator = (By.CSS_SELECTOR, '[data-l10n-id="empty-text"]')
_albums_tab_locator = (By.CSS_SELECTOR, '[data-l10n-id="albums-tab"]')
_songs_tab_locator = (By.CSS_SELECTOR, '[data-l10n-id="songs-tab"]')
_artists_tab_locator = (By.CSS_SELECTOR, '[data-l10n-id="artists-tab"]')
_title_locator = (By.ID, 'header-title')
def launch(self):
Base.launch(self)
Wait(self.marionette).until(expected.element_not_displayed(
*self._loading_spinner_locator))
def wait_for_music_tiles_displayed(self):
Wait(self.marionette).until(expected.element_displayed(*self._active_view_locator))
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
Wait(self.marionette).until(expected.element_displayed(*self._music_tiles_locator))
self.apps.switch_to_displayed_app()
def wait_for_empty_message_to_load(self):
self.marionette.switch_to_shadow_root(self.marionette.find_element(*self._empty_overlay_shadow_DOM_locator))
Wait(self.marionette).until(lambda m: self.empty_music_title != '')
Wait(self.marionette).until(lambda m: self.empty_music_text != '')
self.apps.switch_to_displayed_app()
def wait_for_view_displayed(self, view_name):
title = self.marionette.find_element(*self._title_locator)
Wait(self.marionette).until(expected.element_displayed(title))
Wait(self.marionette).until(lambda m: title.text == view_name)
@property
def empty_music_title(self):
return self.marionette.find_element(*self._empty_music_title_locator).text
@property
def empty_music_text(self):
return self.marionette.find_element(*self._empty_music_text_locator).text
@property
def views(self):
return self.marionette.find_element(*self._views_locator)
@property
def tabs(self):
return self.marionette.find_element(*self._tabs_locator)
def tap_albums_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._albums_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return AlbumsView(self.marionette)
def tap_songs_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._songs_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return SongsView(self.marionette)
def tap_artists_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._artists_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return ArtistsView(self.marionette)
def a11y_click_albums_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._albums_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
return AlbumsView(self.marionette)

View file

@ -0,0 +1,80 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base, PageRegion
from gaiatest.apps.music.regions.sublist_view import AlbumSublistView, ArtistSublistView
from gaiatest.apps.music.regions.player_view import PlayerView
class ListView(Base):
_list_item_locator = (By.CLASS_NAME, 'gfl-item')
def _set_active_view_locator(self, type):
self._active_view_locator = (By.CSS_SELECTOR, 'iframe.active[src*="/views/{}/index.html"]'.format(type))
@property
def media(self):
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
elements = Wait(self.marionette).until(
expected.elements_present(*self._list_item_locator))
Wait(self.marionette).until(expected.element_displayed(elements[0]))
self.apps.switch_to_displayed_app()
return [Media(self.marionette, element, self._active_view_locator) for element in elements]
class AlbumsView(ListView):
def __init__(self, marionette):
ListView.__init__(self, marionette)
self._set_active_view_locator('albums')
class ArtistsView(ListView):
def __init__(self, marionette):
ListView.__init__(self, marionette)
self._set_active_view_locator('artists')
class SongsView(ListView):
def __init__(self, marionette):
ListView.__init__(self, marionette)
self._set_active_view_locator('songs')
class Media(PageRegion):
_first_media_link_locator = (By.CLASS_NAME, 'gfl-item first')
def __init__(self, marionette, element, _active_view_locator):
PageRegion.__init__(self, marionette, element)
self._active_view_locator = _active_view_locator
def switch_to_active_view(self):
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
def tap_first_album(self):
self.switch_to_active_view()
self.marionette.find_element(*self._first_media_link_locator).tap()
self.apps.switch_to_displayed_app()
return AlbumSublistView(self.marionette)
def tap_first_song(self):
self.switch_to_active_view()
self.marionette.find_element(*self._first_media_link_locator).tap()
self.apps.switch_to_displayed_app()
return PlayerView(self.marionette)
def tap_first_artist(self):
self.switch_to_active_view()
self.marionette.find_element(*self._first_media_link_locator).tap()
self.apps.switch_to_displayed_app()
return ArtistSublistView(self.marionette)
def a11y_click_first_album(self):
self.switch_to_active_view()
self.accessibility.click(
self.marionette.find_element(*self._first_media_link_locator))
self.apps.switch_to_displayed_app()
return AlbumSublistView(self.marionette)

View file

@ -0,0 +1,109 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import time
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
class PlayerView(Base):
_audio_locator = (By.ID, 'audio')
_active_view_locator = (By.CSS_SELECTOR, 'iframe.active[src*="/views/player/index.html"]')
_player_seek_elapsed_locator = (By.ID, 'elapsed-time')
_player_controls_shadow_dom_locator = (By.ID, 'controls')
_player_controls_play_locator = (By.ID, 'toggle')
_player_controls_forward_locator = (By.ID, 'next')
_cover_image_shadow_dom_locator = (By.ID, 'artwork')
_seek_bar_shadow_dom_locator = (By.ID, 'seek-bar')
_cover_share_locator = (By.CSS_SELECTOR, '[data-l10n-id="share-song"]')
_rating_view_locator = (By.ID, 'rating')
_stars_on_locator = (By.CSS_SELECTOR, '.rating-star.star-on')
def __init__(self, marionette):
Base.__init__(self, marionette)
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
element = self.marionette.find_element(*self._cover_image_shadow_dom_locator)
Wait(self.marionette).until(lambda m: element.rect['x'] == 0 and element.is_displayed())
self.apps.switch_to_displayed_app()
def tap_play(self):
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
self.marionette.switch_to_shadow_root(self.marionette.find_element(*self._player_controls_shadow_dom_locator))
self.marionette.find_element(*self._player_controls_play_locator).tap()
self.apps.switch_to_displayed_app()
def tap_forward(self):
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
self.marionette.switch_to_shadow_root(self.marionette.find_element(*self._player_controls_shadow_dom_locator))
self.marionette.find_element(*self._player_controls_forward_locator).tap()
self.apps.switch_to_displayed_app()
def tap_cover_in_player_view(self):
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
Wait(self.marionette).until(expected.element_not_displayed(*self._rating_view_locator))
self.marionette.switch_to_shadow_root()
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()
def tap_share_button(self):
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))
self.marionette.find_element(*self._cover_share_locator).tap()
from gaiatest.apps.system.regions.activities import Activities
return Activities(self.marionette)
def _get_star_locator(self, rating):
return By.CSS_SELECTOR, 'button[value="%s"]' % rating
def tap_star(self, rate):
"""
give rating. (After tapping the cover to make the ratings overlay appear)
"""
self.tap_cover_in_player_view()
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))
self.marionette.switch_to_shadow_root(self.marionette.find_element(*self._rating_view_locator))
Wait(self.marionette).until(expected.element_displayed(*self._get_star_locator(rate)))
self.marionette.find_element(*self._get_star_locator(rate)).tap()
self.apps.switch_to_displayed_app()
@property
def star_rating(self):
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))
rating = self.marionette.find_element(*self._rating_view_locator).get_attribute('value')
self.apps.switch_to_displayed_app()
return int(rating)
@property
def player_elapsed_time(self):
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
self.marionette.switch_to_shadow_root(self.marionette.find_element(*self._seek_bar_shadow_dom_locator))
Wait(self.marionette).until(expected.element_displayed(
Wait(self.marionette).until(expected.element_present(*self._player_seek_elapsed_locator))))
elapsed_time = time.strptime(self.marionette.find_element(*self._player_seek_elapsed_locator).text, '%M:%S')
self.apps.switch_to_displayed_app()
return elapsed_time
def is_player_playing(self):
# get 4 timestamps during approx. 1 sec
# ensure that newer timestamp has greater value than previous one
timestamps = []
for i in range(4):
timestamps.append(self.player_current_timestamp)
time.sleep(.25)
return all([timestamps[i - 1] < timestamps[i] for i in range(1, 3)])
@property
def player_current_timestamp(self):
player = self.marionette.find_element(*self._audio_locator)
return float(player.get_attribute('currentTime'))

View file

@ -0,0 +1,49 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.music.regions.player_view import PlayerView
class SublistView(Base):
_list_locator = (By.ID, 'list')
_first_song_locator = (By.CLASS_NAME, 'gfl-item first')
def _set_active_view(self, type):
self._active_view_locator = (By.CSS_SELECTOR, 'iframe.active[src*="/views/{}-detail/index.html"]'.format(type))
def switch_to_active_view(self):
Wait(self.marionette).until(expected.element_displayed(*self._active_view_locator))
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
def wait_sublist_view_draw(self):
self.switch_to_active_view()
element = self.marionette.find_element(*self._list_locator)
Wait(self.marionette).until(lambda m: element.rect['x'] == 0 and element.is_displayed())
self.apps.switch_to_displayed_app()
def tap_first_song(self):
self.switch_to_active_view()
song = Wait(self.marionette).until(
expected.element_present(*self._first_song_locator))
song.tap()
self.apps.switch_to_displayed_app()
return PlayerView(self.marionette)
class AlbumSublistView(SublistView):
def __init__(self, marionette):
Base.__init__(self, marionette)
self._set_active_view('album')
self.wait_sublist_view_draw()
class ArtistSublistView(SublistView):
def __init__(self, marionette):
Base.__init__(self, marionette)
self._set_active_view('artist')
self.wait_sublist_view_draw()

View file

@ -0,0 +1,29 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.music.regions.player_view import PlayerView
class TileView(Base):
_active_view_locator = (By.CSS_SELECTOR, 'iframe.active[src*="/views/home/index.html"]')
_tile_group_locator = (By.ID, 'tiles')
def __init__(self, marionette):
Base.__init__(self, marionette)
# there are two type of tiles - main and sub. There should be at least one main tile
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
Wait(self.marionette).until(expected.element_displayed(*self._tile_group_locator))
self.apps.switch_to_displayed_app()
def tap_song(self, filename):
self.marionette.switch_to_frame(self.marionette.find_element(*self._active_view_locator))
_pick_tile_locator = (By.CSS_SELECTOR, '.tile[data-file-path$="{}"]'.format(filename))
self.marionette.find_element(*_pick_tile_locator).tap()
self.apps.switch_to_displayed_app()
return PlayerView(self.marionette)

View file

@ -0,0 +1,88 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base
from gaiatest.apps.music_oga.regions.list_view import ListView
class Music(Base):
name = 'Music OGA'
manifest_url = '{}music-oga{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL, Base.DEFAULT_APP_HOSTNAME)
_loading_spinner_locator = (By.ID, 'spinner-overlay')
_music_tiles_locator = (By.CSS_SELECTOR, '#views-tiles-anchor > div')
_views_locator = (By.ID, 'views')
_tabs_locator = (By.ID, 'tabs')
_empty_music_title_locator = (By.ID, 'overlay-title')
_empty_music_text_locator = (By.ID, 'overlay-text')
_albums_tab_locator = (By.ID, 'tabs-albums')
_songs_tab_locator = (By.ID, 'tabs-songs')
_artists_tab_locator = (By.ID, 'tabs-artists')
_title_locator = (By.ID, 'title-text')
def launch(self):
Base.launch(self)
Wait(self.marionette).until(expected.element_not_displayed(
*self._loading_spinner_locator))
def wait_for_music_tiles_displayed(self):
Wait(self.marionette).until(expected.element_displayed(
*self._music_tiles_locator))
def wait_for_empty_message_to_load(self):
element = self.marionette.find_element(*self._empty_music_title_locator)
Wait(self.marionette).until(lambda m: not element.text == '')
def wait_for_view_displayed(self, view_name):
title = self.marionette.find_element(*self._title_locator)
Wait(self.marionette).until(expected.element_displayed(title))
Wait(self.marionette).until(lambda m: title.text == view_name)
@property
def empty_music_title(self):
return self.marionette.find_element(*self._empty_music_title_locator).text
@property
def empty_music_text(self):
return self.marionette.find_element(*self._empty_music_text_locator).text
@property
def views(self):
return self.marionette.find_element(*self._views_locator)
@property
def tabs(self):
return self.marionette.find_element(*self._tabs_locator)
def tap_albums_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._albums_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return ListView(self.marionette)
def tap_songs_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._songs_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return ListView(self.marionette)
def tap_artists_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._artists_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return ListView(self.marionette)
def a11y_click_albums_tab(self):
element = Wait(self.marionette).until(
expected.element_present(*self._albums_tab_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
return ListView(self.marionette)

View file

@ -0,0 +1,50 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from marionette_driver import expected, By, Wait
from gaiatest.apps.base import Base, PageRegion
from gaiatest.apps.music_oga.regions.sublist_view import SublistView
from gaiatest.apps.music_oga.regions.player_view import PlayerView
class ListView(Base):
_view_locator = (By.ID, 'views')
_list_item_locator = (By.CSS_SELECTOR, '.list-item')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(
lambda m: self.marionette.find_element(*self._view_locator).get_attribute('class') != 'scrolling')
@property
def media(self):
elements = Wait(self.marionette).until(
expected.elements_present(*self._list_item_locator))
Wait(self.marionette).until(expected.element_displayed(elements[0]))
return [Media(self.marionette, element) for element in elements]
class Media(PageRegion):
_media_link_locator = (By.TAG_NAME, 'a')
def tap_first_album(self):
self.marionette.find_element(*self._media_link_locator).tap()
return SublistView(self.marionette)
def tap_first_song(self):
self.marionette.find_element(*self._media_link_locator).tap()
return PlayerView(self.marionette)
def tap_first_artist(self):
self.marionette.find_element(*self._media_link_locator).tap()
return SublistView(self.marionette)
def a11y_click_first_album(self):
self.accessibility.click(
self.marionette.find_element(*self._media_link_locator))
return SublistView(self.marionette)

Some files were not shown because too many files have changed in this diff Show more