• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import logging
2import time
3from autotest_lib.client.common_lib import error
4from autotest_lib.client.common_lib import utils
5
6
7class UI_Handler(object):
8
9    REGEX_ALL = '/(.*?)/'
10
11    PROMISE_TEMPLATE = \
12        '''new Promise(function(resolve, reject) {
13            chrome.automation.getDesktop(function(root) {
14                    resolve(%s);
15                })
16            })'''
17
18    def start_ui_root(self, cr):
19        """Start the UI root object for testing."""
20        self.ext = cr.autotest_ext
21
22    def is_obj_restricted(self, name, isRegex=False, role=None):
23        """
24        Return True if the object restriction is 'disabled'.
25        This usually means the button is either greyed out, locked, etc.
26
27        @param name: Parameter to provide to the 'name' attribute.
28        @param isRegex: bool, if the item is a regex.
29        @param role: Parameter to provide to the 'role' attribute.
30
31        """
32        FindParams = self._get_FindParams_str(name=name,
33                                              role=role,
34                                              isRegex=isRegex)
35        try:
36            restriction = self.ext.EvaluateJavaScript(
37                self.PROMISE_TEMPLATE % ("%s.restriction" % FindParams),
38                promise=True)
39        except Exception:
40            raise error.TestError(
41                'Could not find object {}.'.format(name))
42        if restriction == 'disabled':
43            return True
44        return False
45
46    def item_present(self, name, isRegex=False, flip=False, role=None):
47        """
48        Determines if an object is present on the screen
49
50        @param name: Parameter to provide to the 'name' attribute.
51        @param isRegex: bool, if the 'name' is a regex.
52        @param flip: Flips the return status.
53        @param role: Parameter to provide to the 'role' attribute.
54
55        @returns:
56            True if object is present and flip is False.
57            False if object is present and flip is True.
58            False if object is not present and flip is False.
59            True if object is not present and flip is True.
60
61        """
62        FindParams = self._get_FindParams_str(name=name,
63                                              role=role,
64                                              isRegex=isRegex)
65
66        if flip is True:
67            return not self._is_item_present(FindParams)
68        return self._is_item_present(FindParams)
69
70    def wait_for_ui_obj(self,
71                        name,
72                        isRegex=False,
73                        remove=False,
74                        role=None,
75                        timeout=10):
76        """
77        Waits for the UI object specified.
78
79        @param name: Parameter to provide to the 'name' attribute.
80        @param isRegex: bool, if the 'name' is a regex.
81        @param remove: bool, if you are waiting for the item to be removed.
82        @param role: Parameter to provide to the 'role' attribute.
83        @param timeout: int, time to wait for the item.
84
85        @raises error.TestError if the element is not loaded (or removed).
86
87        """
88        utils.poll_for_condition(
89            condition=lambda: self.item_present(name=name,
90                                                isRegex=isRegex,
91                                                flip=remove,
92                                                role=role),
93            timeout=timeout,
94            exception=error.TestError('{} did not load in: {}'
95                                      .format(name, self.list_screen_items())))
96
97    def did_obj_not_load(self, name, isRegex=False, timeout=5):
98        """
99        Specifically used to wait and see if an item appears on the UI.
100
101        NOTE: This is different from wait_for_ui_obj because that returns as
102        soon as the object is either loaded or not loaded. This function will
103        wait to ensure over the timeout period the object never loads.
104        Additionally it will return as soon as it does load. Basically a fancy
105        time.sleep()
106
107        @param name: Parameter to provide to the 'name' attribute.
108        @param isRegex: bool, if the item is a regex.
109        @param timeout: Time in seconds to wait for the object to appear.
110
111        @returns: True if object never loaded within the timeout period,
112            else False.
113
114        """
115        t1 = time.time()
116        while time.time() - t1 < timeout:
117            if self.item_present(name=name, isRegex=isRegex):
118                return False
119            time.sleep(1)
120        return True
121
122    def doDefault_on_obj(self, name, isRegex=False, role=None):
123        """Runs the .doDefault() js command on the element."""
124        FindParams = self._get_FindParams_str(name=name,
125                                              role=role,
126                                              isRegex=isRegex)
127        try:
128            self.ext.EvaluateJavaScript(
129                self.PROMISE_TEMPLATE % ("%s.doDefault()" % FindParams),
130                promise=True)
131        except:
132            logging.info('Unable to .doDefault() on {}. All items: {}'
133                         .format(FindParams, self.list_screen_items()))
134            raise error.TestError("doDefault failed on {}".format(FindParams))
135
136    def doCommand_on_obj(self, name, cmd, isRegex=False, role=None):
137        """Run the specified command on the element."""
138        FindParams = self._get_FindParams_str(name=name,
139                                              role=role,
140                                              isRegex=isRegex)
141        return self.ext.EvaluateJavaScript(self.PROMISE_TEMPLATE % """
142            %s.%s""" % (FindParams, cmd), promise=True)
143
144    def list_screen_items(self,
145                          role=None,
146                          name=None,
147                          isRegex=False,
148                          attr='name'):
149
150        """
151        List all the items currently visable on the screen.
152
153        If no paramters are given, it will return the name of each item,
154        including items with empty names.
155
156        @param role: The role of the items to use (ie button).
157        @param name: Parameter to provide to the 'name' attribute.
158        @param isRegex: bool, if the obj is a regex.
159        @param attr: Str, the attribute you want returned in the list
160            (eg 'name').
161
162        """
163
164        if isRegex:
165            if name is None:
166                raise error.TestError('If regex is True name must be given')
167            name = self._format_obj(name, isRegex)
168        elif name is not None:
169            name = self._format_obj(name, isRegex)
170        name = self.REGEX_ALL if name is None else name
171        role = self.REGEX_ALL if role is None else self._format_obj(role,
172                                                                    False)
173
174        new_promise = self.PROMISE_TEMPLATE % """root.findAll({attributes:
175            {name: %s, role: %s}}).map(node => node.%s)""" % (name, role, attr)
176
177        return self.ext.EvaluateJavaScript(new_promise, promise=True)
178
179    def get_name_role_list(self):
180        """
181        Return [{}, {}] containing the name/role of everything on screen.
182
183        """
184        combined = []
185        names = self.list_screen_items(attr='name')
186        roles = self.list_screen_items(attr='role')
187
188        if len(names) != len(roles):
189            raise error.TestError('Number of items in names and roles !=')
190
191        for name, role in zip(names, roles):
192            temp_d = {'name': name, 'role': role}
193            combined.append(temp_d)
194        return combined
195
196    def _format_obj(self, name, isRegex):
197        """
198        Formats the object for use in the javascript name attribute.
199
200        When searching for an element on the UI, a regex expression or string
201        can be used. If the search is using a string, the obj will need to be
202        wrapped in quotes. A Regex is not.
203
204        @param name: Parameter to provide to the 'name' attribute.
205        @param isRegex: if True, the object will be returned as is, if False
206            the obj will be returned wrapped in quotes.
207
208        @returns: The formatted string for regex/name.
209        """
210        if isRegex:
211            return name
212        else:
213            return '"{}"'.format(name)
214
215    def _get_FindParams_str(self, name, role, isRegex):
216        """Returns the FindParms string, so that automation node functions
217        can be run on it
218
219        @param role: The role of the items to use (ie button).
220        @param name: Parameter to provide to the 'name' attribute.
221        @param isRegex: bool, if the obj is a regex.
222
223        @returns: The ".find($FindParams)" string, which can be used to run
224            automation node commands, such as .doDefault()
225
226        """
227        FINDPARAMS_BASE = """
228        root.find({attributes:
229                  {name: %s,
230                   role: %s}}
231                 )"""
232
233        name = self._format_obj(name, isRegex)
234        if role is None:
235            role = self.REGEX_ALL
236        else:
237            role = self._format_obj(role, False)
238        return (FINDPARAMS_BASE % (name, role))
239
240    def _is_item_present(self, findParams):
241        """Return False if tempVar is None, else True."""
242        item_present = self.ext.EvaluateJavaScript(
243            self.PROMISE_TEMPLATE % findParams,
244            promise=True)
245        if item_present is None:
246            return False
247        return True
248
249    def click_and_wait_for_item_with_retries(self,
250                                             item_to_click,
251                                             item_to_wait_for,
252                                             isRegex_click=False,
253                                             isRegex_wait=False,
254                                             click_role=None,
255                                             wait_role=None):
256        """
257        Click on an item, and wait for a subsequent item to load. If the new
258        item does not load, we attempt to click the button again.
259
260        This being done to remove the corner case of button being visually
261        loaded, but not fully ready to be clicked yet. In simple terms:
262            Click button --> Check for next button to appear
263            IF button does not appear, its likely the original button click did
264            not work, thus reclick that button --> recheck for the next button.
265
266            If button did appear, stop clicking.
267
268        """
269        self.doDefault_on_obj(item_to_click,
270                              role=click_role,
271                              isRegex=isRegex_click)
272        for retry in xrange(3):
273            try:
274                self.wait_for_ui_obj(item_to_wait_for,
275                                     role=wait_role,
276                                     isRegex=isRegex_wait,
277                                     timeout=6)
278                break
279            except error.TestError:
280                self.doDefault_on_obj(item_to_click,
281                                      role=click_role,
282                                      isRegex=isRegex_click)
283        else:
284            raise error.TestError('Item {} did not load after 2 tries'.format(
285                                  item_to_wait_for))
286