def long_press_action(marionette, wait_for_condition, expected): testAction = marionette.absolute_url("testAction.html") marionette.navigate(testAction) button = marionette.find_element(By.ID, "button1") action = Actions(marionette) action.long_press(button, 5).perform() wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
def long_press_action(marionette, wait_for_condition, expected): testAction = marionette.absolute_url("testAction.html") marionette.navigate(testAction) button = marionette.find_element(By.ID, "button1") action = Actions(marionette) action.long_press(button, 5).perform() wait_for_condition_else_raise( marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
class test_main(GaiaTestCase): def setUp(self): # Set up child objects... GaiaTestCase.setUp(self) self.UTILS = UTILS(self) self.messages = Messages(self) self.actions = Actions(self.marionette) # Establish which phone number to use. self.phone_number = self.UTILS.general.get_config_variable( "phone_number", "custom") self.incoming_sms_num = self.UTILS.general.get_config_variable( "sms_platform_numbers", "common").split(',') self.UTILS.reporting.logComment("Sending sms to telephone number " + self.phone_number) self.data_layer.delete_all_sms() def tearDown(self): self.UTILS.reporting.reportResults() GaiaTestCase.tearDown(self) def test_run(self): self.UTILS.statusbar.clearAllStatusBarNotifs() timestamp = " {}".format(time.time()) sms_message = "0123456789" * 5 + timestamp self.UTILS.reporting.logComment("Message length sent: {}".format( (len(sms_message)))) self.UTILS.messages.create_incoming_sms(self.phone_number, sms_message) self.UTILS.statusbar.wait_for_notification_toaster_detail(timestamp, timeout=120) title = self.UTILS.statusbar.wait_for_notification_toaster_with_titles( self.incoming_sms_num, timeout=5) self.UTILS.statusbar.click_on_notification_title( title, DOM.Messages.frame_locator) # Open sms option with longtap on it self.UTILS.reporting.logResult("info", "Open sms option with longtap on it") sms = self.messages.last_message_in_this_thread() body = self.marionette.find_element(*DOM.Messages.last_message_body, id=sms.id) self.actions.long_press(body, 2).perform() # Press cancel button self.UTILS.reporting.logResult("info", "Clicking cancel button") time.sleep(2) cancel_btn = self.UTILS.element.getElement( DOM.Messages.cancel_btn_msg_opt, "Cancel button is displayed") self.UTILS.reporting.debug("*** Cancel button: {} text: {}".format( cancel_btn, cancel_btn.text)) self.UTILS.element.simulateClick(cancel_btn)
def long_press_on_xy_action(marionette, wait_for_condition, expected): testAction = marionette.absolute_url("testAction.html") marionette.navigate(testAction) html = marionette.find_element(By.TAG_NAME, "html") button = marionette.find_element(By.ID, "button1") action = Actions(marionette) # Press the center of the button with respect to html. x = button.rect['x'] + button.rect['width'] / 2.0 y = button.rect['y'] + button.rect['height'] / 2.0 action.long_press(html, 5, x, y).perform() wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
def long_press_on_xy_action(marionette, wait_for_condition, expected): testAction = marionette.absolute_url("testAction.html") marionette.navigate(testAction) html = marionette.find_element(By.TAG_NAME, "html") button = marionette.find_element(By.ID, "button1") action = Actions(marionette) # Press the center of the button with respect to html. x = button.rect['x'] + button.rect['width'] / 2.0 y = button.rect['y'] + button.rect['height'] / 2.0 action.long_press(html, 5, x, y).perform() wait_for_condition_else_raise( marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
class test_main(GaiaTestCase): def setUp(self): # Set up child objects... GaiaTestCase.setUp(self) self.UTILS = UTILS(self) self.messages = Messages(self) self.actions = Actions(self.marionette) # Establish which phone number to use. self.phone_number = self.UTILS.general.get_config_variable("phone_number", "custom") self.incoming_sms_num = self.UTILS.general.get_config_variable("sms_platform_numbers", "common").split(',') self.UTILS.reporting.logComment("Sending sms to telephone number " + self.phone_number) self.data_layer.delete_all_sms() def tearDown(self): self.UTILS.reporting.reportResults() GaiaTestCase.tearDown(self) def test_run(self): self.UTILS.statusbar.clearAllStatusBarNotifs() timestamp = " {}".format(time.time()) sms_message = "0123456789" * 5 + timestamp self.UTILS.reporting.logComment("Message length sent: {}".format((len(sms_message)))) self.UTILS.messages.create_incoming_sms(self.phone_number, sms_message) self.UTILS.statusbar.wait_for_notification_toaster_detail(timestamp, timeout=120) title = self.UTILS.statusbar.wait_for_notification_toaster_with_titles(self.incoming_sms_num, timeout=5) self.UTILS.statusbar.click_on_notification_title(title, DOM.Messages.frame_locator) # Open sms option with longtap on it self.UTILS.reporting.logResult("info", "Open sms option with longtap on it") sms = self.messages.last_message_in_this_thread() body = self.marionette.find_element(*DOM.Messages.last_message_body, id=sms.id) self.actions.long_press(body, 2).perform() # Press cancel button self.UTILS.reporting.logResult("info", "Clicking cancel button") time.sleep(2) cancel_btn = self.UTILS.element.getElement(DOM.Messages.cancel_btn_msg_opt, "Cancel button is displayed") self.UTILS.reporting.debug("*** Cancel button: {} text: {}".format(cancel_btn, cancel_btn.text)) self.UTILS.element.simulateClick(cancel_btn)
class test_main(GaiaTestCase): def setUp(self): # Set up child objects... GaiaTestCase.setUp(self) self.UTILS = UTILS(self) self.messages = Messages(self) self.contacts = Contacts(self) self.phone_number = self.UTILS.general.get_config_variable("phone_number", "custom") self.emailAddy = self.UTILS.general.get_config_variable("gmail_1_email", "common") self.test_msg = "Hello {} old bean at {}.".format(self.emailAddy, time.time()) def tearDown(self): self.UTILS.reporting.reportResults() GaiaTestCase.tearDown(self) def test_run(self): # Launch messages app. self.messages.launch() # Create and send a new test message. self.messages.create_and_send_sms([self.phone_number], self.test_msg) send_time = self.messages.last_sent_message_timestamp() msg = self.messages.wait_for_message(send_time=send_time) self.messages.check_last_message_contents(self.test_msg) # Tap on edit mode. edit_btn = self.UTILS.element.getElement(DOM.Messages.edit_messages_icon, "Edit button") edit_btn.tap() select_btn = self.UTILS.element.getElement(DOM.Messages.edit_msgs_select_btn, "Select button") select_btn.tap() # Long press the email link. _link = msg.find_element("tag name", "a") self.actions = Actions(self.marionette) self.actions.long_press(_link, 2).perform() # Check the email address is not a link in edit mode. self.UTILS.element.waitForNotElements(DOM.Messages.header_create_new_contact_btn, "Create new contact button")
class test_main(GaiaTestCase): def __init__(self, *args, **kwargs): kwargs['restart'] = True super(test_main, self).__init__(*args, **kwargs) def setUp(self): # Set up child objects... GaiaTestCase.setUp(self) self.UTILS = UTILS(self) self.settings = Settings(self) self.EME = EverythingMe(self) self.actions = Actions(self.marionette) try: self.apps.set_permission('Homescreen', 'geolocation', 'deny') self.apps.set_permission('Smart Collections', 'geolocation', 'deny') except: self.UTILS.reporting.logComment("Unable to automatically set geolocation permission.") def tearDown(self): # Restart device to restore collections self.device.restart_b2g() GaiaTestCase.setUp(self) self.UTILS.reporting.reportResults() GaiaTestCase.tearDown(self) def test_run(self): self.UTILS.iframe.switchToFrame(*DOM.Home.frame_locator) categories = self.UTILS.element.getElements(DOM.EME.all_collections, "All collections") for cat in categories: name = self.marionette.find_element('css selector', 'span.title', cat.id).text self.UTILS.reporting.debug("** Removing collection: {}".format(name)) self.actions.long_press(cat, 2).perform() delete_btn = ("xpath", DOM.Home.app_delete_icon_xpath.format(name)) delete_button = self.UTILS.element.getElement(delete_btn, "Delete button", False, 30, True) delete_button.tap() delete = self.UTILS.element.getElement(DOM.Home.app_confirm_delete, "Confirm app delete button") delete.tap() self.UTILS.element.waitForNotElements(DOM.EME.all_collections, "All collections", timeout=10)
class EverythingMe(object): def __init__(self, p_parent): self.apps = p_parent.apps self.data_layer = p_parent.data_layer self.parent = p_parent self.marionette = p_parent.marionette self.UTILS = p_parent.UTILS self.actions = Actions(self.marionette) def launch(self): self.apps.kill_all() # If EME has already been launched, then the DOM has changed. self.UTILS.reporting.logResult("info", "Launching Everything ME.") boolOK = False try: self.parent.wait_for_element_displayed(*DOM.EME.start_eme_icon, timeout=1) x = self.marionette.find_element(*DOM.EME.start_eme_icon) x.tap() boolOK = True except: self.UTILS.reporting.logResult( "info", "Everything ME is already 'running', so just waking it up ...") self._relaunch() try: self.parent.wait_for_element_displayed(*DOM.EME.groups, timeout=3) except: self._relaunch() boolOK = True self.UTILS.test.test(boolOK, "EME Starting up ...") def _relaunch(self): """ Private function to re-launch. This gets complicated: 1. el.tap() and el.click() only work *sometimes*, so use the keyboard to relaunch. 2. Sometimes the messges app randomly launches instead of evme! """ x = self.marionette.find_element(*DOM.EME.search_field) x.send_keys("waking up evme") x = self.marionette.find_element(*DOM.EME.search_clear) x.tap() def add_app_to_homescreen(self, name): """ Pick an app from the apps listed in this group. """ x = self.UTILS.element.getElementByXpath( DOM.EME.app_to_install.format(name)) app_name = x.text self.UTILS.reporting.logResult( "debug", "icon displayed: {}".format(x.is_displayed())) time.sleep(2) self.UTILS.test.test(app_name == name, "" + app_name + "'is the correct app", True) self.actions.long_press(x, 2).perform() x = self.UTILS.element.getElement(DOM.EME.add_to_homescreen_btn, "Add app to homescreen button") x.tap() time.sleep(2) return True def add_group(self, group): """ Adds a group to EME (assumes you're already in the EME group screen). """ self.UTILS.reporting.logResult("info", "(Adding group '" + group + "'.)") # Click the 'More' icon. x = self.UTILS.element.getElement(DOM.EME.add_group_button, "'More' icon") x.tap() # Wait for the 'loading' spinner to go away (can take a while!). self.UTILS.element.waitForNotElements(DOM.EME.loading_groups_message, "'Loading' message", True, 120) # Chose an item from the groups list... self.UTILS.general.selectFromSystemDialog(group) # Verify the new group is in the groups list. x = self.UTILS.element.getElements(DOM.EME.groups, "Groups") boolOK = False for i in x: if i.get_attribute("data-query") == group: boolOK = True break self.UTILS.test.test( boolOK, "New group '" + group + "' is now present in the EME groups.") return boolOK def add_multiple_groups(self, group_array=False): """ Adds multiple groups based on an array of numbers (defaults to all available groups). <br><br> For example: add_multiple_groups([0,1,2,3,8,11]) ... or just: add_multiple_groups() """ x = self.UTILS.element.getElement(DOM.EME.add_group_button, "'More' icon") x.tap() self.UTILS.element.waitForNotElements(DOM.EME.loading_groups_message, "'Loading' message", True, 120) # Switch to group selector (in top level iframe). self.marionette.switch_to_frame() # for checking later list_names = [] elements = self.UTILS.element.getElements( DOM.GLOBAL.modal_valueSel_list, "Groups list", False) for i in range(len(elements)): if i > 0: # Keep shuffling the groups into view so they can be tapped. self.actions.press(elements[i]).move( elements[i - 1]).wait(0.5).release().perform() elements = self.marionette.find_elements( *DOM.GLOBAL.modal_valueSel_list) # Only select it if it's the list, or there is no list. select_elements = False if group_array: if len(group_array) == len(list_names): # # We've done all of them - stop looping! break if i in group_array: select_elements = True else: select_elements = True if select_elements: tmp_name = elements[i].find_element("tag name", "span").text self.UTILS.reporting.logResult( "info", "Selecting '{}' ...".format(tmp_name)) list_names.append(tmp_name) elements[i].tap() # Sometimes the first tap does nothing for some reason. if not elements[i].get_attribute("aria-checked"): elements[i].tap() # Click the OK button. x = self.UTILS.element.getElement(DOM.GLOBAL.modal_valueSel_ok, "OK button") try: # Sometimes it's one, sometimes the other ... x.tap() x.click() except: pass time.sleep(1) # Checkk all the items we expect are now loaded in evme. self.UTILS.iframe.switchToFrame(*DOM.Home.frame_locator) time.sleep(5) for name in list_names: ok = False # Reload the groups (silently or we'll have loads of these messages!). try: x = self.marionette.find_elements(*DOM.EME.groups) except: break for i in x: group_name = i.get_attribute("data-query") if group_name == name: ok = True break self.UTILS.test.test(ok, "'{}' is now among the groups.".format(name)) def install_app(self, category, name, expect_btn=True): """Try to install an application. Try to install the application with the given name in the given category. expect_btn determines if we expect the "Add to Home Screen" button in the bookmark page. Returns True if the application was successfully installed. """ self.pick_group(category) self.UTILS.iframe.switchToFrame(*DOM.EME.frame_locator) app_name = self.UTILS.element.getElementByXpath( DOM.EME.app_to_install.format(name)).text # Add the app to the homescreen. self.add_app_to_homescreen(app_name) self.UTILS.iframe.switchToFrame(*DOM.EME.bookmark_frame_locator) time.sleep(4) result = False # We expect the application to be installed, so find the Add to Home Screen button and tap it if expect_btn: add_btn = self.UTILS.element.getElement( DOM.EME.add_to_homescreen_done_btn, "Add bookmark to Home Screen Button") add_btn.tap() result = True else: # We expect the application is already installed, so find the proper header title = self.UTILS.element.getElement(DOM.EME.edit_bookmark_header, "Edit link header") self.UTILS.test.test(title, "Title '{}' found".format(title.text)) return result def launch_from_group(self, app_name): """ Function to launch an app directly from an EME group. """ x = self.UTILS.element.getElement( ("xpath", "//li[@data-name='{}']".format(app_name)), "Icon for app '{}'".format(app_name), False) try: x.tap() except: # App is not visible, so I need to move it into view first. _id = x.get_attribute("_id") self.marionette.execute_script( "document.getElementById('{}').scrollIntoView();".format(_id)) x.tap() time.sleep(1) self.UTILS.element.waitForNotElements(DOM.EME.launched_activity_bar, "Activity notifier", True, 30) x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "Screenshot of app running:", x) def pick_group(self, name): """ Pick a group from the main icons. """ screenshot = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult( "info", "<b>Choosing group '{}' from here ...</b>".format(name), screenshot) ok = False self.UTILS.reporting.info("searching for element: {}".format( DOM.Home.app_icon_css_selector.format(name))) icon = self.marionette.find_element( 'css selector', DOM.Home.app_icon_css_selector.format(name)) self.UTILS.reporting.logResult( "debug", "icon displayed: {}".format(icon.is_displayed())) icon.tap() try: self.UTILS.iframe.switchToFrame(*DOM.EME.frame_locator) self.parent.wait_for_element_displayed(*DOM.EME.apps_not_installed, timeout=20) self.UTILS.reporting.logResult( "info", "(Apps for group {} were displayed.)".format(name)) ok = True except Exception as e: self.UTILS.reporting.debug( "*** Error getting apps not installed: {}".format(e)) screenshot = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "(<b>NOTE:</b>Apps for group {} were not displayed.)|{}|{}".\ format(name, screenshot[0], screenshot[1])) return ok def remove_groups(self, group_array): """ Removes groups from the EME group page. group_array is an array of group names (default = all groups) For example: remove_groups(["Games","Local"]) """ """ Put the groups into edit mode. Sometimes this takes a while to happen, so increase the length of time you press the icon until it works! """ ok = False x = self.marionette.find_element( 'xpath', DOM.Home.app_icon_css_selector.format(group_array[0])) self.actions.press(x).wait(3).release() try: actions.perform() except: pass try: x = self.UTILS.element.getElement( ("xpath", DOM.Home.app_delete_icon_xpath.format( group_array[0])), "Delete button", False, 5, True) if x.is_displayed(): ok = True except: pass time.sleep(2) self.UTILS.test.test(ok, "Enabled EDIT mode.") x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "Screenshot of app in EDIT mode:", x) # Remove all groups in the array. removed = 0 group_cnt = len(group_array) self.UTILS.reporting.logResult("info", "Removing {} groups".format(group_cnt)) self.UTILS.reporting.logResult( "info", "Removing groups: {}".format(group_array)) for group_specified in group_array: # Remove it. self.marionette.find_element( 'xpath', DOM.Home.app_icon_css_selector.format(group_specified)) y = self.UTILS.element.getElement( ("xpath", DOM.Home.app_delete_icon_xpath.format(group_specified)), "Delete button", False, 5, True) y.tap() delete = self.UTILS.element.getElement( DOM.Home.app_confirm_delete, "Confirm app delete button") delete.tap() removed = removed + 1 self.UTILS.reporting.logResult( "info", "Removed group '{}' ...".format(group_specified)) self.UTILS.reporting.logResult("info", "Removed {} groups".format(removed)) if removed == group_cnt: break # Turn off edit mode. self.UTILS.reporting.logResult("info", "Disabling edit mode ...") self.UTILS.home.touchHomeButton() def search_for_app(self, name): """ Uses the search field to find the app (waits for the result to appear etc...).<br> Returns the element for the icon (or False if it's not found). """ x = self.UTILS.element.getElement(DOM.EME.search_field, "Search field") x.clear() x.send_keys(name) x.click() time.sleep(5) # Can take a few seconds to appear, so try a few times (about 1 min). for retry in range(10): x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult( "debug", "Looking for '{}' - attempt {} ...".format(name, retry), x) x = self.UTILS.element.getElements(DOM.EME.search_suggestions, "Search suggestions") ok = False for i in x: i_name = i.get_attribute("data-suggestion") if i_name: i_name = i_name.lower() i_name = i_name.replace("[", "") i_name = i_name.replace("]", "") is_in = False for i2 in name.lower().split(): self.UTILS.reporting.logResult( "debug", "Is '{}' in '{}'?".format(i2, i_name)) if i2 not in i_name: is_in = False break else: is_in = True if is_in: i.tap() ok = True break if ok: break time.sleep(6) self.UTILS.test.test(ok, "Found '%s' in suggestions." % name) ok = True try: elem = ("xpath", DOM.EME.search_result_icon_xpath.format(name)) self.parent.wait_for_element_displayed(*elem, timeout=60) x = self.marionette.find_element(*elem) return x except: ok = False return ok
class Messages(object): def __init__(self, parent): self.apps = parent.apps self.data_layer = parent.data_layer self.parent = parent self.marionette = parent.marionette self.UTILS = parent.UTILS self.actions = Actions(self.marionette) @retry(5) def launch(self): self.app = self.apps.launch(self.__class__.__name__) self.UTILS.element.waitForNotElements( DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") return self.app def cancelSettings(self): self.UTILS.reporting.logResult("info", "Cliking on messages options button") options_btn = self.UTILS.element.getElement( DOM.Messages.messages_options_btn, "Messages option button is displayed") options_btn.tap() # Press cancel button cancelBtn = self.UTILS.element.getElement(DOM.Messages.cancel_btn_msg, "Press Cancel button") cancelBtn.tap() def deleteSubject(self, subject): self.UTILS.reporting.logResult("info", "Cliking on messages options button") x = self.UTILS.element.getElement( DOM.Messages.messages_options_btn, "Messages option button is displayed") x.tap() # Press add subject button self.UTILS.reporting.logResult("info", "Cliking on delete subject button") x = self.UTILS.element.getElement( DOM.Messages.deletesubject_btn_msg_opt, "delete subject option button is displayed") x.tap() def addSubject(self, subject): self.UTILS.reporting.logResult("info", "Cliking on messages options button") x = self.UTILS.element.getElement( DOM.Messages.messages_options_btn, "Messages option button is displayed") x.tap() # Press add subject button screenshot = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult('info', "Screenshot", screenshot) self.UTILS.reporting.logResult("info", "Cliking on add subject button") x = self.UTILS.element.getElement( DOM.Messages.addsubject_btn_msg_opt, "add subject option button is displayed") x.tap() self.UTILS.general.typeThis(DOM.Messages.target_subject, "Target Subject field", subject, p_no_keyboard=True, p_validate=False, p_clear=False, p_enter=False) def checkAirplaneModeWarning(self): """ Checks for the presence of the popup warning message if you just sent a message while in 'airplane mode' (also removes the message so you can continue). """ x = self.UTILS.element.getElement( DOM.Messages.airplane_warning_message, "Airplane mode warning message", True, 5, False) if x: self.UTILS.reporting.logResult( "info", "Warning message title detected = '" + x.text + "'.") x = self.UTILS.element.getElement(DOM.Messages.airplane_warning_ok, "OK button") x.tap() def check_last_message_contents(self, expected, mms=False): """ Get the last message text and check it against the expected value. """ msg = self.last_message_in_this_thread() dom = DOM.Messages.last_message_mms_text if mms else DOM.Messages.last_message_text msg_text = self.marionette.find_element(*dom, id=msg.id) self.UTILS.test.test( (msg_text and msg_text.text == expected), u"Expected message text = '{}' ({}) (got '{}' ({})).".format( expected, len(expected), msg_text.text, len(msg_text.text))) def checkIsInToField(self, target, targetIsPresent=True): """ Verifies if a number (or contact name) is displayed in the "To: " field of a compose message.<br> (Uses 'caseless' search for this.) """ time.sleep(1) x = self.UTILS.element.getElements(DOM.Messages.target_numbers, "'To:' field contents", False) boolOK = False for i in x: if i.text.lower() == str(target).lower(): boolOK = True break testMsg = "is" if targetIsPresent else "is not" testMsg = "\"" + str(target) + "\" " + testMsg + " in the 'To:' field." self.UTILS.test.test(boolOK == targetIsPresent, testMsg) return boolOK def checkMMSIcon(self, thread_name): # Get the thread for which we want to check the icon existence selector = ("xpath", DOM.Messages.thread_selector_xpath.format(thread_name)) elem = self.UTILS.element.getElement( selector, "Message thread for " + thread_name) """ But, in order to make sure we're getting the specific frame, what we trully got above is an inner child of the thread element. So, we gotta get the father """ thread = self.marionette.execute_script(""" return arguments[0].parentNode; """, script_args=[elem]) # Checks for the presence of the MMS icon icon = thread.find_element(*DOM.Messages.mms_icon) if icon: self.UTILS.test.test( icon is not None, "MMS icon detected for thread [{}]".format(thread_name)) def checkNumberIsInToField(self, target): """ Verifies if a number is contained in the "To: " field of a compose message (even if it's not displayed - i.e. a contact name is displayed, but this validates the <i>number</i> for that contact). """ x = self.UTILS.element.getElements(DOM.Messages.target_numbers, "'To:' field contents") boolOK = False for i in x: if i.get_attribute("data-number") == target: boolOK = True break self.UTILS.test.test( boolOK, "\"" + str(target) + "\" is the number in one of the 'To:' field targets.") return boolOK def checkThreadHeader(self, header): """ Verifies if a string is contained in the header """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Header") boolOK = False if x.get_attribute("data-number") == header: boolOK = True self.UTILS.test.test( boolOK, "\"" + str(header) + "\" is the header in the SMS conversation.") return boolOK def checkThreadHeaderWithNameSurname(self, header): """ Verifies if a string is contained in the header """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Header") boolOK = False if x.text == header: boolOK = True self.UTILS.test.test( boolOK, "\"" + header + "\" is the header in the SMS conversation.") return boolOK def closeThread(self): """ Closes the current thread (returns you to the 'thread list' SMS screen). """ self.go_back() self.UTILS.element.waitForElements( ("xpath", "//h1[text()='{}']".format(_("Messages"))), "Messages main header") def countMessagesInThisThread(self): """ Returns the number of messages in this thread (assumes you're already in the thread). """ try: return len( self.UTILS.element.getElements(DOM.Messages.message_list, "Messages")) except: return 0 def countNumberOfThreads(self): """ Count all threads (assumes the messagin app is already open). """ try: return len( self.UTILS.element.getElements(DOM.Messages.threads_list, "Threads")) except: return 0 def create_and_send_mms(self, attached_type, nums, m_text): self.gallery = Gallery(self.parent) self.video = Video(self.parent) self.music = Music(self.parent) self.launch() self.startNewSMS() self.addNumbersInToField(nums) self.enterSMSMsg(m_text) if attached_type == "image": # Add an image file self.UTILS.general.add_file_to_device( './tests/_resources/80x60.jpg') self.create_mms_image() self.gallery.click_on_thumbnail_at_position_mms(0) elif attached_type == "cameraImage": # Add an image file from camera self.create_mms_camera_image() time.sleep(3) elif attached_type == "video": # Load an video file into the device. self.UTILS.general.add_file_to_device( './tests/_resources/mpeg4.mp4') self.create_mms_video() self.video.click_on_video_at_position_mms(0) elif attached_type == "audio": # Load an video file into the device. self.UTILS.general.add_file_to_device('./tests/_resources/AMR.amr') self.create_mms_music() self.music.click_on_song_mms() else: # self.UTILS.reporting.logResult("info", "incorrect value received") msg = "FAILED: Incorrect parameter received in create_and_send_mms()"\ ". attached_type must being image, video or audio." self.UTILS.test.test(False, msg) time.sleep(2) self.sendSMS() return self.last_sent_message_timestamp() def create_and_send_sms(self, nums, msg): """ Create and send a new SMS.<br> <b>Note:</b> The nums field must be an array of numbers or contact names. """ self.startNewSMS() self.addNumbersInToField(nums) self.enterSMSMsg(msg) # The header should now say how many recipients. time.sleep(2) # give the header time to change. num_recs = len(nums) search_str = _(" recipient") if num_recs == 1 else _(" recipients") self.UTILS.element.headerCheck(str(num_recs) + search_str) # Send the message. self.sendSMS() def create_mms_image(self): attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() gallery = self.UTILS.element.getElement(DOM.Messages.mms_from_gallery, "From gallery") gallery.tap() self.UTILS.iframe.switchToFrame(*DOM.Gallery.frame_locator) def create_mms_camera_image(self): self.camera = Camera(self.parent) attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() camera = self.UTILS.element.getElement(DOM.Messages.mms_from_camera, "From Camera") camera.tap() self.UTILS.iframe.switchToFrame(*DOM.Camera.frame_locator) # Take a picture. self.camera.take_and_select_picture() self.UTILS.iframe.switchToFrame(*DOM.Messages.frame_locator) def create_mms_music(self): attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() music = self.UTILS.element.getElement(DOM.Messages.mms_from_music, "From music") music.tap() self.UTILS.iframe.switchToFrame(*DOM.Music.frame_locator) def go_back(self): """Press back button in messages thread """ # TODO: remove tap with coordinates after Bug 1061698 is fixed self.UTILS.element.getElement(DOM.Messages.header_link_locator, "Back button").tap(25, 25) def create_mms_video(self): attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() video = self.UTILS.element.getElement(DOM.Messages.mms_from_video, "From video") video.tap() self.UTILS.iframe.switchToFrame(*DOM.Video.frame_locator) def delete_all_threads(self): """ Deletes all threads (assumes the messagin app is already open). """ try: self.parent.wait_for_element_displayed( *DOM.Messages.no_threads_message, timeout=3) no_threads_message = self.marionette.find_element( *DOM.Messages.no_threads_message) if no_threads_message.is_displayed(): self.UTILS.reporting.logResult( "info", "(No message threads to delete.)") except: self.UTILS.reporting.logResult("info", "Deleting message threads ...") self.threadEditModeON() select_threads = self.UTILS.element.getElement( DOM.Messages.edit_msgs_select_threads_btn, "Delete threads button") select_threads.tap() select_all_btn = self.UTILS.element.getElement( DOM.Messages.check_all_threads_btn, "Select all button") select_all_btn.tap() self.deleteSelectedThreads() self.UTILS.element.waitForElements( DOM.Messages.no_threads_message, "No message threads notification", True, 60) def deleteMessagesInThisThread(self, msg_array=False): """ Enters edit mode, selects the required messages and deletes them.<br> msg_array is an array of numbers. If it's not specified then all messages in this thread will be deleted. """ if msg_array: self.editAndSelectMessages(msg_array) else: # Go into messages Settings.. edit_btn = self.UTILS.element.getElement( DOM.Messages.edit_messages_icon, "Edit button") edit_btn.tap() select_btn = self.UTILS.element.getElement( DOM.Messages.edit_msgs_select_btn, "Select button") select_btn.tap() # Press select all button. select_all_btn = self.UTILS.element.getElement( DOM.Messages.check_all_messages_btn, "'Select all' button") select_all_btn.tap() self.deleteSelectedMessages() def deleteSelectedMessages(self): self.UTILS.reporting.debug("*** Tapping top Delete button") delete_btn = self.UTILS.element.getElement( DOM.Messages.delete_messages_ok_btn, "Delete button") delete_btn.tap() time.sleep(2) self.UTILS.reporting.debug( "*** Tap Delete messages confirmation button") confirm_btn = self.UTILS.element.getElement( DOM.Messages.delete_threads_ok_btn, "Delete messages confirmation button") confirm_btn.tap() time.sleep(2) def deleteSelectedThreads(self): delete_btn = self.UTILS.element.getElement( DOM.Messages.threads_delete_button, "Delete threads button") delete_btn.tap() delete_confirm_btn = self.UTILS.element.getElement( DOM.Messages.delete_threads_ok_btn, "Delete threads confirmation button") delete_confirm_btn.tap() def deleteThreads(self, target_array=False): """ Enters edit mode, selects the required messages and deletes them.<br> target_array is an array of target numbers or contacts which identify the threads to be selected. If it's not specified then all messages in this thread will be deleted. """ try: self.parent.wait_for_element_displayed( *DOM.Messages.no_threads_message, timeout=2) x = self.marionette.find_element(*DOM.Messages.no_threads_message) if x.is_displayed(): self.UTILS.reporting.logResult( "info", "(No message threads to delete.)") except: self.UTILS.reporting.logResult("info", "Deleting message threads ...") if target_array: self.UTILS.reporting.debug( "*** Selecting threads for deletion [{}]".format( target_array)) self.editAndSelectThreads(target_array) self.UTILS.reporting.debug("*** Threads selected") self.deleteSelectedThreads() else: self.delete_all_threads() def editAndSelectMessages(self, msg_array): """ Puts this thread into Edit mode and selects the messages listed in msg_array.<br> msg_array is an array of numbers. """ edit_btn = self.UTILS.element.getElement( DOM.Messages.edit_messages_icon, "Edit button") edit_btn.tap() time.sleep(2) # Go into message edit mode.. select_btn = self.UTILS.element.getElement( DOM.Messages.edit_msgs_select_btn, "Select messages button") select_btn.tap() time.sleep(2) messages = self.UTILS.element.getElements(DOM.Messages.message_list, "Messages") for msg in msg_array: messages[msg].tap() def editAndSelectThreads(self, target_array): """ Puts this thread into Edit mode and selects the messages listed in p_msg_array.<br> target_array is an array of target numbers or contacts which identify the threads to be selected. """ # Go into edit mode.. self.threadEditModeON() select_btn = self.UTILS.element.getElement( DOM.Messages.edit_msgs_select_threads_btn, "Select threads button") select_btn.tap() if len(target_array) == 0: return for i in target_array: self.UTILS.reporting.debug("selecting thread for [{}]".format(i)) thread = self.UTILS.element.getElement( ("xpath", DOM.Messages.thread_selector_xpath.format(i)), "Thread checkbox for '{}'".format(i)) self.UTILS.reporting.debug( "Trying to tap in element {}".format(thread)) thread.tap() # Finally check that all desired threads have been selected header = self.UTILS.element.getElement( DOM.Messages.edit_threads_header, "Edit threads header").text expected_title = str( len(target_array)) if len(target_array) else _("Delete messages") self.UTILS.test.test( expected_title in header, "Check that all desired threads have been selected") def enterSMSMsg(self, msg, not_keyboard=True): """ Create and send a message (assumes we are in a new 'create new message' screen with the destination number filled in already). """ self.parent.wait_for_element_displayed( *DOM.Messages.input_message_area) input_area = self.marionette.find_element( *DOM.Messages.input_message_area) input_area.tap() input_area.send_keys(msg) # Validate the field. # Get the element again in order to avoid StaleElementException input_area = self.marionette.find_element( *DOM.Messages.input_message_area) self.UTILS.test.test( input_area.text == msg, u'The text in the message area is "{}". Expected: "{}"'.format( input_area.text, msg)) def addNumbersInToField(self, nums): """ Add the phone numbers in the 'To' field of this sms message. Assumes you are in 'create sms' screen. """ # This variable is used to keep track of the appearance of the keyboard frame n = 0 for num in nums: """ Even though we don't use the keyboard for putting the number in, we need it for the ENTER button (which allows us to put more than one number in). So check that the keyboard appears when we tap the "TO" field if we have more than one number. """ if len(nums) > 1: self.UTILS.reporting.logResult( "info", "Checking the keyboard appears when I tap the 'To' field ..." ) to_field = self.UTILS.element.getElement( DOM.Messages.target_numbers_empty, "Target number field") to_field.tap() boolKBD = False self.marionette.switch_to_frame() if n < 1: try: # A 'silent' check to see if the keyboard iframe appears. elDef = ("xpath", "//iframe[contains(@{}, '{}')]".format( DOM.Keyboard.frame_locator[0], DOM.Keyboard.frame_locator[1])) self.parent.wait_for_element_displayed(*elDef, timeout=2) boolKBD = True except: boolKBD = False self.UTILS.test.test( boolKBD, "Keyboard is displayed when 'To' field is clicked for the first time" ) self.UTILS.iframe.switchToFrame(*DOM.Messages.frame_locator) if n == 0: self.UTILS.general.typeThis(DOM.Messages.target_numbers_empty, "Target number field", num, p_no_keyboard=True, p_validate=False, p_clear=False, p_enter=True) else: self.UTILS.general.typeThis(DOM.Messages.target_numbers_empty, "Target number field", num, p_no_keyboard=True, p_validate=False, p_clear=False, p_enter=False) input_area = self.UTILS.element.getElement( DOM.Messages.input_message_area, "Target number field") input_area.tap() n += 1 def addContactToField(self, contact_name): self._search_for_contact(contact_name) # Now check the correct name is in the 'To' list. self.checkIsInToField(contact_name) def _select_forward_option_for(self, element): self.actions.long_press(element, 2).perform() self.UTILS.reporting.logResult("info", "Clicking on forward button") forward_option = self.UTILS.element.getElement( DOM.Messages.forward_btn_msg_opt, "Forward button is displayed") forward_option.tap() def _search_for_contact(self, contact_name): self.contacts = Contacts(self.parent) self.selectAddContactButton() self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) self.contacts.search(contact_name) self.contacts.check_search_results(contact_name) results = self.UTILS.element.getElements( DOM.Contacts.search_results_list, "Contacts search results") for result in results: if result.text == contact_name: result.tap() break # Switch back to the sms iframe. self.apps.switch_to_displayed_app() def forwardMessage(self, msg_type, target_telNum): """ Forwards the last message of the thread to a number """ self.UTILS.reporting.logResult( 'info', "The message type to forward is: {}".format(msg_type)) if msg_type == "sms" or msg_type == "mms": self.UTILS.reporting.logResult( "info", "Open {} option with longtap on it".format(msg_type)) last = self.last_message_in_this_thread() body = self.marionette.find_element( *DOM.Messages.last_message_body, id=last.id) self._select_forward_option_for(body) elif msg_type == "mmssub": self.UTILS.reporting.logResult( "info", "Open mms with subject options with longtap on it") mms_subject = self.UTILS.element.getElement( DOM.Messages.received_mms_subject, "Target MMS field") self._select_forward_option_for(mms_subject) else: self.UTILS.reporting.logResult("info", "incorrect value received") self.UTILS.test.test(False, "Incorrect value received") self.addNumbersInToField([target_telNum]) self.UTILS.reporting.logResult("info", "Clicking on Send button") self.sendSMS() def forwardMessageToContact(self, msg_type, contact_name): """ Forwards the last message of the thread to a contact, searching for it """ self.UTILS.reporting.logResult( 'info', "The message type to forward is: {}".format(msg_type)) if msg_type == "sms" or msg_type == "mms": # Open sms option with longtap on it self.UTILS.reporting.logResult( "info", "Open sms option with longtap on it") sms = self.last_message_in_this_thread() body = self.marionette.find_element( *DOM.Messages.last_message_body, id=sms.id) self._select_forward_option_for(body) elif msg_type == "mmssub": # Open mms option with longtap on it self.UTILS.reporting.logResult( "info", "Open mms with subject options with longtap on it") mms_subject = self.UTILS.element.getElement( DOM.Messages.received_mms_subject, "Target MMS field") self._select_forward_option_for(mms_subject) else: self.UTILS.reporting.logResult("info", "incorrect value received") self.UTILS.test.test(False, "Incorrect value received") # Search for the contact and check it's been added self.addContactToField(contact_name) # Send the mms. self.UTILS.reporting.logResult("info", "Clicking on Send button") self.sendSMS() def forwardMessageToMultipleRecipients(self, msg_type, target_telNum, contact_name): self.UTILS.reporting.logResult( 'info', "The message type to forward is: {}".format(msg_type)) if msg_type == "sms" or msg_type == "mms": # Open sms option with longtap on it self.UTILS.reporting.logResult( "info", "Open sms option with longtap on it") sms = self.last_message_in_this_thread() body = self.marionette.find_element( *DOM.Messages.last_message_body, id=sms.id) self._select_forward_option_for(body) elif msg_type == "mmssub": # Open mms option with longtap on it self.UTILS.reporting.logResult( "info", "Open mms with subject options with longtap on it") mms_subject = self.UTILS.element.getElement( DOM.Messages.received_mms_subject, "Target MMS field") self._select_forward_option_for(mms_subject) else: self.UTILS.reporting.logResult("info", "incorrect value received") self.UTILS.test.test(False, "Incorrect value received") # Add phone numbers self.addNumbersInToField([target_telNum]) # Search for the contact and check it's been added self.addContactToField(contact_name) # Send the mms. self.UTILS.reporting.logResult("info", "Clicking on Send button") self.sendSMS() def get_mms_attachments_info(self, mms): """Give name and file size for all attachments in an MMS. Given an MMS, return a list containing a dictionary for every attachment, with two keys, name and size. """ attachment_names = self.marionette.find_elements( *DOM.Messages.mms_attachment_names, id=mms.id) attachment_sizes = self.marionette.find_elements( *DOM.Messages.mms_attachment_sizes, id=mms.id) result = [] for (i, name) in enumerate(attachment_names): inner_text = self.marionette.execute_script( """return arguments[0].innerHTML;""", script_args=[name]) att = {} att["name"] = inner_text size_elem = attachment_sizes[i].get_attribute("data-l10n-args") size = size_elem[size_elem.index(":") + 2:size_elem.rfind("\"")] i = i + 1 att["size"] = size result.append(att) return result def getThreadText(self, num): """ Returns the preview text for the thread for this number (if it exists), or False if either the thread doesn't exist or can't be found. """ if self.threadExists(num): x = self.UTILS.element.getElements(DOM.Messages.threads_list, "Threads") for thread in x: try: thread.find_element("xpath", ".//p[text()='{}']".format(num)) z = thread.find_element("xpath", ".//span[@class='body-text']") return z.text except: pass return False def header_addToContact(self): """ Taps the header and tries to tap the 'Add to an existsing contact' button. - Assumes we are looking at a message thread already. - Leaves you in the correct iframe to continue (contacts). """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() x = self.UTILS.element.getElement( DOM.Messages.header_add_to_contact_btn, "'Add to an existing contact' button") x.tap() # Switch to correct iframe. self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) def header_call(self): """Tap on the header of a messages thread and dial the number """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() # Select dialer option. x = self.UTILS.element.getElement(DOM.Messages.header_call_btn, "'Call' button") x.tap() # Switch to correct iframe. self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator) def header_createContact(self): """ Taps the header and tries to tap the 'send message' button. - Assumes we are looking at a message thread already. - Leaves you in the correct iframe to continue. """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() x = self.UTILS.element.getElement( DOM.Messages.header_create_new_contact_btn, "'Create new contact' button") x.tap() # Switch to correct iframe. self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) def header_sendMessage(self): """ Taps the header and tries to tap the 'send message' button. - Assumes we are looking at a message thread already. - Leaves you in the correct iframe to continue. """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() x = self.UTILS.element.getElement(DOM.Messages.header_send_message_btn, "'Send message' button") x.tap() def last_message_in_this_thread(self): """ Returns an object of the last message in the current thread. """ self.parent.wait_for_element_present(*DOM.Messages.last_message, timeout=20) message = self.marionette.find_element(*DOM.Messages.last_message) self.UTILS.element.scroll_into_view(message) return message def last_sent_message_timestamp(self): """Returns the timestamp of the last sent message """ send_time = self.marionette.find_element( *DOM.Messages.last_sent_message).get_attribute("data-timestamp") return float(send_time) / 1000 def last_sent_message(self): """Returns the last sent message """ return self.marionette.find_element(*DOM.Messages.last_sent_message) def open_attached_file(self, frame_to_change): elem = DOM.Messages.last_message_attachment_av last = self.UTILS.element.getElement(elem, "Last message attachment") self.UTILS.element.scroll_into_view(last) # Now get the thumbnail in order to open it thumb = last.find_element(*DOM.Messages.last_message_thumbnail) thumb.tap() self.UTILS.iframe.switchToFrame(*frame_to_change) def openThread(self, num): """ Opens the thread for this number (assumes we're looking at the threads in the messaging screen). """ try: thread_el = ("xpath", DOM.Messages.thread_selector_xpath.format(num)) x = self.UTILS.element.getElement(thread_el, "Message thread for " + num) x.tap() self.UTILS.element.waitForElements( DOM.Messages.send_message_button, "'Send' button") except Exception as e: x = self.UTILS.debug.screenShotOnErr() msg = "<b>NOTE:</b> The thread <i>may</i> have failed to open due to [{}].".format( e) self.UTILS.reporting.logResult("info", msg, x) def readLastSMSInThread(self): """ Read last message in the current thread. """ received_message = self.UTILS.element.getElements( DOM.Messages.received_messages, "Received messages")[-1] return str(received_message.text) def readNewSMS(self, fromNum): """ Read and return the value of the new message received from number. """ x = self.UTILS.element.getElement( ("xpath", DOM.Messages.messages_from_num.format(fromNum)), "Message from '" + fromNum + "'") x.tap() # (From gaiatest: "TODO Due to displayed bugs I cannot find a good wait # for switch btw views") time.sleep(5) # Return the last comment in this thread. return self.readLastSMSInThread() def removeContactFromToField(self, target): """ Removes target from the "To" field of this SMS.<br> Returns True if it found the target, or False if not. """ x = self.UTILS.element.getElements(DOM.Messages.target_numbers, "'To:' field contents") for i in range(len(x)): self.UTILS.reporting.logResult( "info", "Checking target '{}' to '{}' ...".format(x[i].text, target)) if x[i].text.lower() == target.lower(): self.UTILS.reporting.logResult( "info", "Tapping contact '" + target + "' ...") x[i].tap() try: # This contact was added via 'add contact' icon. self.parent.wait_for_element_displayed( "xpath", "//button[text()='{}']".format(_("Remove")), timeout=2) y = self.marionette.find_element( "xpath", "//button[text()='{}']".format(_("Remove"))) self.UTILS.reporting.logResult("info", "Tapping 'Remove' button.") y.tap() return True except: # This contact was manually entered. z = self.UTILS.element.getElements( DOM.Messages.target_numbers, "Target to be removed")[i] z.clear() return True return False def selectAddContactButton(self): """ Taps the 'add contact' button """ add_btn = self.UTILS.element.getElement( DOM.Messages.add_contact_button, "Add contact button") add_btn.tap() def sendSMS(self): """ Just presses the 'send' button (assumes everything else is done). """ self.parent.wait_for_condition(lambda m: m.find_element( *DOM.Messages.send_message_button).is_enabled()) send_btn = self.marionette.find_element( *DOM.Messages.send_message_button) send_btn.tap() # (Give the spinner time to appear.) time.sleep(2) self.UTILS.element.waitForNotElements( DOM.Messages.message_sending_spinner, "'Sending' icon", True, 180) # Check if we received the 'service unavailable' message. try: self.parent.wait_for_element_displayed( *DOM.Messages.service_unavailable_msg, timeout=2) screenshot = self.UTILS.debug.screenShotOnErr() msg = "'Service unavailable' message detected - unable to send sms!" self.UTILS.reporting.logResult("info", msg, screenshot) return False except: pass return True def startNewSMS(self): """ Starts a new sms (doesn't fill anything in). Assumes the Messaging app is already launched. """ newMsgBtn = self.UTILS.element.getElement( DOM.Messages.create_new_message_btn, "Create new message button") newMsgBtn.tap() def threadCarrier(self): # Returns the 'carrier' being used by this thread. x = self.UTILS.element.getElement(DOM.Messages.type_and_carrier_field, "Type and carrier information") parts = x.text.split("|") if len(parts) > 1: return parts[1].strip() return parts[0].strip() def threadEditModeOFF(self): """ Turns off Edit mode while in the SMS threads screen. """ x = self.UTILS.element.getElement(DOM.Messages.cancel_edit_threads, "Cancel button") x.tap() self.UTILS.element.waitForElements(DOM.Messages.edit_threads_button, "Edit button") def threadEditModeON(self): """ Turns on Edit mode while in the SMS threads screen. """ edit_btn = self.UTILS.element.getElement( DOM.Messages.edit_threads_button, "Edit button") edit_btn.tap() self.UTILS.element.waitForElements(DOM.Messages.cancel_edit_threads, "Cancel button") def threadExists(self, num): """ Verifies that a thread exists for this number (returns True or False). """ boolOK = False try: self.parent.wait_for_element_present( "xpath", DOM.Messages.thread_selector_xpath.format(num), 1) boolOK = True except: boolOK = False return boolOK def threadType(self): """ Returns the 'type' being used by this thread. """ x = self.UTILS.element.getElement(DOM.Messages.type_and_carrier_field, "Type and carrier information") parts = x.text.split("|") typ = parts[0].strip() return typ if len(parts) > 1 else '' def time_of_last_message_in_thread(self): """ Returns the time of the last message in the current thread. """ t = self.UTILS.element.getElement(DOM.Messages.last_message_time, "Last message time") return t.text def time_of_thread(self, num): """ Returns the time of a thread. """ x = self.UTILS.element.getElement( ("xpath", DOM.Messages.thread_timestamp_xpath.format(num)), "Thread time", True, 5, False) return x.text def thread_timestamp(self, num): """ Returns the timestamp of a thread """ x = self.marionette.find_element( *DOM.Messages.last_message).get_attribute("data-timestamp") return float(x) def verify_mms_received(self, attached_type, sender_number): self.UTILS.iframe.switchToFrame(*DOM.Messages.frame_locator) self.openThread(sender_number) message = self.last_message_in_this_thread() self.UTILS.test.test(message, "A received message appeared in the thread.", True) self.UTILS.reporting.debug( "*** attached type: {}".format(attached_type)) if attached_type == "img": elem = DOM.Messages.last_message_attachment_img elif attached_type == "video": elem = DOM.Messages.last_message_attachment_av elif attached_type == "audio": elem = DOM.Messages.last_message_attachment_av else: # self.UTILS.reporting.logResult("info", "incorrect value received") msg = "FAILED: Incorrect parameter received in verify_mms_received()"\ ". Attached_type must be image, video or audio." self.UTILS.test.test(False, msg) self.UTILS.reporting.debug("*** searching for attachment type") # Look for all the attachments, since there can be more than one atts = self.UTILS.element.getElements(elem, "Last message attachments") self.UTILS.element.scroll_into_view(atts[0]) found = False for att in atts: typ = att.get_attribute("data-attachment-type") self.UTILS.reporting.debug( "*** searching for attachment type Result: {}".format(typ)) if typ == attached_type: found = True if not found: msg = "No attachment with type {} was found in the message".format( attached_type) self.UTILS.test.test(False, msg) def wait_for_message(self, send_time=None, timeout=120): """Wait for a received message in the current thread and return it. """ if not send_time: send_time = self.last_sent_message_timestamp() poll_time = 2 poll_reps = (timeout / poll_time) result = False for i in range(poll_reps): # Get last message in this thread. last_msg = self.last_message_in_this_thread() if not last_msg: time.sleep(poll_time) continue # If the send_time timestamp is greater than this message's timestamp,it means the message # we expect has not arrived yet, so we have to wait a bit more. message_data_time = float( last_msg.get_attribute("data-timestamp")) / 1000 fmt = "data-timestamp of last message in thread: {:.3f} send_time: {:.3f} --> {}" self.UTILS.reporting.debug( fmt.format(message_data_time, send_time, send_time > message_data_time)) if send_time > message_data_time: continue # Is this a received message? if "incoming" in last_msg.get_attribute("class"): result = last_msg break # Nope - sleep then try again. time.sleep(poll_time) self.UTILS.test.test( result, "Last message in thread 'received' within {} seconds.".format( timeout)) return result
class EverythingMe(object): def __init__(self, p_parent): self.apps = p_parent.apps self.data_layer = p_parent.data_layer self.parent = p_parent self.marionette = p_parent.marionette self.UTILS = p_parent.UTILS self.actions = Actions(self.marionette) def launch(self): self.apps.kill_all() # If EME has already been launched, then the DOM has changed. self.UTILS.reporting.logResult("info", "Launching Everything ME.") boolOK = False try: self.parent.wait_for_element_displayed(*DOM.EME.start_eme_icon, timeout=1) x = self.marionette.find_element(*DOM.EME.start_eme_icon) x.tap() boolOK = True except: self.UTILS.reporting.logResult("info", "Everything ME is already 'running', so just waking it up ...") self._relaunch() try: self.parent.wait_for_element_displayed(*DOM.EME.groups, timeout=3) except: self._relaunch() boolOK = True self.UTILS.test.test(boolOK, "EME Starting up ...") def _relaunch(self): """ Private function to re-launch. This gets complicated: 1. el.tap() and el.click() only work *sometimes*, so use the keyboard to relaunch. 2. Sometimes the messges app randomly launches instead of evme! """ x = self.marionette.find_element(*DOM.EME.search_field) x.send_keys("waking up evme") x = self.marionette.find_element(*DOM.EME.search_clear) x.tap() def add_app_to_homescreen(self, name): """ Pick an app from the apps listed in this group. """ x = self.UTILS.element.getElementByXpath(DOM.EME.app_to_install.format(name)) app_name = x.text self.UTILS.reporting.logResult("debug", "icon displayed: {}".format(x.is_displayed())) time.sleep(2) self.UTILS.test.test(app_name == name, "" + app_name + "'is the correct app", True) self.actions.long_press(x, 2).perform() x = self.UTILS.element.getElement(DOM.EME.add_to_homescreen_btn, "Add app to homescreen button") x.tap() time.sleep(2) return True def add_group(self, group): """ Adds a group to EME (assumes you're already in the EME group screen). """ self.UTILS.reporting.logResult("info", "(Adding group '" + group + "'.)") # Click the 'More' icon. x = self.UTILS.element.getElement(DOM.EME.add_group_button, "'More' icon") x.tap() # Wait for the 'loading' spinner to go away (can take a while!). self.UTILS.element.waitForNotElements(DOM.EME.loading_groups_message, "'Loading' message", True, 120) # Chose an item from the groups list... self.UTILS.general.selectFromSystemDialog(group) # Verify the new group is in the groups list. x = self.UTILS.element.getElements(DOM.EME.groups, "Groups") boolOK = False for i in x: if i.get_attribute("data-query") == group: boolOK = True break self.UTILS.test.test(boolOK, "New group '" + group + "' is now present in the EME groups.") return boolOK def add_multiple_groups(self, group_array=False): """ Adds multiple groups based on an array of numbers (defaults to all available groups). <br><br> For example: add_multiple_groups([0,1,2,3,8,11]) ... or just: add_multiple_groups() """ x = self.UTILS.element.getElement(DOM.EME.add_group_button, "'More' icon") x.tap() self.UTILS.element.waitForNotElements(DOM.EME.loading_groups_message, "'Loading' message", True, 120) # Switch to group selector (in top level iframe). self.marionette.switch_to_frame() # for checking later list_names = [] elements = self.UTILS.element.getElements(DOM.GLOBAL.modal_valueSel_list, "Groups list", False) for i in range(len(elements)): if i > 0: # Keep shuffling the groups into view so they can be tapped. self.actions.press(elements[i]).move(elements[i - 1]).wait(0.5).release().perform() elements = self.marionette.find_elements(*DOM.GLOBAL.modal_valueSel_list) # Only select it if it's the list, or there is no list. select_elements = False if group_array: if len(group_array) == len(list_names): # # We've done all of them - stop looping! break if i in group_array: select_elements = True else: select_elements = True if select_elements: tmp_name = elements[i].find_element("tag name", "span").text self.UTILS.reporting.logResult("info", "Selecting '{}' ...".format(tmp_name)) list_names.append(tmp_name) elements[i].tap() # Sometimes the first tap does nothing for some reason. if not elements[i].get_attribute("aria-checked"): elements[i].tap() # Click the OK button. x = self.UTILS.element.getElement(DOM.GLOBAL.modal_valueSel_ok, "OK button") try: # Sometimes it's one, sometimes the other ... x.tap() x.click() except: pass time.sleep(1) # Checkk all the items we expect are now loaded in evme. self.UTILS.iframe.switchToFrame(*DOM.Home.frame_locator) time.sleep(5) for name in list_names: ok = False # Reload the groups (silently or we'll have loads of these messages!). try: x = self.marionette.find_elements(*DOM.EME.groups) except: break for i in x: group_name = i.get_attribute("data-query") if group_name == name: ok = True break self.UTILS.test.test(ok, "'{}' is now among the groups.".format(name)) def install_app(self, category, name, expect_btn=True): """Try to install an application. Try to install the application with the given name in the given category. expect_btn determines if we expect the "Add to Home Screen" button in the bookmark page. Returns True if the application was successfully installed. """ self.pick_group(category) self.UTILS.iframe.switchToFrame(*DOM.EME.frame_locator) app_name = self.UTILS.element.getElementByXpath(DOM.EME.app_to_install.format(name)).text # Add the app to the homescreen. self.add_app_to_homescreen(app_name) self.UTILS.iframe.switchToFrame(*DOM.EME.bookmark_frame_locator) time.sleep(4) result = False # We expect the application to be installed, so find the Add to Home Screen button and tap it if expect_btn: add_btn = self.UTILS.element.getElement(DOM.EME.add_to_homescreen_done_btn, "Add bookmark to Home Screen Button") add_btn.tap() result = True else: # We expect the application is already installed, so find the proper header title = self.UTILS.element.getElement(DOM.EME.edit_bookmark_header, "Edit link header") self.UTILS.test.test(title, "Title '{}' found".format(title.text)) return result def launch_from_group(self, app_name): """ Function to launch an app directly from an EME group. """ x = self.UTILS.element.getElement(("xpath", "//li[@data-name='{}']".format(app_name)), "Icon for app '{}'".format(app_name), False) try: x.tap() except: # App is not visible, so I need to move it into view first. _id = x.get_attribute("_id") self.marionette.execute_script("document.getElementById('{}').scrollIntoView();".format(_id)) x.tap() time.sleep(1) self.UTILS.element.waitForNotElements(DOM.EME.launched_activity_bar, "Activity notifier", True, 30) x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "Screenshot of app running:", x) def pick_group(self, name): """ Pick a group from the main icons. """ screenshot = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "<b>Choosing group '{}' from here ...</b>".format(name), screenshot) ok = False self.UTILS.reporting.info("searching for element: {}".format(DOM.Home.app_icon_css_selector.format(name))) icon = self.marionette.find_element('css selector', DOM.Home.app_icon_css_selector.format(name)) self.UTILS.reporting.logResult("debug", "icon displayed: {}".format(icon.is_displayed())) icon.tap() try: self.UTILS.iframe.switchToFrame(*DOM.EME.frame_locator) self.parent.wait_for_element_displayed(*DOM.EME.apps_not_installed, timeout=20) self.UTILS.reporting.logResult("info", "(Apps for group {} were displayed.)".format(name)) ok = True except Exception as e: self.UTILS.reporting.debug("*** Error getting apps not installed: {}".format(e)) screenshot = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "(<b>NOTE:</b>Apps for group {} were not displayed.)|{}|{}".\ format(name, screenshot[0], screenshot[1])) return ok def remove_groups(self, group_array): """ Removes groups from the EME group page. group_array is an array of group names (default = all groups) For example: remove_groups(["Games","Local"]) """ """ Put the groups into edit mode. Sometimes this takes a while to happen, so increase the length of time you press the icon until it works! """ ok = False x = self.marionette.find_element('xpath', DOM.Home.app_icon_css_selector.format(group_array[0])) self.actions.press(x).wait(3).release() try: actions.perform() except: pass try: x = self.UTILS.element.getElement(("xpath", DOM.Home.app_delete_icon_xpath.format(group_array[0])), "Delete button", False, 5, True) if x.is_displayed(): ok = True except: pass time.sleep(2) self.UTILS.test.test(ok, "Enabled EDIT mode.") x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "Screenshot of app in EDIT mode:", x) # Remove all groups in the array. removed = 0 group_cnt = len(group_array) self.UTILS.reporting.logResult("info", "Removing {} groups".format(group_cnt)) self.UTILS.reporting.logResult("info", "Removing groups: {}".format(group_array)) for group_specified in group_array: # Remove it. self.marionette.find_element('xpath', DOM.Home.app_icon_css_selector.format(group_specified)) y = self.UTILS.element.getElement(("xpath", DOM.Home.app_delete_icon_xpath.format(group_specified)), "Delete button", False, 5, True) y.tap() delete = self.UTILS.element.getElement(DOM.Home.app_confirm_delete, "Confirm app delete button") delete.tap() removed = removed + 1 self.UTILS.reporting.logResult("info", "Removed group '{}' ...".format(group_specified)) self.UTILS.reporting.logResult("info", "Removed {} groups".format(removed)) if removed == group_cnt: break # Turn off edit mode. self.UTILS.reporting.logResult("info", "Disabling edit mode ...") self.UTILS.home.touchHomeButton() def search_for_app(self, name): """ Uses the search field to find the app (waits for the result to appear etc...).<br> Returns the element for the icon (or False if it's not found). """ x = self.UTILS.element.getElement(DOM.EME.search_field, "Search field") x.clear() x.send_keys(name) x.click() time.sleep(5) # Can take a few seconds to appear, so try a few times (about 1 min). for retry in range(10): x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("debug", "Looking for '{}' - attempt {} ...".format(name, retry), x) x = self.UTILS.element.getElements(DOM.EME.search_suggestions, "Search suggestions") ok = False for i in x: i_name = i.get_attribute("data-suggestion") if i_name: i_name = i_name.lower() i_name = i_name.replace("[", "") i_name = i_name.replace("]", "") is_in = False for i2 in name.lower().split(): self.UTILS.reporting.logResult("debug", "Is '{}' in '{}'?".format(i2, i_name)) if i2 not in i_name: is_in = False break else: is_in = True if is_in: i.tap() ok = True break if ok: break time.sleep(6) self.UTILS.test.test(ok, "Found '%s' in suggestions." % name) ok = True try: elem = ("xpath", DOM.EME.search_result_icon_xpath.format(name)) self.parent.wait_for_element_displayed(*elem, timeout=60) x = self.marionette.find_element(*elem) return x except: ok = False return ok
class Dialer(object): """Object representing the Dialer application. """ def __init__(self, p_parent): self.apps = p_parent.apps self.data_layer = p_parent.data_layer self.parent = p_parent self.marionette = p_parent.marionette self.UTILS = p_parent.UTILS self.actions = Actions(self.marionette) self.app_name = "Phone" def launch(self): """ Launch the app It's called a different name (Phone) to the everyone knows it as, so hardcode it! """ self.app = self.apps.launch(self.app_name) self.UTILS.element.waitForNotElements(DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") return self.app def _cancel_addNumberToContact(self): self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) self.UTILS.element.waitForElements( ("xpath", "//h1[text()='{}']".format(_("Contacts"))), "'Select contact' header") self.UTILS.element.getElement(DOM.Dialer.contact_list_header, "Header").tap(25, 25) def _complete_addNumberToContact(self, p_num, p_name): """ Finishes the process of adding a number to an existing contact (used bu addThisNumberToContact() etc...).<br> Handles switching frames etc... and finishes with you back in the dialer. """ self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) self.UTILS.element.waitForElements( ("xpath", "//h1[text()='{}']".format(_("Contacts"))), "'Select contact' header") y = self.UTILS.element.getElements(DOM.Contacts.view_all_contact_list, "All contacts list") boolOK = False for i in y: if p_name in i.text: self.UTILS.reporting.logResult("info", "Contact '{}' found in all contacts.".format(p_num)) i.tap() boolOK = True break self.UTILS.test.test(boolOK, "Succesfully selected contact from list.") self.UTILS.element.waitForElements(DOM.Contacts.edit_contact_header, "'Edit contact' header") # Test for an input field for number_<x> contaiing our number. self.UTILS.element.waitForElements(("xpath", DOM.Contacts.phone_field_xpath.format(p_num)), "Phone field containing {}".format(p_num)) # Hit 'update' to save the changes to this contact. done_button = self.UTILS.element.getElement(DOM.Contacts.edit_update_button, "'Update' button") done_button.tap() # Verify that the contacts app is closed and we are returned to the call log. self.marionette.switch_to_frame() self.UTILS.element.waitForNotElements(("xpath", "//iframe[contains(@{}, '{}')]". format(DOM.Contacts.frame_locator[0], DOM.Contacts.frame_locator[1])), "COntacts frame") self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator) def addThisNumberToContact(self, p_name): # Adds the current number to existing contact. x = self.UTILS.element.getElement(DOM.Dialer.phone_number, "Phone number field", False) dialer_num = x.get_attribute("value") x = self.UTILS.element.getElement(DOM.Dialer.add_to_contacts_button, "Add to contacts button") x.tap() x = self.UTILS.element.getElement(DOM.Dialer.add_to_existing_contact_btn, "Add to existing contact button") x.tap() self._complete_addNumberToContact(dialer_num, p_name) def callLog_createContact(self, entry, p_open_call_log=True): """ Creates a new contact from the call log (only as far as the contacts app opening). If p_open_call_log is set to False it will assume you are already in the call log. """ if p_open_call_log: self.open_call_log() self.callLog_long_tap(entry) self.callLog_long_tap_select_action( DOM.Dialer.call_log_numtap_create_new, "Create new contact button", call_info=True) self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) self.UTILS.element.waitForElements(DOM.Contacts.add_contact_header, "'Add contact' header") def callLog_addToContact(self, phone_number, contact_name, p_open_call_log=True, cancel_process=False): """ Adds this number in the call log to an existing contact (and returns you to the call log). If p_open_call_log is set to False it will assume you are already in the call log. """ if p_open_call_log: self.open_call_log() self.callLog_long_tap(phone_number) time.sleep(1) self.callLog_long_tap_select_action( DOM.Dialer.call_log_numtap_add_to_existing, "Add to existing contact button", call_info=True) if cancel_process: self._cancel_addNumberToContact() else: self._complete_addNumberToContact(phone_number, contact_name) def callLog_long_tap(self, phone_number): entry = self.UTILS.element.getElement(("xpath", DOM.Dialer.call_log_number_xpath.format(phone_number)), "The call log for number {}".format(phone_number)) self.actions.long_press(entry, 3).perform() def callLog_long_tap_select_action(self, locator, msg="Option chosen", call_info=True): if call_info: call_info = self.UTILS.element.getElement(DOM.Dialer.call_log_numtap_call_info, "Call information button") call_info.tap() option = self.UTILS.element.getElement(locator, msg, True, 10) option.tap() def callLog_call(self, p_num): own_num = self.UTILS.general.get_config_variable("phone_number", "custom") # Calls a number from the call log. try: self.parent.wait_for_element_displayed(*DOM.Dialer.call_log_filter, timeout=1) except: self.open_call_log() entry = self.UTILS.element.getElement(("xpath", DOM.Dialer.call_log_number_xpath.format(p_num)), "The call log for number {}".format(p_num)) entry.tap() if own_num == p_num: time.sleep(2) # self.marionette.switch_to_frame() x = self.UTILS.element.getElement(DOM.Dialer.call_busy_button_ok, "OK button (callLog_call)") x.tap() else: time.sleep(1) self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator_calling) self.UTILS.element.waitForElements(("xpath", DOM.Dialer.outgoing_call_numberXP.format(p_num)), "Outgoing call found with number matching {}".format(p_num)) def callLog_clearAll(self): """ Wipes all entries from the call log. """ try: self.parent.wait_for_element_displayed(*DOM.Dialer.call_log_filter, timeout=1) except: self.open_call_log() try: self.parent.wait_for_element_displayed(*DOM.Dialer.call_log_no_calls_msg, timeout=1) except: self.UTILS.reporting.logResult("info", "Some numbers are in the call log here - removing them ...") edit_btn = self.UTILS.element.getElement(DOM.Dialer.call_log_edit_btn, "Edit button") edit_btn.tap() """Here something weird is happening, since the form where this button should appear, remains hidden. Thus, when we try to get the button, it won't appear. Then, we have have to use raw JavaScript in order to obtain the button and click on it.""" self.parent.wait_for_element_present(*DOM.Dialer.call_log_edit_selAll, timeout=10) self.marionette.execute_script("document.getElementById('{}').click();". format(DOM.Dialer.call_log_edit_selAll[1])) self.parent.wait_for_element_present(*DOM.Dialer.call_log_edit_delete, timeout=2) self.marionette.execute_script("document.getElementById('{}').click();". format(DOM.Dialer.call_log_edit_delete[1])) confirm_delete = self.UTILS.element.getElement(DOM.Dialer.call_log_confirm_delete, "Confirm button") confirm_delete.tap() self.UTILS.element.waitForElements(DOM.Dialer.call_log_no_calls_msg, "'No calls ...' message") def callLog_clearSome(self, p_entryNumbers): """ Wipes entries from the call log, using p_entryNumbers as an array of numbers. For example: callLog_clearSome([1,2,3]) will remove the first 3. <br><b>NOTE:</b> the first item is 1, <i>not</i> 0. """ try: self.parent.wait_for_element_displayed(*DOM.Dialer.call_log_filter, timeout=1) except: self.open_call_log() boolLIST = True try: self.parent.wait_for_element_displayed(*DOM.Dialer.call_log_no_calls_msg, timeout=1) boolLIST = False except: pass if boolLIST: """ At the moment, the 'edit' div looks like it's not displayed, so Marionette can't tap it. For this reason I'm using JS to click() instead. """ self.UTILS.reporting.logResult("info", "Some numbers are in the call log here - removing them ...") x = self.UTILS.element.getElement(DOM.Dialer.call_log_edit_btn, "Edit button") x.tap() """ The edit mode doens't seem to be 'displayed', so we have to work around that at the moment. """ time.sleep(2) self.parent.wait_for_element_present(*DOM.Dialer.call_log_edit_header, timeout=2) _els = ("xpath", "//ol[@class='log-group']//li") x = self.UTILS.element.getElements(_els, "Call log items", False) _precount = len(x) self.UTILS.reporting.logResult("info", "{} items found.".format(_precount)) for i in p_entryNumbers: if i != 0: _precount = _precount - 1 x[i - 1].tap() self.parent.wait_for_element_present(*DOM.Dialer.call_log_edit_delete, timeout=2) self.marionette.execute_script("document.getElementById('{}').click();". format(DOM.Dialer.call_log_edit_delete[1])) # Click on Delete button delete_btn = self.UTILS.element.getElement(DOM.GLOBAL.confirm_form_delete_btn, "Confirm delete") delete_btn.tap() try: _postcount = self.UTILS.element.getElements(_els, "Call log items", False) _postcount = len(_postcount) except: _postcount = 0 self.UTILS.test.test(_postcount == _precount, "{} numbers are left after deletion (there are {}).".format(_precount, _postcount)) def tap_on_call_button(self): call_number_button = self.UTILS.element.getElement(DOM.Dialer.call_number_button, "Call number button") self.UTILS.element.simulateClick(call_number_button) def call_this_number(self): """ Calls the current number. """ self.tap_on_call_button() self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator_calling) self.UTILS.element.waitForElements(DOM.Dialer.outgoing_call_locator, "Outgoing call locator", True, 5) def call_this_number_and_hangup(self, delay): self.call_this_number() time.sleep(delay) self.hangUp() def get_and_accept_fdn_warning(self, phone_number): # Check warning message self.UTILS.element.waitForElements(DOM.Settings.fdn_warning_header, "Waiting for FDN warning header", True, 10) self.UTILS.element.waitForElements(DOM.Settings.fdn_warning_body, "Waiting for FDN warning body") body = self.marionette.find_element(*DOM.Settings.fdn_warning_body) self.UTILS.reporting.log_to_file("body.text: {} msg: {}".format(body.text, DOM.Dialer.fdn_warning_msg. format(phone_number))) self.UTILS.test.test(body.text == DOM.Dialer.fdn_warning_msg.format(phone_number), "Correct FDN warning message") ok_btn = self.UTILS.element.getElement(DOM.Settings.fdn_warning_ok, "OK button") ok_btn.tap() def createContactFromThisNum(self): """ Creates a new contact from the number currently in the dialer (doesn't fill in the contact details). """ self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator) add_btn = self.UTILS.element.getElement(DOM.Dialer.add_to_contacts_button, "Add to contacts button") add_btn.tap() create_btn = self.UTILS.element.getElement(DOM.Dialer.create_new_contact_btn, "Create new contact button") create_btn.tap() # Switch to the contacts frame. self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) def createMultipleCallLogEntries(self, phone_number, entries_number): """ Put a number in the call log multiple times (done by manipulating the device time). Leaves you in the call log. """ today = datetime.datetime.today() for i in range(entries_number): delta = datetime.timedelta(days=i) new_date = today - delta self.UTILS.date_and_time.setTimeToSpecific(p_day=new_date.day, p_month=new_date.month) self.enterNumber(phone_number) self.call_this_number_and_hangup(delay=7) # This needs to be done bcs sometimes (50%) the Dialer app crushes after hanging up self.apps.kill_all() time.sleep(2) self.launch() def enterNumber(self, number, validate=True): """ Enters a number into the dialer using the keypad. """ try: self.parent.wait_for_element_displayed(*DOM.Dialer.phone_number, timeout=1) except: keypad_selector = self.UTILS.element.getElement(DOM.Dialer.option_bar_keypad, "Keypad option selector") keypad_selector.tap() self.UTILS.element.waitForElements(DOM.Dialer.phone_number, "Phone number area") for i in str(number): if i == "+": plus_symbol = self.UTILS.element.getElement(("xpath", DOM.Dialer.dialer_button_xpath.format(0)), "keypad symbol '+'") self.actions.long_press(plus_symbol, 2).perform() else: number_symbol = self.UTILS.element.getElement(("xpath", DOM.Dialer.dialer_button_xpath.format(i)), "keypad number {}".format(i)) number_symbol.tap() # Verify that the number field contains the expected number. if validate: dialed_num = self.marionette.execute_script("return window.wrappedJSObject.KeypadManager._phoneNumber") self.UTILS.reporting.debug(u"** Dialer_num entered: [{}]".format(dialed_num)) self.UTILS.test.test( str(number) in dialed_num, u"Dialed number matches the original number".format(number, dialed_num)) def clear_dialer(self): """ Deletes a single number from the dialer """ try: self.parent.wait_for_element_displayed(*DOM.Dialer.keypad_delete) delete = self.marionette.find_element(*DOM.Dialer.keypad_delete) delete.tap() except: return def clear_dialer_all(self): """ Clears all dialer input area """ try: self.parent.wait_for_element_displayed(*DOM.Dialer.keypad_delete) delete = self.marionette.find_element(*DOM.Dialer.keypad_delete) self.actions.long_press(delete, 3).perform() except: return def answer(self): self.marionette.switch_to_frame() elDef = ("xpath", "//iframe[contains(@{}, '{}')]". format(DOM.Dialer.frame_locator_calling[0], DOM.Dialer.frame_locator_calling[1])) self.parent.wait_for_element_displayed(*elDef, timeout=60) frame_calling = self.marionette.find_element(*elDef) if frame_calling: self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator_calling) self.parent.wait_for_element_displayed(*DOM.Dialer.answer_callButton, timeout=1) answer = self.marionette.find_element(*DOM.Dialer.answer_callButton) if answer: answer.tap() def answer_and_hangup(self, delay=5): self.answer() time.sleep(delay) self.parent.wait_for_element_displayed(*DOM.Dialer.hangup_bar_locator, timeout=1) hangup = self.marionette.find_element(*DOM.Dialer.hangup_bar_locator) if hangup: hangup.tap() else: try: self.parent.data_layer.kill_active_call() except: self.UTILS.reporting.logResult("info", "Exception when killing active call via data_layer") pass def hangUp(self): """ Hangs up (assuming we're in the 'calling' frame). """ self.marionette.switch_to_frame() try: self.parent.wait_for_element_displayed(*DOM.Dialer.frame_locator_calling) self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator_calling) self.marionette.find_element(*DOM.Dialer.hangup_bar_locator).tap() self.apps.switch_to_displayed_app() except: self.apps.switch_to_displayed_app() self.parent.wait_for_element_displayed(*DOM.Dialer.call_busy_button_ok, timeout=5) ok_btn = self.marionette.find_element(*DOM.Dialer.call_busy_button_ok) ok_btn.tap() def open_call_log(self): """ Opens the call log. """ x = self.UTILS.element.getElement(DOM.Dialer.option_bar_call_log, "Call log button") x.tap() time.sleep(2) def wait_for_incoming_call(self, phone_number, timeout=30): self.marionette.switch_to_frame() try: self.parent.wait_for_element_displayed(*DOM.Dialer.call_screen_locator, timeout=60) frame = self.marionette.find_element(*DOM.Dialer.call_screen_frame_locator) self.marionette.switch_to_frame(frame) elem = ("xpath", DOM.Dialer.outgoing_call_numberXP.format(phone_number)) self.UTILS.element.waitForElements( elem, "Phone number [{}] is displayed in call_screen frame".format(phone_number)) except: self.UTILS.test.test(False, "No incoming call received", True) def resume_hidden_call(self): self.marionette.switch_to_frame() self.UTILS.statusbar.displayStatusBar() attention = self.marionette.find_element('id', 'attention-window-notifications-container') attention.tap() self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator_calling)
class Messages(object): def __init__(self, parent): self.apps = parent.apps self.data_layer = parent.data_layer self.parent = parent self.marionette = parent.marionette self.UTILS = parent.UTILS self.actions = Actions(self.marionette) @retry(5) def launch(self): self.app = self.apps.launch(self.__class__.__name__) self.UTILS.element.waitForNotElements(DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") return self.app def cancelSettings(self): self.UTILS.reporting.logResult("info", "Cliking on messages options button") options_btn = self.UTILS.element.getElement(DOM.Messages.messages_options_btn, "Messages option button is displayed") options_btn.tap() # Press cancel button cancelBtn = self.UTILS.element.getElement(DOM.Messages.cancel_btn_msg, "Press Cancel button") cancelBtn.tap() def deleteSubject(self, subject): self.UTILS.reporting.logResult("info", "Cliking on messages options button") x = self.UTILS.element.getElement(DOM.Messages.messages_options_btn, "Messages option button is displayed") x.tap() # Press add subject button self.UTILS.reporting.logResult("info", "Cliking on delete subject button") x = self.UTILS.element.getElement(DOM.Messages.deletesubject_btn_msg_opt, "delete subject option button is displayed") x.tap() def addSubject(self, subject): self.UTILS.reporting.logResult("info", "Cliking on messages options button") x = self.UTILS.element.getElement(DOM.Messages.messages_options_btn, "Messages option button is displayed") x.tap() # Press add subject button screenshot = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult('info', "Screenshot", screenshot) self.UTILS.reporting.logResult("info", "Cliking on add subject button") x = self.UTILS.element.getElement(DOM.Messages.addsubject_btn_msg_opt, "add subject option button is displayed") x.tap() self.UTILS.general.typeThis(DOM.Messages.target_subject, "Target Subject field", subject, p_no_keyboard=True, p_validate=False, p_clear=False, p_enter=False) def checkAirplaneModeWarning(self): """ Checks for the presence of the popup warning message if you just sent a message while in 'airplane mode' (also removes the message so you can continue). """ x = self.UTILS.element.getElement(DOM.Messages.airplane_warning_message, "Airplane mode warning message", True, 5, False) if x: self.UTILS.reporting.logResult("info", "Warning message title detected = '" + x.text + "'.") x = self.UTILS.element.getElement(DOM.Messages.airplane_warning_ok, "OK button") x.tap() def check_last_message_contents(self, expected, mms=False): """ Get the last message text and check it against the expected value. """ msg = self.last_message_in_this_thread() dom = DOM.Messages.last_message_mms_text if mms else DOM.Messages.last_message_text msg_text = self.marionette.find_element(*dom, id=msg.id) self.UTILS.test.test((msg_text and msg_text.text == expected), u"Expected message text = '{}' ({}) (got '{}' ({})).". format(expected, len(expected), msg_text.text, len(msg_text.text))) def checkIsInToField(self, target, targetIsPresent=True): """ Verifies if a number (or contact name) is displayed in the "To: " field of a compose message.<br> (Uses 'caseless' search for this.) """ time.sleep(1) x = self.UTILS.element.getElements(DOM.Messages.target_numbers, "'To:' field contents", False) boolOK = False for i in x: if i.text.lower() == str(target).lower(): boolOK = True break testMsg = "is" if targetIsPresent else "is not" testMsg = "\"" + str(target) + "\" " + testMsg + " in the 'To:' field." self.UTILS.test.test(boolOK == targetIsPresent, testMsg) return boolOK def checkMMSIcon(self, thread_name): # Get the thread for which we want to check the icon existence selector = ("xpath", DOM.Messages.thread_selector_xpath.format(thread_name)) elem = self.UTILS.element.getElement(selector, "Message thread for " + thread_name) """ But, in order to make sure we're getting the specific frame, what we trully got above is an inner child of the thread element. So, we gotta get the father """ thread = self.marionette.execute_script(""" return arguments[0].parentNode; """, script_args=[elem]) # Checks for the presence of the MMS icon icon = thread.find_element(*DOM.Messages.mms_icon) if icon: self.UTILS.test.test(icon is not None, "MMS icon detected for thread [{}]".format(thread_name)) def checkNumberIsInToField(self, target): """ Verifies if a number is contained in the "To: " field of a compose message (even if it's not displayed - i.e. a contact name is displayed, but this validates the <i>number</i> for that contact). """ x = self.UTILS.element.getElements(DOM.Messages.target_numbers, "'To:' field contents") boolOK = False for i in x: if i.get_attribute("data-number") == target: boolOK = True break self.UTILS.test.test(boolOK, "\"" + str(target) + "\" is the number in one of the 'To:' field targets.") return boolOK def checkThreadHeader(self, header): """ Verifies if a string is contained in the header """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Header") boolOK = False if x.get_attribute("data-number") == header: boolOK = True self.UTILS.test.test(boolOK, "\"" + str(header) + "\" is the header in the SMS conversation.") return boolOK def checkThreadHeaderWithNameSurname(self, header): """ Verifies if a string is contained in the header """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Header") boolOK = False if x.text == header: boolOK = True self.UTILS.test.test(boolOK, "\"" + header + "\" is the header in the SMS conversation.") return boolOK def closeThread(self): """ Closes the current thread (returns you to the 'thread list' SMS screen). """ self.go_back() self.UTILS.element.waitForElements(("xpath", "//h1[text()='{}']".format(_("Messages"))), "Messages main header") def countMessagesInThisThread(self): """ Returns the number of messages in this thread (assumes you're already in the thread). """ try: return len(self.UTILS.element.getElements(DOM.Messages.message_list, "Messages")) except: return 0 def countNumberOfThreads(self): """ Count all threads (assumes the messagin app is already open). """ try: return len(self.UTILS.element.getElements(DOM.Messages.threads_list, "Threads")) except: return 0 def create_and_send_mms(self, attached_type, nums, m_text): self.gallery = Gallery(self.parent) self.video = Video(self.parent) self.music = Music(self.parent) self.launch() self.startNewSMS() self.addNumbersInToField(nums) self.enterSMSMsg(m_text) if attached_type == "image": # Add an image file self.UTILS.general.add_file_to_device('./tests/_resources/80x60.jpg') self.create_mms_image() self.gallery.click_on_thumbnail_at_position_mms(0) elif attached_type == "cameraImage": # Add an image file from camera self.create_mms_camera_image() time.sleep(3) elif attached_type == "video": # Load an video file into the device. self.UTILS.general.add_file_to_device('./tests/_resources/mpeg4.mp4') self.create_mms_video() self.video.click_on_video_at_position_mms(0) elif attached_type == "audio": # Load an video file into the device. self.UTILS.general.add_file_to_device('./tests/_resources/AMR.amr') self.create_mms_music() self.music.click_on_song_mms() else: # self.UTILS.reporting.logResult("info", "incorrect value received") msg = "FAILED: Incorrect parameter received in create_and_send_mms()"\ ". attached_type must being image, video or audio." self.UTILS.test.test(False, msg) time.sleep(2) self.sendSMS() return self.last_sent_message_timestamp() def create_and_send_sms(self, nums, msg): """ Create and send a new SMS.<br> <b>Note:</b> The nums field must be an array of numbers or contact names. """ self.startNewSMS() self.addNumbersInToField(nums) self.enterSMSMsg(msg) # The header should now say how many recipients. time.sleep(2) # give the header time to change. num_recs = len(nums) search_str = _(" recipient") if num_recs == 1 else _(" recipients") self.UTILS.element.headerCheck(str(num_recs) + search_str) # Send the message. self.sendSMS() def create_mms_image(self): attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() gallery = self.UTILS.element.getElement(DOM.Messages.mms_from_gallery, "From gallery") gallery.tap() self.UTILS.iframe.switchToFrame(*DOM.Gallery.frame_locator) def create_mms_camera_image(self): self.camera = Camera(self.parent) attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() camera = self.UTILS.element.getElement(DOM.Messages.mms_from_camera, "From Camera") camera.tap() self.UTILS.iframe.switchToFrame(*DOM.Camera.frame_locator) # Take a picture. self.camera.take_and_select_picture() self.UTILS.iframe.switchToFrame(*DOM.Messages.frame_locator) def create_mms_music(self): attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() music = self.UTILS.element.getElement(DOM.Messages.mms_from_music, "From music") music.tap() self.UTILS.iframe.switchToFrame(*DOM.Music.frame_locator) def go_back(self): """Press back button in messages thread """ # TODO: remove tap with coordinates after Bug 1061698 is fixed self.UTILS.element.getElement(DOM.Messages.header_link_locator, "Back button").tap(25, 25) def create_mms_video(self): attach = self.UTILS.element.getElement(DOM.Messages.attach_button, "Attach button") attach.tap() self.marionette.switch_to_frame() video = self.UTILS.element.getElement(DOM.Messages.mms_from_video, "From video") video.tap() self.UTILS.iframe.switchToFrame(*DOM.Video.frame_locator) def delete_all_threads(self): """ Deletes all threads (assumes the messagin app is already open). """ try: self.parent.wait_for_element_displayed(*DOM.Messages.no_threads_message, timeout=3) no_threads_message = self.marionette.find_element(*DOM.Messages.no_threads_message) if no_threads_message.is_displayed(): self.UTILS.reporting.logResult("info", "(No message threads to delete.)") except: self.UTILS.reporting.logResult("info", "Deleting message threads ...") self.threadEditModeON() select_threads = self.UTILS.element.getElement( DOM.Messages.edit_msgs_select_threads_btn, "Delete threads button") select_threads.tap() select_all_btn = self.UTILS.element.getElement(DOM.Messages.check_all_threads_btn, "Select all button") select_all_btn.tap() self.deleteSelectedThreads() self.UTILS.element.waitForElements(DOM.Messages.no_threads_message, "No message threads notification", True, 60) def deleteMessagesInThisThread(self, msg_array=False): """ Enters edit mode, selects the required messages and deletes them.<br> msg_array is an array of numbers. If it's not specified then all messages in this thread will be deleted. """ if msg_array: self.editAndSelectMessages(msg_array) else: # Go into messages Settings.. edit_btn = self.UTILS.element.getElement(DOM.Messages.edit_messages_icon, "Edit button") edit_btn.tap() select_btn = self.UTILS.element.getElement(DOM.Messages.edit_msgs_select_btn, "Select button") select_btn.tap() # Press select all button. select_all_btn = self.UTILS.element.getElement(DOM.Messages.check_all_messages_btn, "'Select all' button") select_all_btn.tap() self.deleteSelectedMessages() def deleteSelectedMessages(self): self.UTILS.reporting.debug("*** Tapping top Delete button") delete_btn = self.UTILS.element.getElement(DOM.Messages.delete_messages_ok_btn, "Delete button") delete_btn.tap() time.sleep(2) self.UTILS.reporting.debug("*** Tap Delete messages confirmation button") confirm_btn = self.UTILS.element.getElement( DOM.Messages.delete_threads_ok_btn, "Delete messages confirmation button") confirm_btn.tap() time.sleep(2) def deleteSelectedThreads(self): delete_btn = self.UTILS.element.getElement(DOM.Messages.threads_delete_button, "Delete threads button") delete_btn.tap() delete_confirm_btn = self.UTILS.element.getElement( DOM.Messages.delete_threads_ok_btn, "Delete threads confirmation button") delete_confirm_btn.tap() def deleteThreads(self, target_array=False): """ Enters edit mode, selects the required messages and deletes them.<br> target_array is an array of target numbers or contacts which identify the threads to be selected. If it's not specified then all messages in this thread will be deleted. """ try: self.parent.wait_for_element_displayed(*DOM.Messages.no_threads_message, timeout=2) x = self.marionette.find_element(*DOM.Messages.no_threads_message) if x.is_displayed(): self.UTILS.reporting.logResult("info", "(No message threads to delete.)") except: self.UTILS.reporting.logResult("info", "Deleting message threads ...") if target_array: self.UTILS.reporting.debug("*** Selecting threads for deletion [{}]".format(target_array)) self.editAndSelectThreads(target_array) self.UTILS.reporting.debug("*** Threads selected") self.deleteSelectedThreads() else: self.delete_all_threads() def editAndSelectMessages(self, msg_array): """ Puts this thread into Edit mode and selects the messages listed in msg_array.<br> msg_array is an array of numbers. """ edit_btn = self.UTILS.element.getElement(DOM.Messages.edit_messages_icon, "Edit button") edit_btn.tap() time.sleep(2) # Go into message edit mode.. select_btn = self.UTILS.element.getElement(DOM.Messages.edit_msgs_select_btn, "Select messages button") select_btn.tap() time.sleep(2) messages = self.UTILS.element.getElements(DOM.Messages.message_list, "Messages") for msg in msg_array: messages[msg].tap() def editAndSelectThreads(self, target_array): """ Puts this thread into Edit mode and selects the messages listed in p_msg_array.<br> target_array is an array of target numbers or contacts which identify the threads to be selected. """ # Go into edit mode.. self.threadEditModeON() select_btn = self.UTILS.element.getElement(DOM.Messages.edit_msgs_select_threads_btn, "Select threads button") select_btn.tap() if len(target_array) == 0: return for i in target_array: self.UTILS.reporting.debug("selecting thread for [{}]".format(i)) thread = self.UTILS.element.getElement(("xpath", DOM.Messages.thread_selector_xpath.format(i)), "Thread checkbox for '{}'".format(i)) self.UTILS.reporting.debug("Trying to tap in element {}".format(thread)) thread.tap() # Finally check that all desired threads have been selected header = self.UTILS.element.getElement(DOM.Messages.edit_threads_header, "Edit threads header").text expected_title = str(len(target_array)) if len(target_array) else _("Delete messages") self.UTILS.test.test(expected_title in header, "Check that all desired threads have been selected") def enterSMSMsg(self, msg, not_keyboard=True): """ Create and send a message (assumes we are in a new 'create new message' screen with the destination number filled in already). """ self.parent.wait_for_element_displayed(*DOM.Messages.input_message_area) input_area = self.marionette.find_element(*DOM.Messages.input_message_area) input_area.tap() input_area.send_keys(msg) # Validate the field. # Get the element again in order to avoid StaleElementException input_area = self.marionette.find_element(*DOM.Messages.input_message_area) self.UTILS.test.test(input_area.text == msg, u'The text in the message area is "{}". Expected: "{}"'.format(input_area.text, msg)) def addNumbersInToField(self, nums): """ Add the phone numbers in the 'To' field of this sms message. Assumes you are in 'create sms' screen. """ # This variable is used to keep track of the appearance of the keyboard frame n = 0 for num in nums: """ Even though we don't use the keyboard for putting the number in, we need it for the ENTER button (which allows us to put more than one number in). So check that the keyboard appears when we tap the "TO" field if we have more than one number. """ if len(nums) > 1: self.UTILS.reporting.logResult("info", "Checking the keyboard appears when I tap the 'To' field ...") to_field = self.UTILS.element.getElement(DOM.Messages.target_numbers_empty, "Target number field") to_field.tap() boolKBD = False self.marionette.switch_to_frame() if n < 1: try: # A 'silent' check to see if the keyboard iframe appears. elDef = ("xpath", "//iframe[contains(@{}, '{}')]". format(DOM.Keyboard.frame_locator[0], DOM.Keyboard.frame_locator[1])) self.parent.wait_for_element_displayed(*elDef, timeout=2) boolKBD = True except: boolKBD = False self.UTILS.test.test( boolKBD, "Keyboard is displayed when 'To' field is clicked for the first time") self.UTILS.iframe.switchToFrame(*DOM.Messages.frame_locator) if n == 0: self.UTILS.general.typeThis(DOM.Messages.target_numbers_empty, "Target number field", num, p_no_keyboard=True, p_validate=False, p_clear=False, p_enter=True) else: self.UTILS.general.typeThis(DOM.Messages.target_numbers_empty, "Target number field", num, p_no_keyboard=True, p_validate=False, p_clear=False, p_enter=False) input_area = self.UTILS.element.getElement(DOM.Messages.input_message_area, "Target number field") input_area.tap() n += 1 def addContactToField(self, contact_name): self._search_for_contact(contact_name) # Now check the correct name is in the 'To' list. self.checkIsInToField(contact_name) def _select_forward_option_for(self, element): self.actions.long_press(element, 2).perform() self.UTILS.reporting.logResult("info", "Clicking on forward button") forward_option = self.UTILS.element.getElement(DOM.Messages.forward_btn_msg_opt, "Forward button is displayed") forward_option.tap() def _search_for_contact(self, contact_name): self.contacts = Contacts(self.parent) self.selectAddContactButton() self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) self.contacts.search(contact_name) self.contacts.check_search_results(contact_name) results = self.UTILS.element.getElements(DOM.Contacts.search_results_list, "Contacts search results") for result in results: if result.text == contact_name: result.tap() break # Switch back to the sms iframe. self.apps.switch_to_displayed_app() def forwardMessage(self, msg_type, target_telNum): """ Forwards the last message of the thread to a number """ self.UTILS.reporting.logResult('info', "The message type to forward is: {}".format(msg_type)) if msg_type == "sms" or msg_type == "mms": self.UTILS.reporting.logResult("info", "Open {} option with longtap on it".format(msg_type)) last = self.last_message_in_this_thread() body = self.marionette.find_element(*DOM.Messages.last_message_body, id=last.id) self._select_forward_option_for(body) elif msg_type == "mmssub": self.UTILS.reporting.logResult("info", "Open mms with subject options with longtap on it") mms_subject = self.UTILS.element.getElement(DOM.Messages.received_mms_subject, "Target MMS field") self._select_forward_option_for(mms_subject) else: self.UTILS.reporting.logResult("info", "incorrect value received") self.UTILS.test.test(False, "Incorrect value received") self.addNumbersInToField([target_telNum]) self.UTILS.reporting.logResult("info", "Clicking on Send button") self.sendSMS() def forwardMessageToContact(self, msg_type, contact_name): """ Forwards the last message of the thread to a contact, searching for it """ self.UTILS.reporting.logResult('info', "The message type to forward is: {}".format(msg_type)) if msg_type == "sms" or msg_type == "mms": # Open sms option with longtap on it self.UTILS.reporting.logResult("info", "Open sms option with longtap on it") sms = self.last_message_in_this_thread() body = self.marionette.find_element(*DOM.Messages.last_message_body, id=sms.id) self._select_forward_option_for(body) elif msg_type == "mmssub": # Open mms option with longtap on it self.UTILS.reporting.logResult("info", "Open mms with subject options with longtap on it") mms_subject = self.UTILS.element.getElement(DOM.Messages.received_mms_subject, "Target MMS field") self._select_forward_option_for(mms_subject) else: self.UTILS.reporting.logResult("info", "incorrect value received") self.UTILS.test.test(False, "Incorrect value received") # Search for the contact and check it's been added self.addContactToField(contact_name) # Send the mms. self.UTILS.reporting.logResult("info", "Clicking on Send button") self.sendSMS() def forwardMessageToMultipleRecipients(self, msg_type, target_telNum, contact_name): self.UTILS.reporting.logResult('info', "The message type to forward is: {}".format(msg_type)) if msg_type == "sms" or msg_type == "mms": # Open sms option with longtap on it self.UTILS.reporting.logResult("info", "Open sms option with longtap on it") sms = self.last_message_in_this_thread() body = self.marionette.find_element(*DOM.Messages.last_message_body, id=sms.id) self._select_forward_option_for(body) elif msg_type == "mmssub": # Open mms option with longtap on it self.UTILS.reporting.logResult("info", "Open mms with subject options with longtap on it") mms_subject = self.UTILS.element.getElement(DOM.Messages.received_mms_subject, "Target MMS field") self._select_forward_option_for(mms_subject) else: self.UTILS.reporting.logResult("info", "incorrect value received") self.UTILS.test.test(False, "Incorrect value received") # Add phone numbers self.addNumbersInToField([target_telNum]) # Search for the contact and check it's been added self.addContactToField(contact_name) # Send the mms. self.UTILS.reporting.logResult("info", "Clicking on Send button") self.sendSMS() def get_mms_attachments_info(self, mms): """Give name and file size for all attachments in an MMS. Given an MMS, return a list containing a dictionary for every attachment, with two keys, name and size. """ attachment_names = self.marionette.find_elements(*DOM.Messages.mms_attachment_names, id=mms.id) attachment_sizes = self.marionette.find_elements(*DOM.Messages.mms_attachment_sizes, id=mms.id) result = [] for (i, name) in enumerate(attachment_names): inner_text = self.marionette.execute_script("""return arguments[0].innerHTML;""", script_args=[name]) att = {} att["name"] = inner_text size_elem = attachment_sizes[i].get_attribute("data-l10n-args") size = size_elem[size_elem.index(":") + 2:size_elem.rfind("\"")] i = i + 1 att["size"] = size result.append(att) return result def getThreadText(self, num): """ Returns the preview text for the thread for this number (if it exists), or False if either the thread doesn't exist or can't be found. """ if self.threadExists(num): x = self.UTILS.element.getElements(DOM.Messages.threads_list, "Threads") for thread in x: try: thread.find_element("xpath", ".//p[text()='{}']".format(num)) z = thread.find_element("xpath", ".//span[@class='body-text']") return z.text except: pass return False def header_addToContact(self): """ Taps the header and tries to tap the 'Add to an existsing contact' button. - Assumes we are looking at a message thread already. - Leaves you in the correct iframe to continue (contacts). """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() x = self.UTILS.element.getElement(DOM.Messages.header_add_to_contact_btn, "'Add to an existing contact' button") x.tap() # Switch to correct iframe. self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) def header_call(self): """Tap on the header of a messages thread and dial the number """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() # Select dialer option. x = self.UTILS.element.getElement(DOM.Messages.header_call_btn, "'Call' button") x.tap() # Switch to correct iframe. self.UTILS.iframe.switchToFrame(*DOM.Dialer.frame_locator) def header_createContact(self): """ Taps the header and tries to tap the 'send message' button. - Assumes we are looking at a message thread already. - Leaves you in the correct iframe to continue. """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() x = self.UTILS.element.getElement(DOM.Messages.header_create_new_contact_btn, "'Create new contact' button") x.tap() # Switch to correct iframe. self.UTILS.iframe.switchToFrame(*DOM.Contacts.frame_locator) def header_sendMessage(self): """ Taps the header and tries to tap the 'send message' button. - Assumes we are looking at a message thread already. - Leaves you in the correct iframe to continue. """ x = self.UTILS.element.getElement(DOM.Messages.message_header, "Thread header") x.tap() x = self.UTILS.element.getElement(DOM.Messages.header_send_message_btn, "'Send message' button") x.tap() def last_message_in_this_thread(self): """ Returns an object of the last message in the current thread. """ self.parent.wait_for_element_present(*DOM.Messages.last_message, timeout=20) message = self.marionette.find_element(*DOM.Messages.last_message) self.UTILS.element.scroll_into_view(message) return message def last_sent_message_timestamp(self): """Returns the timestamp of the last sent message """ send_time = self.marionette.find_element(*DOM.Messages.last_sent_message).get_attribute("data-timestamp") return float(send_time) / 1000 def last_sent_message(self): """Returns the last sent message """ return self.marionette.find_element(*DOM.Messages.last_sent_message) def open_attached_file(self, frame_to_change): elem = DOM.Messages.last_message_attachment_av last = self.UTILS.element.getElement(elem, "Last message attachment") self.UTILS.element.scroll_into_view(last) # Now get the thumbnail in order to open it thumb = last.find_element(*DOM.Messages.last_message_thumbnail) thumb.tap() self.UTILS.iframe.switchToFrame(*frame_to_change) def openThread(self, num): """ Opens the thread for this number (assumes we're looking at the threads in the messaging screen). """ try: thread_el = ("xpath", DOM.Messages.thread_selector_xpath.format(num)) x = self.UTILS.element.getElement(thread_el, "Message thread for " + num) x.tap() self.UTILS.element.waitForElements(DOM.Messages.send_message_button, "'Send' button") except Exception as e: x = self.UTILS.debug.screenShotOnErr() msg = "<b>NOTE:</b> The thread <i>may</i> have failed to open due to [{}].".format(e) self.UTILS.reporting.logResult("info", msg, x) def readLastSMSInThread(self): """ Read last message in the current thread. """ received_message = self.UTILS.element.getElements(DOM.Messages.received_messages, "Received messages")[-1] return str(received_message.text) def readNewSMS(self, fromNum): """ Read and return the value of the new message received from number. """ x = self.UTILS.element.getElement(("xpath", DOM.Messages.messages_from_num.format(fromNum)), "Message from '" + fromNum + "'") x.tap() # (From gaiatest: "TODO Due to displayed bugs I cannot find a good wait # for switch btw views") time.sleep(5) # Return the last comment in this thread. return self.readLastSMSInThread() def removeContactFromToField(self, target): """ Removes target from the "To" field of this SMS.<br> Returns True if it found the target, or False if not. """ x = self.UTILS.element.getElements(DOM.Messages.target_numbers, "'To:' field contents") for i in range(len(x)): self.UTILS.reporting.logResult("info", "Checking target '{}' to '{}' ...".format(x[i].text, target)) if x[i].text.lower() == target.lower(): self.UTILS.reporting.logResult("info", "Tapping contact '" + target + "' ...") x[i].tap() try: # This contact was added via 'add contact' icon. self.parent.wait_for_element_displayed("xpath", "//button[text()='{}']".format(_("Remove")), timeout=2) y = self.marionette.find_element("xpath", "//button[text()='{}']".format(_("Remove"))) self.UTILS.reporting.logResult("info", "Tapping 'Remove' button.") y.tap() return True except: # This contact was manually entered. z = self.UTILS.element.getElements(DOM.Messages.target_numbers, "Target to be removed")[i] z.clear() return True return False def selectAddContactButton(self): """ Taps the 'add contact' button """ add_btn = self.UTILS.element.getElement(DOM.Messages.add_contact_button, "Add contact button") add_btn.tap() def sendSMS(self): """ Just presses the 'send' button (assumes everything else is done). """ self.parent.wait_for_condition(lambda m: m.find_element(*DOM.Messages.send_message_button).is_enabled()) send_btn = self.marionette.find_element(*DOM.Messages.send_message_button) send_btn.tap() # (Give the spinner time to appear.) time.sleep(2) self.UTILS.element.waitForNotElements(DOM.Messages.message_sending_spinner, "'Sending' icon", True, 180) # Check if we received the 'service unavailable' message. try: self.parent.wait_for_element_displayed(*DOM.Messages.service_unavailable_msg, timeout=2) screenshot = self.UTILS.debug.screenShotOnErr() msg = "'Service unavailable' message detected - unable to send sms!" self.UTILS.reporting.logResult("info", msg, screenshot) return False except: pass return True def startNewSMS(self): """ Starts a new sms (doesn't fill anything in). Assumes the Messaging app is already launched. """ newMsgBtn = self.UTILS.element.getElement(DOM.Messages.create_new_message_btn, "Create new message button") newMsgBtn.tap() def threadCarrier(self): # Returns the 'carrier' being used by this thread. x = self.UTILS.element.getElement(DOM.Messages.type_and_carrier_field, "Type and carrier information") parts = x.text.split("|") if len(parts) > 1: return parts[1].strip() return parts[0].strip() def threadEditModeOFF(self): """ Turns off Edit mode while in the SMS threads screen. """ x = self.UTILS.element.getElement(DOM.Messages.cancel_edit_threads, "Cancel button") x.tap() self.UTILS.element.waitForElements(DOM.Messages.edit_threads_button, "Edit button") def threadEditModeON(self): """ Turns on Edit mode while in the SMS threads screen. """ edit_btn = self.UTILS.element.getElement(DOM.Messages.edit_threads_button, "Edit button") edit_btn.tap() self.UTILS.element.waitForElements(DOM.Messages.cancel_edit_threads, "Cancel button") def threadExists(self, num): """ Verifies that a thread exists for this number (returns True or False). """ boolOK = False try: self.parent.wait_for_element_present("xpath", DOM.Messages.thread_selector_xpath.format(num), 1) boolOK = True except: boolOK = False return boolOK def threadType(self): """ Returns the 'type' being used by this thread. """ x = self.UTILS.element.getElement(DOM.Messages.type_and_carrier_field, "Type and carrier information") parts = x.text.split("|") typ = parts[0].strip() return typ if len(parts) > 1 else '' def time_of_last_message_in_thread(self): """ Returns the time of the last message in the current thread. """ t = self.UTILS.element.getElement(DOM.Messages.last_message_time, "Last message time") return t.text def time_of_thread(self, num): """ Returns the time of a thread. """ x = self.UTILS.element.getElement(("xpath", DOM.Messages.thread_timestamp_xpath.format(num)), "Thread time", True, 5, False) return x.text def thread_timestamp(self, num): """ Returns the timestamp of a thread """ x = self.marionette.find_element(*DOM.Messages.last_message).get_attribute("data-timestamp") return float(x) def verify_mms_received(self, attached_type, sender_number): self.UTILS.iframe.switchToFrame(*DOM.Messages.frame_locator) self.openThread(sender_number) message = self.last_message_in_this_thread() self.UTILS.test.test(message, "A received message appeared in the thread.", True) self.UTILS.reporting.debug("*** attached type: {}".format(attached_type)) if attached_type == "img": elem = DOM.Messages.last_message_attachment_img elif attached_type == "video": elem = DOM.Messages.last_message_attachment_av elif attached_type == "audio": elem = DOM.Messages.last_message_attachment_av else: # self.UTILS.reporting.logResult("info", "incorrect value received") msg = "FAILED: Incorrect parameter received in verify_mms_received()"\ ". Attached_type must be image, video or audio." self.UTILS.test.test(False, msg) self.UTILS.reporting.debug("*** searching for attachment type") # Look for all the attachments, since there can be more than one atts = self.UTILS.element.getElements(elem, "Last message attachments") self.UTILS.element.scroll_into_view(atts[0]) found = False for att in atts: typ = att.get_attribute("data-attachment-type") self.UTILS.reporting.debug("*** searching for attachment type Result: {}".format(typ)) if typ == attached_type: found = True if not found: msg = "No attachment with type {} was found in the message".format(attached_type) self.UTILS.test.test(False, msg) def wait_for_message(self, send_time=None, timeout=120): """Wait for a received message in the current thread and return it. """ if not send_time: send_time = self.last_sent_message_timestamp() poll_time = 2 poll_reps = (timeout / poll_time) result = False for i in range(poll_reps): # Get last message in this thread. last_msg = self.last_message_in_this_thread() if not last_msg: time.sleep(poll_time) continue # If the send_time timestamp is greater than this message's timestamp,it means the message # we expect has not arrived yet, so we have to wait a bit more. message_data_time = float(last_msg.get_attribute("data-timestamp")) / 1000 fmt = "data-timestamp of last message in thread: {:.3f} send_time: {:.3f} --> {}" self.UTILS.reporting.debug(fmt.format(message_data_time, send_time, send_time > message_data_time)) if send_time > message_data_time: continue # Is this a received message? if "incoming" in last_msg.get_attribute("class"): result = last_msg break # Nope - sleep then try again. time.sleep(poll_time) self.UTILS.test.test(result, "Last message in thread 'received' within {} seconds.". format(timeout)) return result