Merge lp:~elopio/u1-test-utils/in-dash-sso into lp:u1-test-utils
- in-dash-sso
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~elopio/u1-test-utils/in-dash-sso |
Merge into: | lp:u1-test-utils |
Diff against target: |
778 lines (+712/-0) 13 files modified
bin/vm_session_setup.py (+19/-0) selftest.py (+19/-0) tests/__init__.py (+114/-0) tests/browsers.py (+24/-0) tests/dash.py (+131/-0) tests/goods.py (+9/-0) tests/sso.py (+147/-0) tests/test_dash.py (+34/-0) tests/test_dbus.py (+19/-0) tests/test_purchase_good.py (+92/-0) tests/test_session.py (+19/-0) tests/test_users.py (+42/-0) tests/users.py (+43/-0) |
To merge this branch: | bzr merge lp:~elopio/u1-test-utils/in-dash-sso |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Canonical ISD hackers | Pending | ||
Review via email: mp+162634@code.launchpad.net |
This proposal has been superseded by a proposal from 2013-05-06.
Commit message
Added the code to create a user and log in to Ubuntu SSO.
Description of the change
Leo Arias (elopio) wrote : | # |
- 32. By Leo Arias
-
Implemented the log out with a call to the sso dbus.
- 33. By Leo Arias
-
Implemented the is_log_in with the sso dbus.
- 34. By Leo Arias
-
Moved the sso helpers to the sso module.
- 35. By Leo Arias
-
Use the django config.
- 36. By Leo Arias
-
Added the missing settings and schema.
- 37. By Leo Arias
-
Cleanup.
- 38. By Leo Arias
-
Clean up after login, keep the system clean.
- 39. By Leo Arias
-
Cleaned the log in and log out test.
- 40. By Leo Arias
-
Some remainders of the failed config version.
- 41. By Leo Arias
-
Updated to the latests u1testutils version.
- 42. By Leo Arias
-
Use the async find credentials.
- 43. By Leo Arias
-
Import os instead of os.path.
- 44. By Leo Arias
-
Moved the mainloop call to the methods that use it.
- 45. By Leo Arias
-
Added a sso server parameter to create account.
- 46. By Leo Arias
-
Added an assertion that the user is not logged in.
- 47. By Leo Arias
-
Pass the sso url to the login method, added a FIXME on the django config import.
- 48. By Leo Arias
-
Comment about the user without payment method.
- 49. By Leo Arias
-
Pass the test to log in to make sure the user is logged out afterwards.
- 50. By Leo Arias
-
Added a comment about deleting users.
- 51. By Leo Arias
-
Added fixme comments to django imports.
- 52. By Leo Arias
-
Moved the async method to sso, and added to assertion methods to users.
- 53. By Leo Arias
-
Missing parameter.
- 54. By Leo Arias
-
Extra parameter.
- 55. By Leo Arias
-
Wrong assertion.
- 56. By Leo Arias
-
Also pass the ubuntu server url from the test to the helpers.
- 57. By Leo Arias
-
Refactored is_logged_in to be synchronous.
- 58. By Leo Arias
-
Renamed the sso is_loged_in function.
- 59. By Leo Arias
-
We need to make sure that the SSO service is running.
- 60. By Leo Arias
-
We need to make sure that the SSO service is running.
- 61. By Leo Arias
-
We need to make sure that the SSO service is running.
- 62. By Leo Arias
-
Set the urls as instance attributes.
Unmerged revisions
- 62. By Leo Arias
-
Set the urls as instance attributes.
- 61. By Leo Arias
-
We need to make sure that the SSO service is running.
- 60. By Leo Arias
-
We need to make sure that the SSO service is running.
- 59. By Leo Arias
-
We need to make sure that the SSO service is running.
- 58. By Leo Arias
-
Renamed the sso is_loged_in function.
- 57. By Leo Arias
-
Refactored is_logged_in to be synchronous.
- 56. By Leo Arias
-
Also pass the ubuntu server url from the test to the helpers.
- 55. By Leo Arias
-
Wrong assertion.
- 54. By Leo Arias
-
Extra parameter.
- 53. By Leo Arias
-
Missing parameter.
Preview Diff
1 | === added directory 'bin' |
2 | === added file 'bin/vm_session_setup.py' |
3 | --- bin/vm_session_setup.py 1970-01-01 00:00:00 +0000 |
4 | +++ bin/vm_session_setup.py 2013-05-06 17:45:31 +0000 |
5 | @@ -0,0 +1,19 @@ |
6 | +#!/usr/bin/env python |
7 | +""" |
8 | +Setup the gnome session environment to suit test needs. |
9 | +""" |
10 | + |
11 | +from gi.repository import Gio, Gtk |
12 | + |
13 | + |
14 | +def run(args=None): |
15 | + session = Gio.Settings('org.gnome.desktop.session') |
16 | + # Never sleep, we're in a vm |
17 | + session.set_uint('idle-delay', 0) |
18 | + # Never lock the screen either |
19 | + screen_saver = Gio.Settings('org.gnome.desktop.screensaver') |
20 | + screen_saver.set_boolean('lock-enabled', False) |
21 | + |
22 | + |
23 | +if __name__ == "__main__": |
24 | + run() |
25 | |
26 | === added file 'selftest.py' |
27 | --- selftest.py 1970-01-01 00:00:00 +0000 |
28 | +++ selftest.py 2013-05-06 17:45:31 +0000 |
29 | @@ -0,0 +1,19 @@ |
30 | +#!/usr/bin/env python |
31 | + |
32 | +import sys |
33 | + |
34 | +import tests |
35 | + |
36 | +# load tests |
37 | +# filter tests |
38 | +# order tests |
39 | +# run tests |
40 | + |
41 | + |
42 | +# We discover tests under './tests', the python 'load_test' protocol can be |
43 | +# used in test modules for more fancy stuff. |
44 | +discover_args = ['discover', |
45 | + '--start-directory', './tests', |
46 | + ] |
47 | +tests.TestProgram(__name__, argv=[sys.argv[0]] + discover_args + sys.argv[1:]) |
48 | + |
49 | |
50 | === added directory 'tests' |
51 | === added file 'tests/__init__.py' |
52 | --- tests/__init__.py 1970-01-01 00:00:00 +0000 |
53 | +++ tests/__init__.py 2013-05-06 17:45:31 +0000 |
54 | @@ -0,0 +1,114 @@ |
55 | +import os |
56 | +import selenium |
57 | +from selenium.webdriver.firefox import ( |
58 | + firefox_binary, |
59 | + webdriver as ff_webdriver, |
60 | + ) |
61 | + |
62 | +from sst import runtests as stests |
63 | +import testtools |
64 | +from testtools import ( |
65 | + run, |
66 | + testcase, |
67 | + ) |
68 | +import unittest |
69 | +import unity.tests as utests |
70 | + |
71 | + |
72 | +class TestCase(testcase.TestCase): |
73 | + pass |
74 | + |
75 | + |
76 | +class FirefoxNoRemote(firefox_binary.FirefoxBinary): |
77 | + """A firefox that can be called by all apps. |
78 | + |
79 | + Selelnium provides a firefox instance working in isolation. While that's |
80 | + the most common case, we need a variant here as we don't control how the |
81 | + dash or any lens will call the web browser. |
82 | + """ |
83 | + |
84 | + def __init__(self, firefox_path=None): |
85 | + super(FirefoxNoRemote, self).__init__(firefox_path) |
86 | + if self._firefox_env.get('MOZ_NO_REMOTE', None) is not None: |
87 | + del self._firefox_env['MOZ_NO_REMOTE'] |
88 | + |
89 | + if selenium.__version__ < '2.32': |
90 | + # FIXME: The constructor above is all that is needed for selenium 2.32, |
91 | + # but it hasn't been pushed in all places so far so the method below |
92 | + # achieve the same trick for previous selenium releases |
93 | + def _start_from_profile_path(self, path): |
94 | + import platform |
95 | + from subprocess import Popen, PIPE, STDOUT |
96 | + self._firefox_env["XRE_PROFILE_PATH"] = path |
97 | + self._firefox_env["MOZ_CRASHREPORTER_DISABLE"] = "1" |
98 | + self._firefox_env["NO_EM_RESTART"] = "1" |
99 | + |
100 | + if platform.system().lower() == 'linux': |
101 | + self._modify_link_library_path() |
102 | + command = [self._start_cmd, "-silent"] |
103 | + if self.command_line is not None: |
104 | + for cli in self.command_line: |
105 | + command.append(cli) |
106 | + |
107 | + Popen(command, stdout=PIPE, stderr=STDOUT, |
108 | + env=self._firefox_env).communicate() |
109 | + command[1] = '-foreground' |
110 | + self.process = Popen( |
111 | + command, stdout=PIPE, stderr=STDOUT, |
112 | + env=self._firefox_env) |
113 | + |
114 | + |
115 | +class WebDriverNoRemote(ff_webdriver.WebDriver): |
116 | + """A selenium webdriver using FirefoxNoRemote. |
117 | + |
118 | + See FirefoxNoRemote for rationale. |
119 | + """ |
120 | + |
121 | + def __init__(self, firefox_profile=None, firefox_binary=None, timeout=30, |
122 | + capabilities=None, proxy=None): |
123 | + super(WebDriverNoRemote, self).__init__( |
124 | + firefox_profile, FirefoxNoRemote(), timeout, capabilities, proxy) |
125 | + |
126 | + |
127 | +class FirefoxNoRemoteFactory(stests.FirefoxFactory): |
128 | + """A browser factory using FirefoxNoRemote. |
129 | + |
130 | + See FirefoxNoRemote for rationale. |
131 | + """ |
132 | + |
133 | + webdriver_class = WebDriverNoRemote |
134 | + |
135 | + |
136 | +class UnitySSTestcase(utests.UnityTestCase, stests.SSTTestCase): |
137 | + |
138 | + def setUp(self): |
139 | + # We need to setup a firefox instance that capture all web connections |
140 | + self.browser_factory = FirefoxNoRemoteFactory() |
141 | + utests.UnityTestCase.setUp(self) |
142 | + # FIXME: Urgh, for an unclear reason, the following occurs in |
143 | + # autopilot.testcase.LoggedTestCase.setUp when super(LoggedTestCase, |
144 | + # self).setUp() is called. The joy of multiple inheritance... One more |
145 | + # reason to avoid it but sst doesn't provide a way to compose instead |
146 | + # of inheriting in our case. If we leave the following call in place, |
147 | + # we end up calling SSTTestCase.setUp twice which is utterly wrong. One |
148 | + # workaround may be to make SSTTestCase detect double calls and avoid |
149 | + # them but that's even dirtier than the actual behavior and may lead to |
150 | + # even more obscure failures -- vila 2013-03-27 |
151 | + # stests.SSTTestCase.setUp(self) |
152 | + |
153 | + # Check that the browser started with a single window opened |
154 | + self.assertEqual(1, len(self.browser.window_handles)) |
155 | + self.assertEqual('about:blank', self.browser.current_url) |
156 | + |
157 | + # FIXME: sst overrides testtools definition, that breaks autopilot |
158 | + # assumption that shortDescription can be used to generate a unique log |
159 | + # file. -- vila 2013-03-27 |
160 | + shortDescription = testcase.TestCase.shortDescription |
161 | + |
162 | +class TestProgram(testtools.run.TestProgram): |
163 | + |
164 | + def __init__(self, module, argv, stdout=None, testRunner=None, exit=True): |
165 | + if testRunner is None: |
166 | + testRunner = unittest.TextTestRunner |
167 | + super(TestProgram, self).__init__(module, argv=argv, stdout=stdout, |
168 | + testRunner=testRunner, exit=exit) |
169 | |
170 | === added file 'tests/browsers.py' |
171 | --- tests/browsers.py 1970-01-01 00:00:00 +0000 |
172 | +++ tests/browsers.py 2013-05-06 17:45:31 +0000 |
173 | @@ -0,0 +1,24 @@ |
174 | +from sst import actions |
175 | + |
176 | + |
177 | +class Browser(object): |
178 | + |
179 | + def __init__(self, test): |
180 | + super(Browser, self).__init__() |
181 | + self.browser = test.browser |
182 | + self.nb_windows = len(self.browser.window_handles) |
183 | + |
184 | + def switch_to_new_window(self, test): |
185 | + def new_window_appeared(): |
186 | + return len(self.browser.window_handles) > 1 |
187 | + actions.wait_for(new_window_appeared) |
188 | + test.assertEqual(2, len(self.browser.window_handles)) |
189 | + self.browser.switch_to_window(self.browser.window_handles[1]) |
190 | + |
191 | + @property |
192 | + def url(self): |
193 | + return self.browser.current_url |
194 | + |
195 | + @property |
196 | + def title(self): |
197 | + return self.browser.title |
198 | |
199 | === added file 'tests/dash.py' |
200 | --- tests/dash.py 1970-01-01 00:00:00 +0000 |
201 | +++ tests/dash.py 2013-05-06 17:45:31 +0000 |
202 | @@ -0,0 +1,131 @@ |
203 | +from autopilot.matchers import Eventually |
204 | +from testtools.matchers import ( |
205 | + Equals, |
206 | + GreaterThan, |
207 | + NotEquals, |
208 | +) |
209 | +from unity.emulators import dash as udash |
210 | + |
211 | + |
212 | +def wait_for_category(test, scope, category_name): |
213 | + get_category = lambda: scope.get_category_by_name(category_name) |
214 | + test.assertThat(get_category, Eventually(NotEquals(None))) |
215 | + return get_category() |
216 | + |
217 | + |
218 | +class Window(object): |
219 | + """A dash window with a content, including buttons and links.""" |
220 | + |
221 | + def __init__(self, user): |
222 | + self.user = user |
223 | + |
224 | +class Home(Window): |
225 | + """The first window opened when the dash is activated.""" |
226 | + |
227 | + def get_result(self, test): |
228 | + lens = test.unity.dash.get_current_lens() |
229 | + # Wait for the category we care about to appear (that's the one where |
230 | + # goods can be bought). |
231 | + category = lens.get_category_by_name('More suggestions') |
232 | + |
233 | + refresh_results_fn = lambda: len(category.get_results()) |
234 | + test.assertThat(refresh_results_fn, Eventually(GreaterThan(0))) |
235 | + # Select first result as soon as it appears |
236 | + return category.get_results()[0] |
237 | + |
238 | + def get_preview(self, test, good): |
239 | + test.unity.dash.ensure_visible() |
240 | + test.addCleanup(test.unity.dash.ensure_hidden) |
241 | + if test.lens_name == 'music': |
242 | + test.unity.dash.reveal_music_lens() |
243 | + # Start the search |
244 | + test.keyboard.type(good.search) |
245 | + result = self.get_result(test) |
246 | + # The name of the result is different between home lens and music lens |
247 | + # :-/ -- vila 2013-04-04 |
248 | + test.assertTrue(good.name in result.name) |
249 | + # We got the right one, preview it |
250 | + result.preview() |
251 | + test.assertThat(test.unity.dash.view.preview_displaying, |
252 | + Eventually(Equals(True))) |
253 | + test.assertThat(test.unity.dash.view.get_preview_container, |
254 | + Eventually(NotEquals(None))) |
255 | + container = test.unity.dash.view.get_preview_container() |
256 | + # We have the preview now |
257 | + return Preview(self.user, good, container) |
258 | + |
259 | + |
260 | +class WindowWithGood(Window): |
261 | + |
262 | + def __init__(self, user, good): |
263 | + super(WindowWithGood, self).__init__(user) |
264 | + self.good = good |
265 | + |
266 | + def good_displayed(self): |
267 | + # FIXME: There should be a way to fail ;) -- vila 2013-03-26 |
268 | + return True |
269 | + |
270 | + |
271 | +class Preview(WindowWithGood): |
272 | + |
273 | + def __init__(self, user, good, container): |
274 | + super(Preview, self).__init__(user, good) |
275 | + self.container = container |
276 | + |
277 | + |
278 | + def get_dash_preview(self): |
279 | + content = self.container.get_children_by_type(udash.PreviewContent)[0] |
280 | + preview = content.get_current_preview() |
281 | + return preview |
282 | + |
283 | + def good_displayed(self): |
284 | + preview = self.get_dash_preview() |
285 | + # FIXME: At some point, self.good.name == preview.text_boxes[0].text |
286 | + # used to be True... we need some other way now -- vila 2013-04-03 |
287 | + return True |
288 | + |
289 | + def get_download_button(self): |
290 | + preview = self.get_dash_preview() |
291 | + download = preview.get_action_by_id('download_album') |
292 | + return download |
293 | + |
294 | + def click_download(self, test): |
295 | + download = self.get_download_button() |
296 | + # FIXME: MusicPreview should allow executing the action but it's |
297 | + # currently broken (http://pad.lv/1163930) -- vila 2013-04-03 |
298 | + mouse = udash.Mouse() |
299 | + mouse.move(download.x, download.y) |
300 | + mouse.click() |
301 | + # FIXME: The current implementation diverges from the design spec by |
302 | + # going straight to the web without presenting a window explaining why |
303 | + # the good can't be bought while also displaying the good. As of today, |
304 | + # the workaround is to display a message instead of the button. But |
305 | + # this is only implemented for the music lens, not the home |
306 | + # lens. -- vila 2013-03-28 |
307 | + return GotoU1(self.user, self.good) |
308 | + |
309 | + |
310 | +class GotoU1(WindowWithGood): |
311 | + |
312 | + def __init__(self, user, good): |
313 | + super(GotoU1, self).__init__(user, good) |
314 | + if self.user.logged_in: |
315 | + if self.user.payment_method == 'expired': |
316 | + self.message = ('Your card has expired.' |
317 | + 'To add a new payment method, please visit...') |
318 | + else: |
319 | + self.message = 'To add a payment method, please visit...' |
320 | + else: |
321 | + self.message = ("You don't have an Ubuntu One account," |
322 | + " or you are not logged in.") |
323 | + |
324 | + def click_goto_u1(self): |
325 | + from tests import browsers |
326 | + browser = browsers.get_default() |
327 | + # Fake the app response by forcing the url in the browser |
328 | + if self.user.logged_in: |
329 | + browser.current_url = 'https://pay.ubuntu.com' |
330 | + else: |
331 | + browser.current_url = 'https://one.ubuntu.com' |
332 | + return browser |
333 | + |
334 | |
335 | === added file 'tests/goods.py' |
336 | --- tests/goods.py 1970-01-01 00:00:00 +0000 |
337 | +++ tests/goods.py 2013-05-06 17:45:31 +0000 |
338 | @@ -0,0 +1,9 @@ |
339 | +class Good(object): |
340 | + |
341 | + def __init__(self, name, search): |
342 | + # Name is used to lightly check the result we get from the |
343 | + # search. Lightly here means that we ensure that the result name |
344 | + # contains the good name. |
345 | + self.name = name |
346 | + # How to search for this good from the dash |
347 | + self.search = search |
348 | |
349 | === added file 'tests/sso.py' |
350 | --- tests/sso.py 1970-01-01 00:00:00 +0000 |
351 | +++ tests/sso.py 2013-05-06 17:45:31 +0000 |
352 | @@ -0,0 +1,147 @@ |
353 | +import os |
354 | +import subprocess |
355 | +import time |
356 | + |
357 | +from autopilot.emulators import input |
358 | +from autopilot import introspection |
359 | +from u1testutils import settings |
360 | + |
361 | + |
362 | +class UbuntuSSO(object): |
363 | + |
364 | + def launch_application(self): |
365 | + self._prepare_environment() |
366 | + process = subprocess.Popen( |
367 | + '/usr/lib/ubuntu-sso-client/ubuntu-sso-login-qt') |
368 | + self.application = ( |
369 | + introspection.get_autopilot_proxy_object_for_process(process)) |
370 | + |
371 | + def _prepare_environment(self): |
372 | + os.environ['TESTABILITY'] = '1' |
373 | + os.environ['USSOC_SERVICE_URL'] = '{0}/api/1.0/'.format( |
374 | + settings.OPENID_SSO_SERVER_URL) |
375 | + os.environ['SSO_AUTH_BASE_URL'] = settings.OPENID_SSO_SERVER_URL |
376 | + os.environ['SSO_UONE_BASE_URL'] = settings.UBUNTUONE_SERVER_URL |
377 | + os.system('pkill ubuntu-sso') |
378 | + subprocess.Popen('/usr/lib/ubuntu-sso-client/ubuntu-sso-login') |
379 | + |
380 | + def log_in(self, user): |
381 | + self.launch_application() |
382 | + log_in_page = self._go_to_log_in_page() |
383 | + success_page = log_in_page.log_in_with_successful_authentication(user) |
384 | + assert (success_page.get_message() == |
385 | + 'You are now logged into Ubuntu One.') |
386 | + success_page.finish() |
387 | + |
388 | + def _go_to_log_in_page(self): |
389 | + sign_up_page = SignUpPage(self.application) |
390 | + sign_up_page.wait_for_action_to_complete() |
391 | + return sign_up_page.go_to_log_in() |
392 | + |
393 | + |
394 | +class Page(object): |
395 | + |
396 | + def __init__(self, application, page_type): |
397 | + self.application = application |
398 | + self.page = self.application.select_single(page_type) |
399 | + self.keyboard = input.get_keyboard() |
400 | + self.mouse = input.get_mouse() |
401 | + |
402 | + def wait_for_action_to_complete(self): |
403 | + """Wait for the last action to complete. |
404 | + |
405 | + This method waits for the loading overlay to dissapear. |
406 | + |
407 | + """ |
408 | + # If autopilot executes too fast, we might look for the loading overlay |
409 | + # before it is shown. We should wait a little to give it time to |
410 | + # appear, but it's not possible to wait for it, because if autopilot |
411 | + # executes too slow, we might look for it after it dissapears. |
412 | + # Thus, the only solution that comes to mind is a sleep. |
413 | + # -- elopio - 2013-05-03 |
414 | + time.sleep(3) |
415 | + loading_overlay = self.application.select_single('LoadingOverlay') |
416 | + loading_overlay.visible.wait_for(False) |
417 | + |
418 | + # TODO propose this to autopilot. -- elopio - 2013-05-03 |
419 | + def get_child_by_type(self, desired_type, **kwargs): |
420 | + children = self.page.get_children_by_type(desired_type, **kwargs) |
421 | + assert len(children) > 0, 'No child found.' |
422 | + assert len(children) == 1, 'More than one child found.' |
423 | + return children[0] |
424 | + |
425 | + |
426 | +class SignUpPage(Page): |
427 | + |
428 | + def __init__(self, application): |
429 | + super(SignUpPage, self).__init__(application, 'SetupAccountPage') |
430 | + |
431 | + def go_to_log_in(self): |
432 | + self.wait_for_action_to_complete() |
433 | + sign_in_label = self.get_child_by_type( |
434 | + 'QLabel', objectName='sign_in_label') |
435 | + self.mouse.move_to_object(sign_in_label) |
436 | + self.mouse.click() |
437 | + self.wait_for_action_to_complete() |
438 | + return LogInPage(self.application) |
439 | + |
440 | + |
441 | +class LogInPage(Page): |
442 | + |
443 | + def __init__(self, application): |
444 | + super(LogInPage, self).__init__(application, 'CurrentUserSignInPage') |
445 | + |
446 | + def log_in_with_successful_authentication(self, user): |
447 | + self._log_in(user.email, user.password) |
448 | + assert self.get_form_errors() is None |
449 | + return SuccessPage(self.application) |
450 | + |
451 | + def _log_in(self, email, password): |
452 | + self._fill_log_in_form(email, password) |
453 | + sign_in_button = self.get_child_by_type( |
454 | + 'QPushButton', objectName='sign_in_button') |
455 | + self.mouse.move_to_object(sign_in_button) |
456 | + self.mouse.click() |
457 | + self.wait_for_action_to_complete() |
458 | + |
459 | + def _fill_log_in_form(self, email, password): |
460 | + email_edit = self.get_child_by_type( |
461 | + 'QLineEdit', objectName='email_edit') |
462 | + self.mouse.move_to_object(email_edit) |
463 | + self.mouse.click() |
464 | + self.keyboard.type(email) |
465 | + password_edit = self.get_child_by_type( |
466 | + 'QLineEdit', objectName='password_edit') |
467 | + self.mouse.move_to_object(password_edit) |
468 | + self.mouse.click() |
469 | + self.keyboard.type(password) |
470 | + |
471 | + def get_form_errors(self): |
472 | + form_errors = self.get_child_by_type( |
473 | + 'QLabel', objectName='form_errors') |
474 | + if form_errors.visible: |
475 | + return form_errors.text |
476 | + else: |
477 | + return None |
478 | + |
479 | + |
480 | +class SuccessPage(Page): |
481 | + |
482 | + def __init__(self, application): |
483 | + super(SuccessPage, self).__init__(application, 'SuccessPage') |
484 | + |
485 | + def get_message(self): |
486 | + label = self.get_child_by_type( |
487 | + 'QLabel', objectName='success_message_body') |
488 | + return label.text |
489 | + |
490 | + def finish(self): |
491 | + # TODO when we have the emulator, this should be on the wizard, not the |
492 | + # page. -- elopio - 2013-05-03 |
493 | + # Bug reported to QWizard because the buttons don't have objectName: |
494 | + # https://bugreports.qt-project.org/browse/QTBUG-29924 |
495 | + # TODO update when the bug is fixed. -- elopio - 2013-05-03 |
496 | + finish_button = self.application.select_single( |
497 | + 'QPushButton', text='&Finish') |
498 | + self.mouse.move_to_object(finish_button) |
499 | + self.mouse.click() |
500 | |
501 | === added file 'tests/test_dash.py' |
502 | --- tests/test_dash.py 1970-01-01 00:00:00 +0000 |
503 | +++ tests/test_dash.py 2013-05-06 17:45:31 +0000 |
504 | @@ -0,0 +1,34 @@ |
505 | +import autopilot |
506 | +from autopilot.matchers import Eventually |
507 | +from testtools.matchers import Contains, Equals, GreaterThan |
508 | +import unity.tests as utests |
509 | + |
510 | + |
511 | +class TestDash(utests.UnityTestCase): |
512 | + |
513 | + # This test needs to be upgraded to catch up with autopilot and unity |
514 | + # recent versions. |
515 | + def xtest_select_preview(self): |
516 | + self.unity.dash.ensure_visible() |
517 | + self.addCleanup(self.unity.dash.ensure_hidden) |
518 | + lens = self.unity.dash.get_current_lens() |
519 | + self.keyboard.type("hendrix") |
520 | + results_category = lens.get_category_by_name("More suggestions") |
521 | + refresh_results_fn = lambda: len(results_category.get_results()) |
522 | + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1))) |
523 | + # Select first result |
524 | + self.keyboard.press_and_release('Enter') |
525 | + # We have the preview now |
526 | + self.keyboard.press_and_release('Enter') |
527 | + self.assertTrue(self.bamf.wait_until_application_is_running( |
528 | + 'firefox.desktop', 10.0)) |
529 | + firefox = self.bamf.get_running_applications_by_desktop_file( |
530 | + 'firefox.desktop')[0] |
531 | + self.addCleanup(firefox.get_windows()[0].close) |
532 | + # FIXME: We can't get the right title as we receive the window too |
533 | + # soon. -- vila 2013-02-21 |
534 | +# for w in firefox.get_windows(): |
535 | +# print 'title: %s' % (w.title) |
536 | +# import pdb; pdb.set_trace() |
537 | +# self.assertEqual('Log in - Mozilla Firefox', |
538 | +# firefox.get_windows()[0].title) |
539 | |
540 | === added file 'tests/test_dbus.py' |
541 | --- tests/test_dbus.py 1970-01-01 00:00:00 +0000 |
542 | +++ tests/test_dbus.py 2013-05-06 17:45:31 +0000 |
543 | @@ -0,0 +1,19 @@ |
544 | +import tests |
545 | + |
546 | +import dbus |
547 | +from gi.repository import Gio, Gtk |
548 | + |
549 | +class TestDBUS(tests.TestCase): |
550 | + |
551 | + def setUp(self): |
552 | + super(TestDBUS, self).setUp() |
553 | + self.bus = dbus.SessionBus() |
554 | + # Register and clean up our own bus. |
555 | + self.bus.request_name('canonical.online-services.selftest') |
556 | + self.addCleanup(self.bus.release_name, |
557 | + 'canonical.online-services.selftest') |
558 | + |
559 | + def test_it(self): |
560 | + # FIXME: We need some real tests :0) But the setUp above will be |
561 | + # exercised even by an eempty test and represent useful knowledge. |
562 | + pass |
563 | |
564 | === added file 'tests/test_purchase_good.py' |
565 | --- tests/test_purchase_good.py 1970-01-01 00:00:00 +0000 |
566 | +++ tests/test_purchase_good.py 2013-05-06 17:45:31 +0000 |
567 | @@ -0,0 +1,92 @@ |
568 | +import unity.tests as utests |
569 | + |
570 | +from sst import actions as sactions |
571 | + |
572 | +import tests |
573 | +from tests import ( |
574 | + browsers, |
575 | + dash, |
576 | + goods, |
577 | + users, |
578 | +) |
579 | + |
580 | +# Command to use different servers for in-dash purchases |
581 | +# pkill unity-music; G_MESSAGES_DEBUG=all U1_STAGING_WEBAPI=https://staging.one.ubuntu.com/ U1_STAGING_AUTHENTICATION=https://login.staging.ubuntu.com/ /usr/lib/x86_64-linux-gnu/unity-lens-music/unity-musicstore-daemon |
582 | + |
583 | +# Command to use the local servers for in-dash purchases |
584 | +# pkill unity-music; G_MESSAGES_DEBUG=all U1_STAGING_WEBAPI=http://u1.local:8003/ U1_STAGING_AUTHENTICATION=http://sso.local:8001/ /usr/lib/x86_64-linux-gnu/unity-lens-music/unity-musicstore-daemon |
585 | + |
586 | + |
587 | +class TestPurchaseGood(tests.UnitySSTestcase): |
588 | + |
589 | + |
590 | + def setUp(self): |
591 | + super(TestPurchaseGood, self).setUp() |
592 | + # A user will purchase a good |
593 | + self.user = users.User.make_unique() |
594 | + # Arbitrarily using a good that is known to work |
595 | + self.good = goods.Good(u'Jimi Hendrix', 'hendrix') |
596 | + |
597 | + def preview_good(self): |
598 | + home = dash.Home(self.user) |
599 | + self.preview = home.get_preview(self, self.good) |
600 | + # user get a preview screen and a button to click on |
601 | + self.assertTrue(self.preview.good_displayed()) |
602 | + |
603 | + def purchase_good(self): |
604 | + # Attempt to purchase the good |
605 | + return self.preview.click_download(self) |
606 | + |
607 | + |
608 | +class TestUserCannotPay(TestPurchaseGood): |
609 | + |
610 | + scenarios = [(name, {'lens_name': name}) for name in ('home', 'music',)] |
611 | + |
612 | + def test_user_not_logged_in(self): |
613 | + # User not logged in |
614 | + self.user.logout() |
615 | + self.preview_good() |
616 | + if self.lens_name == 'music': |
617 | + # There is a workaround in place for the music lens only that |
618 | + # forbids testing the rest of the workflow |
619 | + self.assertIs(None, self.preview.get_download_button()) |
620 | + # FIXME: It would be nice to check the message displayed when the |
621 | + # button is not available but it's not exposed -- vila 2013-04-04 |
622 | + return |
623 | + goto_u1 = self.purchase_good() |
624 | + # diverges from the design spec, we go directly to the web |
625 | + # self.assertTrue(goto_u1.good_displayed()) |
626 | + browser = browsers.Browser(self) |
627 | + # We've been redirected to the web |
628 | + browser.switch_to_new_window(self) |
629 | + sactions.wait_for(sactions.assert_url_contains, |
630 | + 'https://login.ubuntu.com') |
631 | + self.assertEqual('Log in', browser.title) |
632 | + |
633 | + def test_no_payment_method_stored(self): |
634 | + # User has no payment method |
635 | + self.user.login() |
636 | + self.user.payment_method = None |
637 | + goto_u1 = self.purchase_good() |
638 | + self.assertTrue(goto_u1.good_displayed()) |
639 | + self.assertIn('add a payment method', goto_u1.message) |
640 | + # The default browser is launched to propose paying |
641 | + browser = goto_u1.click_goto_u1() |
642 | + # User is sent to the web |
643 | + self.assertTrue(browser.is_running()) |
644 | + self.assertEqual('https://pay.ubuntu.com', browser.current_url) |
645 | + |
646 | + def test_pay_method_expired(self): |
647 | + # Uesrs's payment has expired |
648 | + self.user.login() |
649 | + self.user.payment_method = 'expired' |
650 | + goto_u1 = self.purchase_good() |
651 | + self.assertTrue(goto_u1.good_displayed()) |
652 | + self.assertIn('card has expired', goto_u1.message) |
653 | + # The default browser is launched to propose paying |
654 | + browser = goto_u1.click_goto_u1() |
655 | + # User is sent to the web |
656 | + self.assertTrue(browser.is_running()) |
657 | + self.assertEqual('https://pay.ubuntu.com', browser.current_url) |
658 | + |
659 | + |
660 | |
661 | === added file 'tests/test_session.py' |
662 | --- tests/test_session.py 1970-01-01 00:00:00 +0000 |
663 | +++ tests/test_session.py 2013-05-06 17:45:31 +0000 |
664 | @@ -0,0 +1,19 @@ |
665 | +import tests |
666 | + |
667 | +from gi.repository import Gio, Gtk |
668 | + |
669 | +class TestGSettings(tests.TestCase): |
670 | + |
671 | + def test_session(self): |
672 | + s = Gio.Settings('org.gnome.desktop.session') |
673 | + orig = s.get_uint('idle-delay') |
674 | + self.addCleanup(s.set_uint, 'idle-delay', orig) |
675 | + s.set_uint('idle-delay', orig + 2) |
676 | + self.assertEqual(orig + 2, s.get_uint('idle-delay')) |
677 | + |
678 | + def test_screensaver(self): |
679 | + s = Gio.Settings('org.gnome.desktop.screensaver') |
680 | + orig = s.get_boolean('lock-enabled') |
681 | + self.addCleanup(s.set_boolean, 'lock-enabled', orig) |
682 | + s.set_boolean('lock-enabled', not orig) |
683 | + self.assertEqual(not orig, s.get_boolean('lock-enabled')) |
684 | |
685 | === added file 'tests/test_users.py' |
686 | --- tests/test_users.py 1970-01-01 00:00:00 +0000 |
687 | +++ tests/test_users.py 2013-05-06 17:45:31 +0000 |
688 | @@ -0,0 +1,42 @@ |
689 | +import mock |
690 | + |
691 | +import tests |
692 | +from tests import users |
693 | + |
694 | + |
695 | +class UsersTest(tests.TestCase): |
696 | + |
697 | + def _patch_u1testutils_settings(self, **kwargs): |
698 | + for setting, new_value in kwargs.items(): |
699 | + name = 'u1testutils.settings.{0}'.format(setting) |
700 | + patcher = mock.patch(name, new_value) |
701 | + self.addCleanup(patcher.stop) |
702 | + patcher.start() |
703 | + |
704 | + def test_make_unique(self): |
705 | + self._patch_u1testutils_settings( |
706 | + EMAIL_ADDRESS_PATTERN='emailconf+%s@example.com', |
707 | + SSO_TEST_ACCOUNT_PASSWORD='password from configuration') |
708 | + with mock.patch('uuid.uuid1') as mock_uuid: |
709 | + mock_uuid.return_value = 'test_uuid' |
710 | + user = users.User.make_unique() |
711 | + |
712 | + self.assertEqual(user.user_data.full_name, 'Test user test_uuid') |
713 | + self.assertEqual( |
714 | + user.user_data.email, 'emailconf+test_uuid@example.com') |
715 | + self.assertEqual( |
716 | + user.user_data.password, 'password from configuration') |
717 | + self.assertEqual(user.is_logged_in(), False) |
718 | + self.assertEqual(user.payment_method, None) |
719 | + |
720 | + def test_log_in(self): |
721 | + user = users.User.make_unique() |
722 | + user.login() |
723 | + self.assertEqual(user.is_logged_in(), True) |
724 | + |
725 | + def test_log_out(self): |
726 | + user = users.User.make_unique() |
727 | + user.logged_in = True |
728 | + user.logout() |
729 | + self.assertEqual(user.is_logged_in(), False) |
730 | + |
731 | |
732 | === added file 'tests/users.py' |
733 | --- tests/users.py 1970-01-01 00:00:00 +0000 |
734 | +++ tests/users.py 2013-05-06 17:45:31 +0000 |
735 | @@ -0,0 +1,43 @@ |
736 | +import dbus |
737 | +from dbus.mainloop.glib import DBusGMainLoop |
738 | + |
739 | +from u1testutils.sso import data, client |
740 | + |
741 | +from tests import sso |
742 | + |
743 | + |
744 | +class User(object): |
745 | + |
746 | + def __init__(self, user_data): |
747 | + self.user_data = user_data |
748 | + self.logged_in = False |
749 | + self.payment_method = None |
750 | + |
751 | + @classmethod |
752 | + def make_unique(cls): |
753 | + user_data = data.User.make_from_configuration(new_user=True) |
754 | + return cls(user_data) |
755 | + |
756 | + def is_logged_in(self): |
757 | + # TODO make this work. |
758 | + #DBusGMainLoop(set_as_default=True) |
759 | + #session_bus = dbus.SessionBus() |
760 | + #proxy = session_bus.get_object( |
761 | + # 'com.ubuntu.sso', '/com/ubuntu/sso/credentials') |
762 | + #interface = dbus.Interface( |
763 | + # proxy, dbus_interface='com.ubuntu.sso.CredentialsManagement') |
764 | + #proxy.connect_to_signal('CredentialsFound', credentials_found) |
765 | + #proxy.connect_to_signal('CredentialsNotFound', credentials_not_found) |
766 | + #proxy.connect_to_signal('CredentialsError', credentials_error) |
767 | + #interface.find_credentials('Ubuntu One', {}) |
768 | + return self.logged_in |
769 | + |
770 | + def login(self): |
771 | + client.create_new_account( |
772 | + self.user_data, captcha_id='Not used', captcha_solution='Not used') |
773 | + ubuntu_sso = sso.UbuntuSSO() |
774 | + ubuntu_sso.log_in(self.user_data) |
775 | + self.logged_in = True |
776 | + |
777 | + def logout(self): |
778 | + self.logged_in = False |
This branch needs: /code.launchpad .net/~elopio/ u1-test- utils/in- dash-prerequisi tes /code.launchpad .net/~elopio/ u1-test- utils/configglu e
https:/
https:/