Merge lp:~elopio/u1-test-utils/in-dash-sso into lp:~canonical-isd-hackers/u1-test-utils/test-in-dash-payments
- in-dash-sso
- Merge into test-in-dash-payments
Status: | Merged |
---|---|
Approved by: | Vincent Ladeuil |
Approved revision: | 62 |
Merged at revision: | 25 |
Proposed branch: | lp:~elopio/u1-test-utils/in-dash-sso |
Merge into: | lp:~canonical-isd-hackers/u1-test-utils/test-in-dash-payments |
Diff against target: |
457 lines (+339/-23) 7 files modified
selftest.py (+2/-1) tests/schema.py (+13/-0) tests/settings.py (+16/-0) tests/sso.py (+230/-0) tests/test_purchase_good.py (+15/-13) tests/test_users.py (+31/-0) tests/users.py (+32/-9) |
To merge this branch: | bzr merge lp:~elopio/u1-test-utils/in-dash-sso |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Vincent Ladeuil (community) | Approve | ||
Review via email: mp+162636@code.launchpad.net |
This proposal supersedes 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
This branch needs:
https:/
Leo Arias (elopio) wrote : Posted in a previous version of this proposal | # |
Vincent Ladeuil (vila) wrote : | # |
41 +import os.path
Just 'import os' and then use os.path in code.
'path' is not a regular module and mentioning it in a import clause is propagating the wrong idea that it is a regular module (look at os.py for details).
76 +DBusQtMainLoop
IMHO, you shouldn't call this when importing the module, instead, tests that
requires it should call it and cleanup accordingly.
79 +def create_
80 + api_client = api.APIClient(
This is an example of the anti-pattern we discussed earlier, referring to a
config setting without any way for the caller to override it smells
wrong. I'm ok to leave it for now as discussed but I would feel better with
a FIXME there ;)
275 - # User not logged in
276 - self.user.logout()
I'm fine with your change of logout() semantic but the comment should be
updated to say something like: "We assume the user is not logged in" or
better an assertion to verify this. But just deleting the comment is
removing some important knowledge from the test.
286 + sactions.wait_for(
287 + sactions.
Fallout from hiding the config in create_
Still ok to go this route for now, even more now that it's clearly
reflected in the test.
291 - # User has no payment method
292 + self.addCleanup
Please keep the comment ;)
I'm a bit surprised to see two self.addCleanup
be made part of self.user.login() instead ? And if that means passing 'self'
to self.user.login(), I'm fine with that too ;)
337 + user.is_
That's a weird syntax 8-/
371 + def is_logged_in(self, callback):
372 + app = QtCore.
373 + deferred = defer.Deferred()
374 + deferred.
375 + def _are_credential
376 + deferred.
377 +
378 + credentials_
379 + credentials_
380 + credentials_
381 + app.exec_()
Ouch, I see why the syntax was weird.
I'd rather go with two helpers in this case (keeping the code but adding
comments explaining the intent, 'callback' is not explained here even if you
can only use it with test assertions): is_logged_in() and
is_not_logged_in().
That may even be the ones I'm asking for in the tests themselves.
Note that in this case I would pass the test object itself again which will
make it clearer than relying on 'callback' being a test method (which is an
indirect way to pass the test object anyway).
383 def login(self):
384 - self.logged_in = True
385 + sso.create_
386 + sso.log_
I've got a feeling we may want to separate user creation and user login in
the future. No objection to land this as is for now.
388 def logout(self):
389 - self.logged_in = False
390 + sso.log_
We don't delete the created user here, may be you should add a comment
explaining your reasoning in login/logout.
Overall, awesome work,...
Leo Arias (elopio) wrote : | # |
> 41 +import os.path
>
> Just 'import os' and then use os.path in code.
Fixed. Thanks for pointing this out.
> 76 +DBusQtMainLoop
>
> IMHO, you shouldn't call this when importing the module, instead, tests that
> requires it should call it and cleanup accordingly.
I moved it to the functions that use the dbus loop. On all the code I've read, they never stop the loop. I'll ask alecu about that.
> 79 +def create_
> 80 + api_client = api.APIClient(
>
> This is an example of the anti-pattern we discussed earlier, referring to a
> config setting without any way for the caller to override it smells
> wrong. I'm ok to leave it for now as discussed but I would feel better with
> a FIXME there ;)
I added a server parameter, and moved the config call to the test.
Also, added a FIXME on the django import.
> 275 - # User not logged in
> 276 - self.user.logout()
>
> I'm fine with your change of logout() semantic but the comment should be
> updated to say something like: "We assume the user is not logged in" or
> better an assertion to verify this. But just deleting the comment is
> removing some important knowledge from the test.
I've put an assertion there.
> 291 - # User has no payment method
> 292 + self.addCleanup
>
> Please keep the comment ;)
I've put a better one.
> I'm a bit surprised to see two self.addCleanup
> be made part of self.user.login() instead ? And if that means passing 'self'
> to self.user.login(), I'm fine with that too ;)
Done.
> 337 + user.is_
>
> That's a weird syntax 8-/
>
>
> 371 + def is_logged_in(self, callback):
> 372 + app = QtCore.
> 373 + deferred = defer.Deferred()
> 374 + deferred.
> 375 + def _are_credential
> 376 + deferred.
> 377 +
> 378 + credentials_
> 379 + credentials_
> 380 + credentials_
> 381 + app.exec_()
>
> Ouch, I see why the syntax was weird.
>
> I'd rather go with two helpers in this case (keeping the code but adding
> comments explaining the intent, 'callback' is not explained here even if you
> can only use it with test assertions): is_logged_in() and
> is_not_logged_in().
>
> That may even be the ones I'm asking for in the tests themselves.
>
> Note that in this case I would pass the test object itself again which will
> make it clearer than relying on 'callback' being a test method (which is an
> indirect way to pass the test object anyway).
I think that now I can improve this a little. Let me try.
> 383 def login(self):
> 384 - self.logged_in = True
> 385 + sso.create_
> 386 + sso.log_
>
> I've got a feeling we may want to separate user creation and user login in
> the future. No objection to land this as is for now.
On the pay ...
Leo Arias (elopio) wrote : | # |
I've made the asynchronous call synchronous, as twisted and dbus were hiding the errors. The alternative would be to use twisted trial.
Vincent Ladeuil (vila) wrote : | # |
First thought after reading the mail containing your newest proposal: wow, that's at least an order of magnitude clearer \o/
> Please wait while I try to refactor the is_logged_in method.
Oh my, that was worth the wait :-D
Thanks for taking my remarks into account and making your code so much more easier to understand for me.
The minor details I've come across while reading are not even worth mentioning here, most of them are already covered by FIXME or TODO comments and I'm confident that the remaining ones will be addressed at the same time.
> I've made the asynchronous call synchronous, as twisted and dbus were hiding the errors. The alternative would be to use twisted trial.
That was a key point, well done.
Asynchronous is harder than synchronous. If we can keep tests free of any asynchronous stuff without reducing their usefulness or stability, we should do that. (IME asynchronous *always* introduce complexity and/hence reduce stability, I'm happy to be proven wrong on exceptions to this rule though).
In short, this proposal provides significant enhancements vastly outweighing any objection I can think of ;)
Let's land it.
Preview Diff
1 | === modified file 'selftest.py' |
2 | --- selftest.py 2013-02-20 15:42:29 +0000 |
3 | +++ selftest.py 2013-05-15 21:02:26 +0000 |
4 | @@ -1,5 +1,6 @@ |
5 | #!/usr/bin/env python |
6 | |
7 | +import os |
8 | import sys |
9 | |
10 | import tests |
11 | @@ -15,5 +16,5 @@ |
12 | discover_args = ['discover', |
13 | '--start-directory', './tests', |
14 | ] |
15 | +os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings' |
16 | tests.TestProgram(__name__, argv=[sys.argv[0]] + discover_args + sys.argv[1:]) |
17 | - |
18 | |
19 | === added file 'tests/schema.py' |
20 | --- tests/schema.py 1970-01-01 00:00:00 +0000 |
21 | +++ tests/schema.py 2013-05-15 21:02:26 +0000 |
22 | @@ -0,0 +1,13 @@ |
23 | +from configglue import schema |
24 | + |
25 | + |
26 | +class InDashPaymentsSchema(schema.Schema): |
27 | + |
28 | + class openid(schema.Section): |
29 | + openid_sso_server_url = schema.StringOption() |
30 | + |
31 | + class ubuntuone(schema.Section): |
32 | + ubuntuone_server_url = schema.StringOption() |
33 | + |
34 | + |
35 | +schema = InDashPaymentsSchema |
36 | |
37 | === added file 'tests/settings.py' |
38 | --- tests/settings.py 1970-01-01 00:00:00 +0000 |
39 | +++ tests/settings.py 2013-05-15 21:02:26 +0000 |
40 | @@ -0,0 +1,16 @@ |
41 | +import os |
42 | + |
43 | +from django_configglue.utils import configglue |
44 | + |
45 | +from tests.schema import schema |
46 | + |
47 | + |
48 | +# Get location of local cfg files. |
49 | +local_configs = os.environ.get('CONFIGGLUE_LOCAL_CONFIG', ['local.cfg']) |
50 | + |
51 | +# Get absolute path for config files |
52 | +current_dir = os.path.dirname(os.path.abspath(__file__)) |
53 | +config_files = map(lambda x: os.path.join(current_dir, x), local_configs) |
54 | + |
55 | +# Glue everything together. |
56 | +configglue(schema, config_files, __name__) |
57 | |
58 | === added file 'tests/sso.py' |
59 | --- tests/sso.py 1970-01-01 00:00:00 +0000 |
60 | +++ tests/sso.py 2013-05-15 21:02:26 +0000 |
61 | @@ -0,0 +1,230 @@ |
62 | +import os |
63 | +import subprocess |
64 | +import threading |
65 | +import time |
66 | + |
67 | +import dbus |
68 | +import dbus.mainloop.glib |
69 | +import gobject |
70 | + |
71 | +from autopilot.emulators import input |
72 | +from autopilot import introspection |
73 | +from u1testutils.sso import api |
74 | + |
75 | + |
76 | +def create_new_account_with_server_api(user, sso_server_url): |
77 | + api_client = api.APIClient(sso_server_url) |
78 | + api_client.create_new_account( |
79 | + user, captcha_id='Not used', captcha_solution='Not used') |
80 | + |
81 | + |
82 | +def log_in_with_desktop_client(user, sso_server_url, ubuntuone_server_url): |
83 | + desktop_client = UbuntuSSODesktopClient( |
84 | + sso_server_url, ubuntuone_server_url) |
85 | + desktop_client.log_in(user) |
86 | + |
87 | + |
88 | +def _start_ubuntu_sso_service_on_desktop(sso_server_url): |
89 | + os.environ['USSOC_SERVICE_URL'] = '{0}/api/1.0/'.format(sso_server_url) |
90 | + os.system('pkill ubuntu-sso') |
91 | + subprocess.Popen('/usr/lib/ubuntu-sso-client/ubuntu-sso-login') |
92 | + |
93 | + |
94 | +def log_out_with_dbus_api(sso_server_url): |
95 | + _start_ubuntu_sso_service_on_desktop(sso_server_url) |
96 | + interface = _get_sso_dbus_interface() |
97 | + interface.clear_credentials('Ubuntu One', {}) |
98 | + |
99 | + |
100 | +def is_logged_in_on_desktop(sso_server_url): |
101 | + _start_ubuntu_sso_service_on_desktop(sso_server_url) |
102 | + credentials = _get_credentials() |
103 | + return credentials is not None |
104 | + |
105 | + |
106 | +def _get_credentials(): |
107 | + signaled = threading.Event() |
108 | + # XXX is there a way to share a variable without the globals? Without that |
109 | + # the signal callbacks don't set the value on the right variable. |
110 | + # -- elopio - 2013-05-15 |
111 | + global _credentials |
112 | + _credentials = None |
113 | + global _credentials_error |
114 | + _credentials_error = None |
115 | + |
116 | + def credentials_found(app_name, credentials=None): |
117 | + if app_name == 'Ubuntu One': |
118 | + global _credentials |
119 | + _credentials = credentials |
120 | + loop.quit() |
121 | + signaled.set() |
122 | + |
123 | + def credentials_error(app_name, error): |
124 | + if app_name == 'Ubuntu One': |
125 | + global _credentials_error |
126 | + _credentials_error = error |
127 | + loop.quit() |
128 | + signaled.set() |
129 | + |
130 | + interface = _get_sso_dbus_interface() |
131 | + interface.connect_to_signal('CredentialsFound', credentials_found) |
132 | + interface.connect_to_signal('CredentialsNotFound', credentials_found) |
133 | + interface.connect_to_signal('CredentialsError', credentials_error) |
134 | + interface.find_credentials('Ubuntu One', {}) |
135 | + loop = gobject.MainLoop() |
136 | + loop.run() |
137 | + signaled.wait() |
138 | + if _credentials_error: |
139 | + raise dbus.DBusException(_credentials_error['message']) |
140 | + else: |
141 | + return _credentials |
142 | + |
143 | + |
144 | +def _get_sso_dbus_interface(): |
145 | + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
146 | + session_bus = dbus.SessionBus() |
147 | + proxy = session_bus.get_object( |
148 | + 'com.ubuntu.sso', '/com/ubuntu/sso/credentials') |
149 | + return dbus.Interface( |
150 | + proxy, dbus_interface='com.ubuntu.sso.CredentialsManagement') |
151 | + |
152 | + |
153 | +class UbuntuSSODesktopClient(object): |
154 | + |
155 | + def __init__(self, sso_server_url, ubuntuone_server_url): |
156 | + self.sso_server_url = sso_server_url |
157 | + self.ubuntuone_server_url = ubuntuone_server_url |
158 | + |
159 | + def launch_application(self): |
160 | + self._prepare_environment() |
161 | + process = subprocess.Popen( |
162 | + '/usr/lib/ubuntu-sso-client/ubuntu-sso-login-qt') |
163 | + self.application = ( |
164 | + introspection.get_autopilot_proxy_object_for_process(process)) |
165 | + |
166 | + def _prepare_environment(self): |
167 | + os.environ['TESTABILITY'] = '1' |
168 | + os.environ['SSO_AUTH_BASE_URL'] = self.sso_server_url |
169 | + os.environ['SSO_UONE_BASE_URL'] = self.ubuntuone_server_url |
170 | + _start_ubuntu_sso_service_on_desktop(self.sso_server_url) |
171 | + |
172 | + def log_in(self, user): |
173 | + self.launch_application() |
174 | + log_in_page = self._go_to_log_in_page() |
175 | + success_page = log_in_page.log_in_with_successful_authentication(user) |
176 | + assert (success_page.get_message() == |
177 | + 'You are now logged into Ubuntu One.') |
178 | + success_page.finish() |
179 | + |
180 | + def _go_to_log_in_page(self): |
181 | + sign_up_page = SignUpPage(self.application) |
182 | + sign_up_page.wait_for_action_to_complete() |
183 | + return sign_up_page.go_to_log_in() |
184 | + |
185 | + |
186 | +class Page(object): |
187 | + |
188 | + def __init__(self, application, page_type): |
189 | + self.application = application |
190 | + self.page = self.application.select_single(page_type) |
191 | + self.keyboard = input.get_keyboard() |
192 | + self.mouse = input.get_mouse() |
193 | + |
194 | + def wait_for_action_to_complete(self): |
195 | + """Wait for the last action to complete. |
196 | + |
197 | + This method waits for the loading overlay to dissapear. |
198 | + |
199 | + """ |
200 | + # If autopilot executes too fast, we might look for the loading overlay |
201 | + # before it is shown. We should wait a little to give it time to |
202 | + # appear, but it's not possible to wait for it, because if autopilot |
203 | + # executes too slow, we might look for it after it dissapears. |
204 | + # Thus, the only solution that comes to mind is a sleep. |
205 | + # -- elopio - 2013-05-03 |
206 | + time.sleep(3) |
207 | + loading_overlay = self.application.select_single('LoadingOverlay') |
208 | + loading_overlay.visible.wait_for(False) |
209 | + |
210 | + # TODO propose this to autopilot. -- elopio - 2013-05-03 |
211 | + def get_child_by_type(self, desired_type, **kwargs): |
212 | + children = self.page.get_children_by_type(desired_type, **kwargs) |
213 | + assert len(children) > 0, 'No child found.' |
214 | + assert len(children) == 1, 'More than one child found.' |
215 | + return children[0] |
216 | + |
217 | + |
218 | +class SignUpPage(Page): |
219 | + |
220 | + def __init__(self, application): |
221 | + super(SignUpPage, self).__init__(application, 'SetupAccountPage') |
222 | + |
223 | + def go_to_log_in(self): |
224 | + self.wait_for_action_to_complete() |
225 | + sign_in_label = self.get_child_by_type( |
226 | + 'QLabel', objectName='sign_in_label') |
227 | + self.mouse.move_to_object(sign_in_label) |
228 | + self.mouse.click() |
229 | + self.wait_for_action_to_complete() |
230 | + return LogInPage(self.application) |
231 | + |
232 | + |
233 | +class LogInPage(Page): |
234 | + |
235 | + def __init__(self, application): |
236 | + super(LogInPage, self).__init__(application, 'CurrentUserSignInPage') |
237 | + |
238 | + def log_in_with_successful_authentication(self, user): |
239 | + self._log_in(user.email, user.password) |
240 | + assert self.get_form_errors() is None |
241 | + return SuccessPage(self.application) |
242 | + |
243 | + def _log_in(self, email, password): |
244 | + self._fill_log_in_form(email, password) |
245 | + sign_in_button = self.get_child_by_type( |
246 | + 'QPushButton', objectName='sign_in_button') |
247 | + self.mouse.move_to_object(sign_in_button) |
248 | + self.mouse.click() |
249 | + self.wait_for_action_to_complete() |
250 | + |
251 | + def _fill_log_in_form(self, email, password): |
252 | + email_edit = self.get_child_by_type( |
253 | + 'QLineEdit', objectName='email_edit') |
254 | + self.mouse.move_to_object(email_edit) |
255 | + self.mouse.click() |
256 | + self.keyboard.type(email) |
257 | + password_edit = self.get_child_by_type( |
258 | + 'QLineEdit', objectName='password_edit') |
259 | + self.mouse.move_to_object(password_edit) |
260 | + self.mouse.click() |
261 | + self.keyboard.type(password) |
262 | + |
263 | + def get_form_errors(self): |
264 | + form_errors = self.get_child_by_type( |
265 | + 'QLabel', objectName='form_errors') |
266 | + if form_errors.visible: |
267 | + return form_errors.text |
268 | + else: |
269 | + return None |
270 | + |
271 | + |
272 | +class SuccessPage(Page): |
273 | + |
274 | + def __init__(self, application): |
275 | + super(SuccessPage, self).__init__(application, 'SuccessPage') |
276 | + |
277 | + def get_message(self): |
278 | + label = self.get_child_by_type( |
279 | + 'QLabel', objectName='success_message_body') |
280 | + return label.text |
281 | + |
282 | + def finish(self): |
283 | + # TODO when we have the emulator, this should be on the wizard, not the |
284 | + # page. -- elopio - 2013-05-03 |
285 | + # Bug reported to QWizard because the buttons don't have objectName: |
286 | + # https://bugreports.qt-project.org/browse/QTBUG-29924 |
287 | + # TODO update when the bug is fixed. -- elopio - 2013-05-03 |
288 | + finish_button = self.application.select_single( |
289 | + 'QPushButton', text='&Finish') |
290 | + self.mouse.move_to_object(finish_button) |
291 | + self.mouse.click() |
292 | |
293 | === modified file 'tests/test_purchase_good.py' |
294 | --- tests/test_purchase_good.py 2013-04-25 08:18:50 +0000 |
295 | +++ tests/test_purchase_good.py 2013-05-15 21:02:26 +0000 |
296 | @@ -1,7 +1,9 @@ |
297 | -import unity.tests as utests |
298 | - |
299 | from sst import actions as sactions |
300 | |
301 | +# FIXME do not use django config to get the server settings. |
302 | +# -- elopio - 2013-05-15 |
303 | +from django import conf |
304 | + |
305 | import tests |
306 | from tests import ( |
307 | browsers, |
308 | @@ -19,11 +21,14 @@ |
309 | |
310 | class TestPurchaseGood(tests.UnitySSTestcase): |
311 | |
312 | - |
313 | def setUp(self): |
314 | super(TestPurchaseGood, self).setUp() |
315 | # A user will purchase a good |
316 | self.user = users.User.make_unique() |
317 | + # Set the urls of the servers that will be used by the user. |
318 | + self.user.set_urls( |
319 | + sso_server_url=conf.settings.OPENID_SSO_SERVER_URL, |
320 | + ubuntuone_server_url=conf.settings.UBUNTUONE_SERVER_URL) |
321 | # Arbitrarily using a good that is known to work |
322 | self.good = goods.Good(u'Jimi Hendrix', 'hendrix') |
323 | |
324 | @@ -43,8 +48,7 @@ |
325 | scenarios = [(name, {'lens_name': name}) for name in ('home', 'music',)] |
326 | |
327 | def test_user_not_logged_in(self): |
328 | - # User not logged in |
329 | - self.user.logout() |
330 | + self.assertFalse(self.user.is_logged_in()) |
331 | self.preview_good() |
332 | if self.lens_name == 'music': |
333 | # There is a workaround in place for the music lens only that |
334 | @@ -59,13 +63,13 @@ |
335 | browser = browsers.Browser(self) |
336 | # We've been redirected to the web |
337 | browser.switch_to_new_window(self) |
338 | - sactions.wait_for(sactions.assert_url_contains, |
339 | - 'https://login.ubuntu.com') |
340 | + sactions.wait_for( |
341 | + sactions.assert_url_contains, conf.settings.OPENID_SSO_SERVER_URL) |
342 | self.assertEqual('Log in', browser.title) |
343 | |
344 | def test_no_payment_method_stored(self): |
345 | - # User has no payment method |
346 | - self.user.login() |
347 | + # Log in with a newly created user, and set no payment method to him. |
348 | + self.user.login(self) |
349 | self.user.payment_method = None |
350 | self.preview_good() |
351 | goto_u1 = self.purchase_good() |
352 | @@ -78,8 +82,8 @@ |
353 | self.assertEqual('https://pay.ubuntu.com', browser.current_url) |
354 | |
355 | def test_pay_method_expired(self): |
356 | - # Uesrs's payment has expired |
357 | - self.user.login() |
358 | + # Users's payment has expired |
359 | + self.user.login(self) |
360 | self.user.payment_method = 'expired' |
361 | self.preview_good() |
362 | goto_u1 = self.purchase_good() |
363 | @@ -90,5 +94,3 @@ |
364 | # User is sent to the web |
365 | self.assertTrue(browser.is_running()) |
366 | self.assertEqual('https://pay.ubuntu.com', browser.current_url) |
367 | - |
368 | - |
369 | |
370 | === added file 'tests/test_users.py' |
371 | --- tests/test_users.py 1970-01-01 00:00:00 +0000 |
372 | +++ tests/test_users.py 2013-05-15 21:02:26 +0000 |
373 | @@ -0,0 +1,31 @@ |
374 | +# FIXME do not use django config to get the server settings. |
375 | +# -- elopio - 2013-05-15 |
376 | +from django import conf |
377 | + |
378 | +import tests |
379 | +from tests import users |
380 | + |
381 | + |
382 | +class UsersTest(tests.TestCase): |
383 | + |
384 | + def test_make_unique(self): |
385 | + user = users.User.make_unique(unique_id='test_uuid') |
386 | + user.set_urls(sso_server_url=conf.settings.OPENID_SSO_SERVER_URL) |
387 | + |
388 | + self.assertEqual(user.user_data.full_name, 'Test user test_uuid') |
389 | + self.assertEqual( |
390 | + user.user_data.email, 'test+test_uuid@example.com') |
391 | + self.assertEqual( |
392 | + user.user_data.password, 'Hola123*') |
393 | + self.assertFalse(user.is_logged_in()) |
394 | + self.assertEqual(user.payment_method, None) |
395 | + |
396 | + def test_log_in_and_out(self): |
397 | + user = users.User.make_unique() |
398 | + user.set_urls( |
399 | + sso_server_url=conf.settings.OPENID_SSO_SERVER_URL, |
400 | + ubuntuone_server_url=conf.settings.UBUNTUONE_SERVER_URL) |
401 | + user.login(self) |
402 | + self.assertTrue(user.is_logged_in()) |
403 | + user.logout() |
404 | + self.assertFalse(user.is_logged_in()) |
405 | |
406 | === modified file 'tests/users.py' |
407 | --- tests/users.py 2012-12-12 15:05:25 +0000 |
408 | +++ tests/users.py 2013-05-15 21:02:26 +0000 |
409 | @@ -1,16 +1,39 @@ |
410 | +from u1testutils.sso import data |
411 | + |
412 | +from tests import sso |
413 | + |
414 | + |
415 | class User(object): |
416 | |
417 | - def __init__(self): |
418 | - self.logged_in = None |
419 | + def __init__(self, user_data): |
420 | + self.user_data = user_data |
421 | self.payment_method = None |
422 | |
423 | + def set_urls(self, sso_server_url=None, ubuntuone_server_url=None): |
424 | + self.sso_server_url = sso_server_url |
425 | + self.ubuntuone_server_url = ubuntuone_server_url |
426 | + |
427 | @classmethod |
428 | - def make_unique(kls): |
429 | - user = User() |
430 | - return user |
431 | - |
432 | - def login(self): |
433 | - self.logged_in = True |
434 | + def make_unique(cls, unique_id=None): |
435 | + user_data = data.User.make_unique(unique_id) |
436 | + # If the user has a name longer than 30 characters, the log in will |
437 | + # fail. TODO is this a bug? -- elopio - 2013-05-07 |
438 | + user_data.full_name = user_data.full_name[:30] |
439 | + return cls(user_data) |
440 | + |
441 | + def is_logged_in(self): |
442 | + return sso.is_logged_in_on_desktop(self.sso_server_url) |
443 | + |
444 | + def login(self, test): |
445 | + # Currently we are using a local server, so we don't need to delete |
446 | + # the user after the test. The same applies to the staging server. |
447 | + # When we start testing against the production server, we should either |
448 | + # use a stock user, or delete it after the test. -- elopio - 2013-05-15 |
449 | + sso.create_new_account_with_server_api( |
450 | + self.user_data, self.sso_server_url) |
451 | + test.addCleanup(self.logout) |
452 | + sso.log_in_with_desktop_client( |
453 | + self.user_data, self.sso_server_url, self.ubuntuone_server_url) |
454 | |
455 | def logout(self): |
456 | - self.logged_in = False |
457 | + sso.log_out_with_dbus_api(self.sso_server_url) |
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:/