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,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 gaiatest.apps.base import Base
class Phone(Base):
name = "Phone"
entry_point = "dialer"
manifest_url = '{}communications{}/manifest.webapp'.format(Base.DEFAULT_PROTOCOL,Base.DEFAULT_APP_HOSTNAME)
_dialog_locator = (By.ID, 'confirmation-message')
_dialog_title_locator = (By.XPATH, "//*[@id='confirmation-message']/section/h1")
_call_log_toolbar_button_locator = (By.ID, 'option-recents')
_call_log_toolbar_locator = (By.ID, 'recents-panel')
_contacts_view_locator = (By.ID, 'option-contacts')
_keypad_toolbar_button_locator = (By.ID, 'option-keypad')
_keypad_toolbar_locator = (By.ID, 'keypad-panel')
_call_group_menu_locator = (By.CLASS_NAME, 'call-group-menu')
_cancel_action_menu_locator = (By.ID, 'cancel-action-menu')
_contacts_toolbar_locator = (By.ID, 'iframe-contacts-container')
_contacts_frame_locator = (By.ID, 'iframe-contacts')
def launch(self):
Base.launch(self)
Wait(self.marionette).until(
expected.element_displayed(*self._keypad_toolbar_button_locator))
@property
def keypad(self):
from gaiatest.apps.phone.regions.keypad import Keypad
return Keypad(self.marionette)
def _switch_to_contacts_frame(self):
# This is a nested frame and we cannot locate it with AppWindowManager
frame = Wait(self.marionette).until(
expected.element_present(*self._contacts_frame_locator))
self.marionette.switch_to_frame(frame)
from gaiatest.apps.contacts.app import Contacts
return Contacts(self.marionette)
def tap_contacts(self):
self.marionette.find_element(*self._contacts_view_locator).tap()
return self._switch_to_contacts_frame()
def a11y_click_contacts(self):
self.accessibility.click(self.marionette.find_element(
*self._contacts_view_locator))
return self._switch_to_contacts_frame()
@property
def call_screen(self):
from gaiatest.apps.phone.regions.call_screen import CallScreen
return CallScreen(self.marionette)
@property
def call_log(self):
from gaiatest.apps.phone.regions.call_log import CallLog
return CallLog(self.marionette)
@property
def confirmation_dialog_text(self):
return self.marionette.find_element(*self._dialog_title_locator).text
def wait_for_confirmation_dialog(self):
Wait(self.marionette).until(
expected.element_displayed(*self._dialog_locator))
def tap_call_log_toolbar_button(self):
element = Wait(self.marionette).until(
expected.element_present(*self._call_log_toolbar_button_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return self.call_log
def a11y_click_call_log_toolbar_button(self):
element = Wait(self.marionette).until(
expected.element_present(*self._call_log_toolbar_button_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
return self.call_log
def tap_keypad_toolbar_button(self):
element = Wait(self.marionette).until(
expected.element_present(*self._keypad_toolbar_button_locator))
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
return self.keypad
def a11y_click_keypad_toolbar_button(self):
element = Wait(self.marionette).until(
expected.element_present(*self._keypad_toolbar_button_locator))
Wait(self.marionette).until(expected.element_displayed(element))
self.accessibility.click(element)
return self.keypad
def make_call_and_hang_up(self, phone_number):
"""Just makes a call and hangs up. Does not do any assertions."""
call_screen = self.keypad.call_number(phone_number)
call_screen.wait_for_outgoing_call()
call_screen.hang_up()
def a11y_make_call(self, phone_number):
call_screen = self.keypad.a11y_call_number(phone_number)
call_screen.wait_for_outgoing_call()
def a11y_hang_up(self):
self.call_screen.a11y_hang_up()
def a11y_make_call_and_hang_up(self, phone_number):
"""Just makes a call and hangs up using the screen reader. Does not do any assertions."""
self.a11y_make_call(phone_number)
self.a11y_hang_up()

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,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 AttentionScreen(Base):
_message_locator = (By.ID, 'message')
def __init__(self, marionette):
Base.__init__(self, marionette)
Wait(self.marionette).until(
expected.element_displayed(*self._message_locator))
@property
def message(self):
return self.marionette.find_element(*self._message_locator).text

View file

@ -0,0 +1,147 @@
# 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 StaleElementException
from gaiatest.apps.phone.app import Phone
from gaiatest.apps.base import PageRegion
from gaiatest.form_controls.binarycontrol import HtmlBinaryControl
class CallLog(Phone):
_upgrade_progress_locator = (By.ID, 'call-log-upgrading')
_call_log_edit_button_locator = (By.ID, 'call-log-icon-edit')
_call_log_header_locator = (By.ID, 'header-edit-mode-text')
_no_logs_message_locator = (By.ID, 'no-result-msg1')
_call_log_groups_locator = (By.CSS_SELECTOR, '#call-log-container section')
_call_log_edit_dialog_locator = (By.ID, 'edit-mode')
_call_log_edit_delete_button_locator = (By.ID, 'delete-button')
_call_log_edit_close_button_locator = (By.ID, 'call-log-icon-close')
_call_log_edit_deselect_all_button_locator = (By.ID, 'deselect-all-threads')
_call_log_edit_select_all_button_locator = (By.ID, 'select-all-threads')
_call_log_delete_confirmation_locator = (By.CSS_SELECTOR, 'button.danger[data-l10n-id="delete"]')
_all_calls_tab_link_locator = (By.CSS_SELECTOR, '#all-filter a')
_missed_calls_tab_link_locator = (By.CSS_SELECTOR, '#missed-filter a')
_calls_list_item_locator = (By.CSS_SELECTOR, 'li.log-item')
_all_calls_list_item_button_locator = (By.CSS_SELECTOR, 'li.log-item a')
_all_calls_list_item_checkbox_locator = (By.CSS_SELECTOR, 'li.log-item input[type="checkbox"]')
def __init__(self, marionette):
Phone.__init__(self, marionette)
Wait(self.marionette).until(
expected.element_not_displayed(*self._upgrade_progress_locator))
Wait(self.marionette).until(
expected.element_displayed(*self._all_calls_tab_link_locator))
def tap_all_calls_tab(self):
self.marionette.find_element(*self._all_calls_tab_link_locator).tap()
def tap_missed_calls_tab(self):
self.marionette.find_element(*self._missed_calls_tab_link_locator).tap()
def a11y_click_all_calls_tab(self):
self.accessibility.click(self.marionette.find_element(*self._all_calls_tab_link_locator))
def tap_edit_button(self):
edit = Wait(self.marionette).until(
expected.element_present(*self._call_log_edit_button_locator))
Wait(self.marionette).until(expected.element_displayed(edit))
edit.tap()
def tap_select_all_button(self):
# TODO Add a wait for the element to be displayed and a proper tap when Bug 1101504 is fixed
Wait(self.marionette).until(expected.element_present(
*self._call_log_edit_select_all_button_locator))
self.marionette.execute_script('document.getElementById("%s").click()' %
self._call_log_edit_select_all_button_locator[1])
def tap_delete_button(self):
# TODO Add a wait for the element to be displayed and a proper tap when Bug 1101504 is fixed
Wait(self.marionette).until(expected.element_present(
*self._call_log_edit_delete_button_locator))
self.marionette.execute_script('document.getElementById("%s").click()' %
self._call_log_edit_delete_button_locator[1])
def tap_delete_confirmation_button(self):
confirm = Wait(self.marionette).until(
expected.element_present(*self._call_log_delete_confirmation_locator))
Wait(self.marionette).until(expected.element_displayed(confirm))
confirm.tap()
Wait(self.marionette, ignored_exceptions=StaleElementException).until(
lambda m: len(self.call_list) == 0)
@property
def header_text(self):
return self.marionette.find_element(*self._call_log_header_locator).text
@property
def is_all_calls_tab_selected(self):
return self.marionette.find_element(*self._all_calls_tab_link_locator).get_attribute('aria-selected') == 'true'
@property
def is_missed_calls_tab_selected(self):
return self.marionette.find_element(*self._missed_calls_tab_link_locator).get_attribute('aria-selected') == 'true'
@property
def call_list(self):
return [LogEntries(self.marionette, element)
for element in self.marionette.find_elements(*self._calls_list_item_locator)
if element.is_displayed()]
@property
def groups_list(self):
return [LogGroups(self.marionette, element)
for element in self.marionette.find_elements(*self._call_log_groups_locator)
if element.is_displayed()]
@property
def no_logs_message(self):
return self.marionette.find_element(*self._no_logs_message_locator).text
class LogGroups(PageRegion):
_group_header_locator = (By.CSS_SELECTOR, 'header')
_calls_list_item_locator = (By.CSS_SELECTOR, 'li.log-item')
@property
def header_text(self):
return self.root_element.find_element(*self._group_header_locator).text
@property
def group_calls(self):
return [LogEntries(self.marionette, element)
for element in self.root_element.find_elements(*self._calls_list_item_locator)
if element.is_displayed()]
class LogEntries(PageRegion):
_edit_mode_checkbox_locator = (By.CSS_SELECTOR, '.call-log-selection input')
_phone_number_locator = (By.CSS_SELECTOR, 'span.primary-info-main')
_retry_count_locator = (By.CSS_SELECTOR, 'span.retry-count')
@property
def phone_number(self):
return self.root_element.find_element(*self._phone_number_locator).text
@property
def retry_count(self):
# return the retry count after trimming the parentheses from before and after the value
return self.root_element.find_element(*self._retry_count_locator).text[1:-1]
@property
def call_type(self):
return self.root_element.get_attribute('data-type')
@property
def is_checked(self):
return HtmlBinaryControl(self.marionette, self._edit_mode_checkbox_locator).is_checked

View file

@ -0,0 +1,159 @@
# 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.phone.app import Phone
class CallScreen(Phone):
MAX_NUMBER_OF_DISPLAYED_DIGITS = 22
_call_screen_locator = (By.CSS_SELECTOR, "iframe[name='call_screen']")
# The name of this ID is due to historical reason, back in the time when
# the CallScreen was not an attention screen. We need to update it once
# bug 1118787 lands.
_main_window_locator = (By.ID, 'call-screen')
_call_options_locator = (By.ID, 'call-options')
_calling_contact_locator = (By.CSS_SELECTOR, 'div.number')
_calling_contact_information_locator = (By.CSS_SELECTOR, 'div.additionalContactInfo')
_outgoing_call_locator = (By.CSS_SELECTOR, '.handled-call.outgoing')
_incoming_call_locator = (By.CSS_SELECTOR, '.handled-call.incoming')
_hangup_bar_locator = (By.ID, 'callbar-hang-up')
_answer_bar_locator = (By.ID, 'callbar-answer')
_lockscreen_handle_locator = (By.ID, 'lockscreen-area-slide')
_bluetooth_menu_locator = (By.ID, 'bluetooth-menu')
_views_locator = (By.ID, 'views')
_incoming_container_locator = (By.ID, 'incoming-container')
_incoming_answer_while_on_call_locator = (By.ID, 'incoming-answer')
_incoming_info_while_on_call_locator = (By.ID, 'incoming-info')
_incoming_number_while_on_call_locator = (By.ID, 'incoming-number')
_hangup_button_locator = (By.CSS_SELECTOR, '.handled-call .hangup-button')
_keypad_hangup_button_locator = (By.ID, 'keypad-hidebar-hang-up-action-wrapper')
_keypad_visibility_button_locator = (By.ID, 'keypad-visibility')
_merge_calls_button_locator = (By.ID, 'merge')
_conference_call_label_locator = (By.ID, 'group-call-label')
_conference_call_locator = (By.ID, 'group-call')
_contact_background_locator = (By.ID, 'contact-background')
_via_sim_locator = (By.CSS_SELECTOR, '[data-l10n-id="via-sim"]')
def __init__(self, marionette):
Phone.__init__(self, marionette)
self.switch_to_call_screen_frame()
def switch_to_call_screen_frame(self):
self.marionette.switch_to_frame()
frame = Wait(self.marionette, timeout=30).until(
expected.element_present(*self._call_screen_locator))
self.marionette.switch_to_frame(frame)
@property
def outgoing_calling_contact(self):
return self.marionette.find_element(*self._outgoing_call_locator).find_element(*self._calling_contact_locator).text
@property
def incoming_calling_contact(self):
return self.marionette.find_element(*self._incoming_call_locator).find_element(*self._calling_contact_locator).text
@property
def incoming_calling_contact_while_on_call(self):
return self.marionette.find_element(*self._incoming_number_while_on_call_locator).text
@property
def calling_contact_information(self):
return self.marionette.find_element(*self._outgoing_call_locator).find_element(*self._calling_contact_information_locator).text
@property
def conference_label(self):
return self.marionette.find_element(*self._conference_call_label_locator).text
@property
def contact_background_style(self):
return self.marionette.find_element(*self._contact_background_locator).get_attribute('style')
@property
def via_sim(self):
return self.marionette.find_element(*self._via_sim_locator).get_attribute('data-l10n-args')
def wait_for_outgoing_call(self):
call = self.marionette.find_element(*self._outgoing_call_locator)
contact = call.find_element(*self._calling_contact_locator)
Wait(self.marionette).until(lambda m: call.location['y'] == 0 and contact.text)
def wait_for_incoming_call(self):
main_window = self.marionette.find_element(*self._main_window_locator)
Wait(self.marionette).until(expected.element_displayed(main_window))
call = self.marionette.find_element(*self._incoming_call_locator)
contact = call.find_element(*self._calling_contact_locator)
Wait(self.marionette).until(lambda m: main_window.location['y'] == 0 and contact.text)
def wait_for_incoming_call_while_on_call(self):
call = self.marionette.find_element(*self._incoming_info_while_on_call_locator)
contact = self.marionette.find_element(*self._incoming_number_while_on_call_locator)
Wait(self.marionette).until(lambda m: call.is_displayed() and contact.text)
def answer_call(self):
self.marionette.find_element(*self._answer_bar_locator).tap()
def answer_call_while_on_call(self):
self.marionette.find_element(*self._incoming_answer_while_on_call_locator).tap()
def a11y_click_hang_up(self):
self.accessibility.click(self.marionette.find_element(*self._hangup_bar_locator))
def a11y_click_keypad_hang_up(self):
self.accessibility.click(self.marionette.find_element(*self._keypad_hangup_button_locator))
def hang_up(self):
self.marionette.find_element(*self._hangup_bar_locator).tap()
self.marionette.switch_to_frame()
Wait(self.marionette).until(
expected.element_not_displayed(*self._call_screen_locator))
def a11y_hang_up(self):
self.a11y_click_hang_up()
self.marionette.switch_to_frame()
Wait(self.marionette).until(
expected.element_not_displayed(*self._call_screen_locator))
def a11y_keypad_hang_up(self):
self.a11y_click_keypad_hang_up()
self.marionette.switch_to_frame()
Wait(self.marionette).until(
expected.element_not_displayed(*self._call_screen_locator))
def _handle_incoming_call(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 == 'reject':
handle_destination = 0
# 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 reject_call(self):
Wait(self.marionette).until(
expected.element_displayed(*self._lockscreen_handle_locator))
self._handle_incoming_call('reject')
self.marionette.switch_to_frame()
Wait(self.marionette).until(
expected.element_not_displayed(*self._call_screen_locator))
def a11y_click_keypad_visibility_button(self):
self.accessibility.click(self.marionette.find_element(
*self._keypad_visibility_button_locator))
def merge_calls(self):
self.marionette.find_element(*self._merge_calls_button_locator).tap()
Wait(self.marionette).until(
expected.element_displayed(*self._conference_call_locator))

View file

@ -0,0 +1,139 @@
# 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
from gaiatest.apps.phone.regions.call_screen import CallScreen
from gaiatest.apps.contacts.app import Contacts
class BaseKeypad(Base):
_phone_number_view_locator = (By.ID, 'phone-number-view')
_keypad_delete_locator = (By.ID, 'keypad-delete')
_call_bar_locator = (By.ID, 'keypad-callbar-call-action')
@property
def phone_number(self):
return self.marionette.find_element(*self._phone_number_view_locator).get_attribute('value')
def dial_phone_number(self, value):
for i in value:
if i == "+":
zero_button = self.marionette.find_element(By.CSS_SELECTOR, 'div.keypad-key[data-value="0"]')
Actions(self.marionette).long_press(zero_button, 1.2).perform()
else:
self.marionette.find_element(By.CSS_SELECTOR, 'div.keypad-key[data-value="%s"]' % i).tap()
time.sleep(0.25)
def call_number(self, value):
self.dial_phone_number(value)
return self.tap_call_button()
def clear_phone_number(self):
delete_button = self.marionette.find_element(*self._keypad_delete_locator)
Actions(self.marionette).long_press(delete_button, 1).perform()
def a11y_dial_phone_number(self, value):
for i in value:
self.accessibility.click(self.marionette.find_element(
By.CSS_SELECTOR, 'div.keypad-key[data-value="%s"]' % i))
time.sleep(0.25)
def a11y_call_number(self, value):
self.a11y_dial_phone_number(value)
return self.a11y_click_call_button()
def tap_call_button(self, switch_to_call_screen=True):
element = Wait(self.marionette).until(
expected.element_present(*self._call_bar_locator))
Wait(self.marionette).until(expected.element_enabled(element))
element.tap()
if switch_to_call_screen:
return CallScreen(self.marionette)
def a11y_click_call_button(self, switch_to_call_screen=True):
self.accessibility.click(self.marionette.find_element(*self._call_bar_locator))
if switch_to_call_screen:
return CallScreen(self.marionette)
from gaiatest.apps.phone.app import Phone
class Keypad(BaseKeypad, Phone):
_add_new_contact_button_locator = (By.ID, 'keypad-callbar-add-contact')
_search_popup_locator = (By.CSS_SELECTOR, '#suggestion-bar .js-suggestion-item')
_suggested_contact_name_locator = (By.CSS_SELECTOR, '#suggestion-bar .js-suggestion-item .js-name')
_suggested_contact_phone_number_locator = (By.CSS_SELECTOR, '#suggestion-bar .js-suggestion-item .js-tel')
def __init__(self, marionette):
Phone.__init__(self, marionette)
self.wait_to_be_displayed()
self.apps.switch_to_displayed_app()
keypad_toolbar_button = self.marionette.find_element(*self._keypad_toolbar_button_locator)
Wait(self.marionette).until(lambda m: 'toolbar-option-selected' in keypad_toolbar_button.get_attribute('class'))
def tap_add_contact(self):
self.marionette.find_element(*self._add_new_contact_button_locator).tap()
return AddNewNumber(self.marionette)
def wait_for_search_popup_visible(self):
Wait(self.marionette).until(
expected.element_displayed(*self._search_popup_locator))
@property
def suggested_name(self):
return self.marionette.find_element(*self._suggested_contact_name_locator).text
@property
def suggested_phone_number(self):
return self.marionette.find_element(*self._suggested_contact_phone_number_locator).text
def tap_search_popup(self):
self.marionette.find_element(*self._search_popup_locator).tap()
return CallScreen(self.marionette)
def wait_for_phone_number_ready(self):
# entering dialer and expecting a phone number there is javascript that
# sets the phone value and enables this button
add_contact = self.marionette.find_element(
*self._add_new_contact_button_locator)
Wait(self.marionette).until(expected.element_enabled(add_contact))
class AddNewNumber(Base):
_create_new_contact_locator = (By.CSS_SELECTOR, '[data-l10n-id=createNewContact]')
_add_to_existing_contact_locator = (By.CSS_SELECTOR, '[data-l10n-id=addToExistingContact]')
_form_locator = (By.CSS_SELECTOR, 'form.visible[data-type="action"]')
def __init__(self, marionette):
Base.__init__(self, marionette)
form = self.marionette.find_element(*self._form_locator)
Wait(self.marionette).until(lambda m: form.location['y'] == 0)
def tap_create_new_contact(self):
create_new_contact = self.marionette.find_element(*self._create_new_contact_locator)
Wait(self.marionette).until(expected.element_displayed(create_new_contact))
create_new_contact.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_add_to_existing_contact(self):
element = self.marionette.find_element(*self._add_to_existing_contact_locator)
Wait(self.marionette).until(expected.element_displayed(element))
element.tap()
contact = Contacts(self.marionette)
contact.switch_to_contacts_frame()
return contact