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