コード例 #1
0
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;")
コード例 #2
0
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;")
コード例 #3
0
ファイル: test_31574.py プロジェクト: owdqa/owd_test_cases
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)
コード例 #4
0
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;")
コード例 #5
0
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;")
コード例 #6
0
ファイル: test_31574.py プロジェクト: owdqa/owd_test_cases
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)
コード例 #7
0
ファイル: test_26977.py プロジェクト: owdqa/owd_test_cases
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")
コード例 #8
0
ファイル: test_26782.py プロジェクト: owdqa/owd_test_cases
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)
コード例 #9
0
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
コード例 #10
0
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
コード例 #11
0
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
コード例 #12
0
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)
コード例 #13
0
ファイル: messages.py プロジェクト: owdqa/OWD_TEST_TOOLKIT
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