[Autotest] Updating UI utils helper and platform_printjob

BUG=chromium:1077325
TEST=re_ran platform_printjob (with some local modifications)

Change-Id: Iaea572de892a195eda7f6b6298b50cbddf8025e9
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2211227
Reviewed-by: Kalin Stoyanov <kalin@chromium.org>
Reviewed-by: Derek Beckett <dbeckett@chromium.org>
Tested-by: Kalin Stoyanov <kalin@chromium.org>
Commit-Queue: Kalin Stoyanov <kalin@chromium.org>
Auto-Submit: Derek Beckett <dbeckett@chromium.org>
(cherry picked from commit da19ff7ce0a2a60abbbf5613c9945406cd66c196)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2219583
Tested-by: Derek Beckett <dbeckett@chromium.org>
Commit-Queue: Derek Beckett <dbeckett@chromium.org>
diff --git a/client/common_lib/ui_utils.py b/client/common_lib/ui_utils.py
index e2bed47..e0fe6c3 100644
--- a/client/common_lib/ui_utils.py
+++ b/client/common_lib/ui_utils.py
@@ -175,13 +175,14 @@
             {name: %s, role: %s}}).map(node => node.%s)""" % (name, role, attr)
         return self.ext.EvaluateJavaScript(new_promise, promise=True)
 
-    def get_name_role_list(self):
+    def get_name_role_list(self, name=None, role=None):
         """
         Return [{}, {}] containing the name/role of everything on screen.
 
         """
-        name = self.REGEX_ALL
-        role = self.REGEX_ALL
+        name = self.REGEX_ALL if name is None else name
+        role = self.REGEX_ALL if role is None else self._format_obj(role,
+                                                                    False)
 
         new_promise = self.PROMISE_TEMPLATE % """root.findAll({attributes:
             {name: %s, role: %s}}).map(node =>
diff --git a/client/common_lib/ui_utils_helpers.py b/client/common_lib/ui_utils_helpers.py
index 9dd13cd..15de46b 100644
--- a/client/common_lib/ui_utils_helpers.py
+++ b/client/common_lib/ui_utils_helpers.py
@@ -1,14 +1,16 @@
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.common_lib import ui_utils
 from autotest_lib.client.cros.input_playback import keyboard
+import logging
 
 
-class UI_Helper_Handler(object):
+class UIPrinterHelper(object):
+    """Helper lib for ChromeOS UI automation."""
 
     def __init__(self, ui_handler=None, chrome=None):
         """ui_handler or chrome must be provided. ui_handler is an already
         existing ui object."""
-        if not self.ui_handler and chrome:
+        if not ui_handler and chrome:
             self.ui = ui_utils.UI_Handler()
             self.ui.start_ui_root(chrome)
         elif not chrome and ui_handler:
@@ -28,45 +30,89 @@
         if self._keyboard:
             self._keyboard.close()
 
-    def open_printer_menu(self):
-        self.ui.doDefault_on_obj('Chrome')
+    def open_printer_menu(self, retry=0):
+        """Open the printer menu via the Chrome Dropdown."""
+        if not self._is_chrome_menu_open():
+            self.ui.doDefault_on_obj('Chrome')
         self.ui.wait_for_ui_obj('/Print/', role='menuItem', isRegex=True)
         self.ui.doDefault_on_obj('/Print/', isRegex=True)
-        self.wait_for_print_ready()
+        if retry == 0:
+            try:
+                self.wait_for_print_ready()
+            except error.TestError:
+                self.open_printer_menu(retry=1)
+        else:
+            self.wait_for_print_ready()
 
     def wait_for_print_ready(self):
-        self.ui.wait_for_ui_obj('Fit to width', role='button')
+        """Wait for the print page to fully load."""
+        try:
+            self.ui.wait_for_ui_obj('Fit to width', role='button', timeout=30)
+        except:
+            logging.info("Missing {} in {}".format("Fit to width", self.ui.get_name_role_list()))
+            raise error.TestError("Fit to width no load")
         self.ui.wait_for_ui_obj('Loading preview', remove=True)
 
     def open_see_more_print_sub_menu(self):
         """For now must pivot there with the KB."""
-        if not self.is_print_menu_open():
-            raise error.TestError(
-                "Cannot open See more print menu when print screen not open")
         if not self._keyboard:
             self._keyboard = keyboard.Keyboard()
-        for i in range(4):
-            self._keyboard.press_key('tab')
-        self._keyboard.press_key('down')
+
+        # Some tests have a notification which causes the chrome menu
+        # to remain open, and loose focus on the print page.
+        if self._is_chrome_menu_open():
+            self._keyboard.press_key("escape")
+            self.verify_print_menu_open()
+
+        self.ui.doDefault_on_obj(self._node_name_above_see_more_menu())
+        self._keyboard.press_key("down")
         self._keyboard.press_key('enter')
 
+    def _node_name_above_see_more_menu(self):
+        """
+        Return the name of the node above see more menu.
+
+        Right now "See More" is not directly clickable. There is investigation
+        being done as for why, but still no answer. For now we will instead
+        get the name of the node above it on the menu list, to "hover" over,
+        then down arrow + enter to select "See more".
+        There are some empty items that return off the list query, so we have
+        to filter these out. Additionally if for some odd reason we do not
+        find an item directly above the "See more" option, we will default
+        to "Save as PDF".
+
+        """
+        temp_list = []
+        menu_list = self.ui.get_name_role_list(role="menuListOption")
+        for i, item in enumerate(menu_list):
+            if item["name"]:
+                temp_list.append(item["name"])
+            if "See more" in item["name"]:
+                if len(temp_list) < 2:
+                    return "Save as PDF"  # Default item above
+                return temp_list[-2]  # First non empty item name prior.
+
+    def _is_chrome_menu_open(self):
+        """Return True if the chrome dropdown menu is still open."""
+        return self.ui.item_present("/Print/", role="menuItem", isRegex=True)
+
     def select_printer_from_see_more_menu(self, printer_name):
         """Click a printer from the "see more" sub menu within print page."""
         if not self.is_see_more_menu_open():
             raise error.TestError(
                 "Cannot select printer from sub menu as its not open.")
-        self.ui.wait_for_ui_obj(printer_name, role='cell')
-        self.ui.doDefault_on_obj(printer_name, role='cell')
-        self.ui.wait_for_ui_obj(printer_name, role='cell', remove=True)
+        # Easier to find via regex since the actual cell has the name twice.
+        if not self.is_str_regex(printer_name):
+            printer_name = self.make_str_regex(printer_name)
+        self.ui.wait_for_ui_obj(printer_name, isRegex=True, role='cell')
+        self.ui.doDefault_on_obj(printer_name, isRegex=True, role='cell')
+        self.ui.wait_for_ui_obj(printer_name, role='cell', remove=True, isRegex=True)
         # Wait for the "Setting up " loading icon to finish
         self.ui.wait_for_ui_obj('Setting up ', remove=True)
 
     def click_print(self, isPDF=False):
         """Click the print button. Click save if PDF."""
-        if not self.is_print_menu_open():
-            raise error.TestError(
-                "Cannot open See more print menu when print screen not open")
-
+        self.verify_print_menu_open()
         self.ui.doDefault_on_obj('Save' if isPDF else 'Print', role='button')
         if isPDF:
             pass  # TODO implement the save menu
@@ -82,3 +128,21 @@
     def is_print_menu_open(self):
         """Return True if the print menu is open."""
         return self.ui.item_present("Print", role="window")
+
+    def verify_print_menu_open(self):
+        """Verify print menu is open. If not raise TestError."""
+        if not self.is_print_menu_open():
+            raise error.TestError(
+                "Cannot open See more print menu when print screen not open")
+
+    def is_str_regex(self, var):
+        """Return True if the given var starts and ends with "/"."""
+        if len(var) < 1:
+            return False
+        if var[0] == "/" and var[-1] == "/":
+            return True
+        return False
+
+    def make_str_regex(self, var):
+        """Return the provided var with a leading and ending "/"."""
+        return ("/" + var + "/")
diff --git a/client/site_tests/platform_PrintJob/platform_PrintJob.py b/client/site_tests/platform_PrintJob/platform_PrintJob.py
index 6de42c2..9140acd 100644
--- a/client/site_tests/platform_PrintJob/platform_PrintJob.py
+++ b/client/site_tests/platform_PrintJob/platform_PrintJob.py
@@ -9,7 +9,7 @@
 from autotest_lib.client.bin import test, utils
 from autotest_lib.client.common_lib.cros import chrome
 from autotest_lib.client.cros.chameleon import chameleon
-from autotest_lib.client.cros.input_playback import input_playback
+from autotest_lib.client.common_lib import ui_utils_helpers
 
 _CHECK_TIMEOUT = 20
 _PRINTER_NAME = "HP OfficeJet g55"
@@ -28,76 +28,9 @@
     """
     version = 1
 
-    def warmup(self):
-        """Test setup."""
-        # Emulate keyboard to play back shortcuts and navigate.
-        # See input_playback README.
-        self._player = input_playback.InputPlayback()
-        self._player.emulate(input_type='keyboard')
-        self._player.find_connected_inputs()
-
-
     def cleanup(self):
         if hasattr(self, 'browser'):
             self.browser.close()
-        self._player.close()
-
-
-
-    def _open_print_dialog(self):
-        """Use keyboard shortcut to open print dialog."""
-        self._player.blocking_playback_of_default_file(
-            input_type='keyboard', filename='keyboard_ctrl+p')
-        time.sleep(_SHORT_WAIT)
-
-
-    def _press_tab(self):
-        """Use keyboard shortcut to press Tab."""
-        self._player.blocking_playback_of_default_file(
-            input_type='keyboard', filename='keyboard_tab')
-
-
-    def _press_down(self):
-        """Use keyboard shortcut to press down."""
-        self._player.blocking_playback_of_default_file(
-            input_type='keyboard', filename='keyboard_down')
-
-
-    def _press_shift_tab(self):
-        """Use keyboard shortcut to press Shift-Tab."""
-        self._player.blocking_playback_of_default_file(
-            input_type='keyboard', filename='keyboard_shift+tab')
-
-
-    def _press_enter(self):
-        """Use keyboard shortcut to press Enter."""
-        self._player.blocking_playback_of_default_file(
-            input_type='keyboard', filename='keyboard_enter')
-        time.sleep(_SHORT_WAIT)
-
-
-    def execute_print_job(self):
-        """Using keyboard imput navigate to print dialog and execute a job."""
-
-        # Open dialog and select 'See more'
-        self._open_print_dialog()
-        time.sleep(_SHORT_WAIT)
-
-        # Navigate to printer selection
-        for x in range(_STEPS_BETWEEN_CONTROLS):
-            self._press_tab()
-        self._press_down()
-        self._press_tab()
-        self._press_down()
-        self._press_enter()
-
-        # Go back to Print button
-        for x in range(_STEPS_BETWEEN_CONTROLS):
-            self._press_shift_tab()
-
-        # Send the print job
-        self._press_enter()
-
 
     def check_notification(self, notification):
         """Polls for successful print job notification"""
@@ -116,7 +49,6 @@
                 desc='Notification %s NOT found' % notification,
                 timeout=_CHECK_TIMEOUT, sleep_interval=_SHORT_WAIT)
 
-
     def navigate_to_pdf(self):
         """Navigate to the pdf page to print"""
         self.cr.browser.platform.SetHTTPServerDirectories(self.bindir)
@@ -127,7 +59,6 @@
                 timeout=_CHECK_TIMEOUT);
         time.sleep(_SHORT_WAIT)
 
-
     def run_once(self, host, args):
         """Run the test."""
 
@@ -148,7 +79,8 @@
             self.navigate_to_pdf()
             time.sleep(_SHORT_WAIT)
             logging.info('PDF file opened in browser!')
-            self.execute_print_job()
+            self.ui_helper = ui_utils_helpers.UIPrinterHelper(chrome=self.cr)
+            self.ui_helper.print_to_custom_printer("Chameleon " + _PRINTER_NAME)
             usb_printer.StartCapturingPrinterData()
             self.check_notification(_PRINTING_NOTIF)
             self.check_notification(_PRINTING_COMPLETE_NOTIF)