@@ -0,0 +1,473 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Copyright 2015 Canonical Ltd.
4+#
5+# This program is free software; you can redistribute it and/or modify
6+# it under the terms of the GNU General Public License version 3, as published
7+# by the Free Software Foundation.
8+#
9+# This program is distributed in the hope that it will be useful,
10+# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+# GNU General Public License for more details.
13+#
14+# You should have received a copy of the GNU General Public License
15+# along with this program. If not, see <http://www.gnu.org/licenses/>
16+
17+import logging
18+import re
19+
20+import autopilot
21+from autopilot import introspection
22+from ubuntuuitoolkit import UbuntuUIToolkitCustomProxyObjectBase
23+
24+from unity8.settings_wizard import override_proxy_timeout
25+
26+logger = logging.getLogger(__name__)
27+
28+
29+def get_wizard(current_page):
30+ return current_page.get_root_instance().select_single(Wizard)
31+
32+
33+class Wizard(UbuntuUIToolkitCustomProxyObjectBase):
34+ """High-level helper to navigate through the pages of the wizard"""
35+
36+ def get_language_page(self):
37+ return self.wait_select_single(
38+ objectName='languagePage', visible='True')
39+
40+ def get_current_page(self):
41+ return self.wait_select_single('Page', visible='True')
42+
43+ def get_sim_page(self):
44+ return self.wait_select_single(
45+ objectName='simPage', visible='True')
46+
47+ def get_password_page(self):
48+ return self.wait_select_single(
49+ objectName='passwdPage', visible='True')
50+
51+ def get_password_entry_page(self):
52+ return self.wait_select_single(
53+ objectName='passwdSetPage', visible='True')
54+
55+ def get_confirm_password_page(self):
56+ return self.wait_select_single(
57+ objectName='passwdConfirmPage', visible='True')
58+
59+ def get_wifi_connect_page(self):
60+ return self.wait_select_single(
61+ objectName='wifiPage', visible='True')
62+
63+ def get_reporting_page(self):
64+ return self.wait_select_single(
65+ objectName='reportingPage', visible='True')
66+
67+ def get_finished_page(self):
68+ return self.wait_select_single(
69+ objectName='finishedPage', visible='True')
70+
71+
72+class WizardLanguagePage(UbuntuUIToolkitCustomProxyObjectBase):
73+ """Helper class to interact with the language welcome page"""
74+
75+ # This class was renamed from LanguagePage to WizardLanguagePage because
76+ # there is a class with the same name in the system settings custom proxy
77+ # objects. Reported in http://pad.lv/1422904. --elopio - 2015-02-17
78+
79+ @classmethod
80+ def validate_dbus_object(cls, path, state):
81+ name = introspection.get_classname_from_path(path)
82+ if name == b'Page':
83+ if state['objectName'][1] == 'languagePage':
84+ return True
85+ return False
86+
87+ def _get_language_button(self):
88+ return self.select_single(
89+ 'ComboButton', objectName='languageCombo')
90+
91+ def _get_continue_button(self):
92+ return self.select_single('StackButton', text='Continue')
93+
94+ def _swipe_to_language(self, list, language):
95+ """Swipe to the chosen language in the given list"""
96+ list.swipe_to_top()
97+ while not list.atYEnd:
98+ try:
99+ item = list.select_single(
100+ 'LabelVisual', text=language, visible='True')
101+ item.swipe_into_view()
102+ return item
103+ except introspection.dbus.StateNotFoundError:
104+ list.swipe_to_show_more_below()
105+ # item could not be found
106+ raise introspection.dbus.StateNotFoundError
107+
108+ @autopilot.logging.log_action(logger.info)
109+ def select_language(self, language):
110+ """Select a different language from the list"""
111+ combo_button = self._get_language_button()
112+ self.pointing_device.click_object(combo_button)
113+ language_list = combo_button.wait_select_single(
114+ 'UbuntuListView11', visible='True')
115+ item = self._swipe_to_language(language_list, language)
116+ self.pointing_device.click_object(item)
117+
118+ def get_selected_language(self):
119+ return self._get_language_button().get_properties()['text']
120+
121+ @autopilot.logging.log_action(logger.info)
122+ def continue_(self):
123+ self.pointing_device.click_object(self._get_continue_button())
124+ wizard = get_wizard(self)
125+ next_page = wizard.get_current_page()
126+ sim_inserted = True
127+ if next_page.objectName == 'simPage':
128+ # no sim is inserted
129+ next_page = wizard.get_sim_page()
130+ sim_inserted = False
131+ else:
132+ # sim is inserted
133+ next_page = wizard.get_password_page()
134+ return sim_inserted, next_page
135+
136+
137+class SimPage(UbuntuUIToolkitCustomProxyObjectBase):
138+ """Helper class to interact with the no sim notification page"""
139+
140+ @classmethod
141+ def validate_dbus_object(cls, path, state):
142+ name = introspection.get_classname_from_path(path)
143+ if name == b'Page':
144+ if state['objectName'][1] == 'simPage':
145+ return True
146+ return False
147+
148+ def _get_back_button(self):
149+ return self.select_single('StackButton', text='Back')
150+
151+ def _get_skip_button(self):
152+ return self.select_single('StackButton', text='Skip')
153+
154+ @autopilot.logging.log_action(logger.info)
155+ def back(self):
156+ self.pointing_device.click_object(self._get_back_button())
157+ return get_wizard(self).get_languge_page()
158+
159+ @autopilot.logging.log_action(logger.info)
160+ def skip(self):
161+ self.pointing_device.click_object(self._get_skip_button())
162+ return get_wizard(self).get_password_page()
163+
164+
165+class PasswordPage(UbuntuUIToolkitCustomProxyObjectBase):
166+ """Helper class to interact with the password security page"""
167+
168+ SECURITY_SWIPE = 'Swipe'
169+ SECURITY_PASSCODE = 'Passcode'
170+ SECURITY_PASSPHRASE = 'Passphrase'
171+
172+ @classmethod
173+ def validate_dbus_object(cls, path, state):
174+ name = introspection.get_classname_from_path(path)
175+ if name == b'Page':
176+ if state['objectName'][1] == 'passwdPage':
177+ return True
178+ return False
179+
180+ def _get_option_name(self, formatted_name):
181+ # the option name is formatted as follows:
182+ # need to read the name between bold tags
183+ # '<b>Passcode</b> (4 digits only)'
184+ formatted_pattern = re.compile(r'<b>(.*?)</b>.*',
185+ re.IGNORECASE | re.DOTALL)
186+ name = None
187+ match = formatted_pattern.search(formatted_name)
188+ if match:
189+ name = match.group(1)
190+ return name
191+
192+ def _get_all_options(self):
193+ return self.select_many('OptionSelectorDelegate')
194+
195+ def _get_continue_button(self):
196+ return self.select_single('StackButton', text='Continue')
197+
198+ def _get_back_button(self):
199+ return self.select_single('StackButton', text='Back')
200+
201+ def _get_selected_option(self):
202+ return self.select_single(
203+ 'OptionSelectorDelegate', visible='True', selected='True')
204+
205+ def get_selected_security_option(self):
206+ name_fmt = self._get_selected_option().get_properties()['subText']
207+ return self._get_option_name(name_fmt)
208+
209+ @autopilot.logging.log_action(logger.info)
210+ def select_security_option(self, selected_option):
211+ options = self._get_all_options()
212+ for option in options:
213+ name = self._get_option_name(option.get_properties()['subText'])
214+ if name == selected_option:
215+ self.pointing_device.click_object(option)
216+
217+ @autopilot.logging.log_action(logger.info)
218+ def back(self, sim_inserted):
219+ self.pointing_device.click_object(self._get_back_button())
220+ wizard = get_wizard(self)
221+ if sim_inserted:
222+ page = wizard.get_languge_page()
223+ else:
224+ page = wizard.get_sim_page()
225+ return page
226+
227+ @autopilot.logging.log_action(logger.info)
228+ def continue_(self):
229+ option = self.get_selected_security_option()
230+ self.pointing_device.click_object(self._get_continue_button())
231+ wizard = get_wizard(self)
232+ if option == self.SECURITY_SWIPE:
233+ next_page = wizard.get_wifi_connect_page()
234+ else:
235+ next_page = wizard.get_password_entry_page()
236+ return next_page
237+
238+
239+class PasswordEntryPage(UbuntuUIToolkitCustomProxyObjectBase):
240+ """Helper class to interact with the pin entry page"""
241+
242+ @classmethod
243+ def validate_dbus_object(cls, path, state):
244+ name = introspection.get_classname_from_path(path)
245+ if name == b'Page':
246+ if state['objectName'][1] == 'passwdSetPage':
247+ return True
248+ return False
249+
250+ def _get_pinpad_button(self, char):
251+ return self.select_single('PinPadButton', text=char)
252+
253+ def _get_continue_button(self):
254+ return self.select_single('StackButton', text='Continue')
255+
256+ def _get_back_button(self):
257+ return self.select_single('StackButton', text='Back')
258+
259+ def _get_text_field(self):
260+ return self.select_single('QQuickTextInput')
261+
262+ @autopilot.logging.log_action(logger.info)
263+ def enter_pin(self, pin):
264+ for char in pin:
265+ self.pointing_device.click_object(self._get_pinpad_button(char))
266+ return get_wizard(self).get_confirm_password_page()
267+
268+ @autopilot.logging.log_action(logger.info)
269+ def enter_text(self, text):
270+ self.pointing_device.click_object(self._get_text_field())
271+ autopilot.input.Keyboard.create().type(text)
272+
273+ @autopilot.logging.log_action(logger.info)
274+ def back(self):
275+ self.pointing_device.click_object(self._get_back_button())
276+ return get_wizard(self).get_password_page()
277+
278+ @autopilot.logging.log_action(logger.info)
279+ def continue_(self):
280+ autopilot.input.Keyboard.create().press_and_release('Enter')
281+ return get_wizard(self).get_confirm_password_page()
282+
283+
284+class ConfirmPasswordPage(UbuntuUIToolkitCustomProxyObjectBase):
285+ """Helper class to interact with the pin confirmation page"""
286+
287+ @classmethod
288+ def validate_dbus_object(cls, path, state):
289+ name = introspection.get_classname_from_path(path)
290+ if name == b'Page':
291+ if state['objectName'][1] == 'passwdConfirmPage':
292+ return True
293+ return False
294+
295+ def _get_pinpad_button(self, char):
296+ return self.select_single('PinPadButton', text=char)
297+
298+ def _get_continue_button(self):
299+ return self.select_single('StackButton', text='Continue')
300+
301+ def _get_back_button(self):
302+ return self.select_single('StackButton', text='Back')
303+
304+ def _get_text_field(self):
305+ return self.select_single('QQuickTextInput')
306+
307+ @autopilot.logging.log_action(logger.info)
308+ def enter_pin(self, pin):
309+ for char in pin:
310+ self.pointing_device.click_object(self._get_pinpad_button(char))
311+ return get_wizard(self).get_wifi_connect_page()
312+
313+ @autopilot.logging.log_action(logger.info)
314+ def enter_text(self, text):
315+ self.pointing_device.click_object(self._get_text_field())
316+ autopilot.input.Keyboard.create().type(text)
317+
318+ @autopilot.logging.log_action(logger.info)
319+ def back(self):
320+ self.pointing_device.click_object(self._get_back_button())
321+ return get_wizard(self).get_password_page()
322+
323+ @autopilot.logging.log_action(logger.info)
324+ def continue_(self):
325+ autopilot.input.Keyboard.create().press_and_release('Enter')
326+ return get_wizard(self).get_wifi_connect_page()
327+
328+
329+class WifiConnectPage(UbuntuUIToolkitCustomProxyObjectBase):
330+ """Helper class to interact with the Wi-Fi network list page"""
331+
332+ @classmethod
333+ def validate_dbus_object(cls, path, state):
334+ name = introspection.get_classname_from_path(path)
335+ if name == b'Page':
336+ if state['objectName'][1] == 'wifiPage':
337+ return True
338+ return False
339+
340+ def _get_network(self, ssid):
341+ return self.wait_select_single(
342+ 'Standard', objectName='accessPoint', text=ssid, visible='True')
343+
344+ def _get_network_checkbox(self, ssid):
345+ return self._get_network(ssid).select_single(
346+ 'CheckBox', visible='True')
347+
348+ def _get_notification(self, unity):
349+ logger.info('Waiting longer for notification object')
350+ with override_proxy_timeout(unity, 30):
351+ return unity.wait_select_single(
352+ Notification, objectName='notification1', visible='True')
353+
354+ def _get_back_button(self):
355+ return self.wait_select_single(
356+ 'StackButton', text='Back', visible='True')
357+
358+ def _get_skip_button(self):
359+ return self.wait_select_single(
360+ 'StackButton', text='Skip', visible='True')
361+
362+ def _get_continue_button(self):
363+ return self.wait_select_single(
364+ 'StackButton', text='Continue', visible='True')
365+
366+ def is_network_checked(self, ssid):
367+ return self._get_network_checkbox(ssid).get_properties()['checked']
368+
369+ @autopilot.logging.log_action(logger.info)
370+ def select_network(self, unity, ssid):
371+ self.pointing_device.click_object(self._get_network_checkbox(ssid))
372+ return PasswordNotification(self._get_notification(unity))
373+
374+ @autopilot.logging.log_action(logger.info)
375+ def back(self):
376+ self.pointing_device.click_object(self._get_back_button())
377+ return get_wizard(self).get_password_page()
378+
379+ @autopilot.logging.log_action(logger.info)
380+ def skip(self):
381+ self.pointing_device.click_object(self._get_skip_button())
382+ return get_wizard(self).get_reporting_page()
383+
384+ @autopilot.logging.log_action(logger.info)
385+ def continue_(self):
386+ self.pointing_device.click_object(self._get_continue_button())
387+ return get_wizard(self).get_reporting_page()
388+
389+
390+class Notification(UbuntuUIToolkitCustomProxyObjectBase):
391+ """Base class for notification objects"""
392+ pass
393+
394+
395+class PasswordNotification(Notification):
396+ """Helper class to interact with the password entry notification"""
397+
398+ def __init__(self, notification):
399+ self.notification = notification
400+
401+ def _get_connect_button(self):
402+ return self.notification.wait_select_single(
403+ 'Button', text='Connect', objectName='notify_button0')
404+
405+ def _get_cancel_buttoon(self):
406+ return self.notification.wait_select_single(
407+ 'Button', text='Cancel', objectName='notify_button1')
408+
409+ def _get_text_field(self):
410+ return self.notification.wait_select_single('TextField')
411+
412+ @autopilot.logging.log_action(logger.info)
413+ def enter_text(self, text):
414+ self._get_text_field().write(text)
415+
416+ @autopilot.logging.log_action(logger.info)
417+ def connect(self):
418+ self.notification.pointing_device.click_object(
419+ self._get_connect_button())
420+
421+ @autopilot.logging.log_action(logger.info)
422+ def cancel(self):
423+ self.notification.pointing_device.click_object(
424+ self._get_cancel_buttoon())
425+
426+
427+class ReportingPage(UbuntuUIToolkitCustomProxyObjectBase):
428+ """Helper class to interact with the reporting page"""
429+
430+ @classmethod
431+ def validate_dbus_object(cls, path, state):
432+ name = introspection.get_classname_from_path(path)
433+ if name == b'Page':
434+ if state['objectName'][1] == 'reportingPage':
435+ return True
436+ return False
437+
438+ def _get_continue_button(self):
439+ return self.select_single('StackButton', text='Continue')
440+
441+ def _get_back_button(self):
442+ return self.select_single('StackButton', text='Back')
443+
444+ @autopilot.logging.log_action(logger.info)
445+ def back(self):
446+ self.pointing_device.click_object(self._get_back_button())
447+ return get_wizard(self).get_wifi_connect_page()
448+
449+ @autopilot.logging.log_action(logger.info)
450+ def continue_(self):
451+ self.pointing_device.click_object(self._get_continue_button())
452+ return get_wizard(self).get_finished_page()
453+
454+
455+class FinishedPage(UbuntuUIToolkitCustomProxyObjectBase):
456+ """Helper class to interact with the finished page"""
457+
458+ @classmethod
459+ def validate_dbus_object(cls, path, state):
460+ name = introspection.get_classname_from_path(path)
461+ if name == b'Page':
462+ if state['objectName'][1] == 'finishedPage':
463+ return True
464+ return False
465+
466+ def _get_finish_button(self):
467+ return self.wait_select_single(
468+ 'StackButton', text='Finish', visible='True')
469+
470+ @autopilot.logging.log_action(logger.info)
471+ def finish(self):
472+ self.pointing_device.click_object(self._get_finish_button())
473+ self.wait_until_destroyed()