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,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