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