source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py @ 6701

Last change on this file since 6701 was 6632, checked in by Henrik Bettermann, 13 years ago

Rename applicants.py, accesscodes.py and students.py modules. Their names should be singular like in the university package.

File size: 40.3 KB
Line 
1##
2## test_browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Tue Mar 29 11:31:11 2011 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""
23Test the applicant-related UI components.
24"""
25import shutil
26import tempfile
27from StringIO import StringIO
28from datetime import datetime
29from mechanize import LinkNotFoundError
30from zope.component import createObject
31from zope.component.hooks import setSite, clearSite
32from zope.security.interfaces import Unauthorized
33from zope.testbrowser.testing import Browser
34from hurry.workflow.interfaces import IWorkflowInfo
35from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
36from waeup.sirp.app import University
37from waeup.sirp.applicants.container import ApplicantsContainer
38from waeup.sirp.applicants.applicant import Applicant
39from waeup.sirp.university.faculty import Faculty
40from waeup.sirp.university.department import Department
41
42PH_LEN = 2059  # Length of placeholder file
43
44class ApplicantsFullSetup(FunctionalTestCase):
45    # A test case that only contains a setup and teardown
46    #
47    # Complete setup for applicants handlings is rather complex and
48    # requires lots of things created before we can start. This is a
49    # setup that does all this, creates a university, creates PINs,
50    # etc.  so that we do not have to bother with that in different
51    # test cases.
52
53    layer = FunctionalLayer
54
55    def setUp(self):
56        super(ApplicantsFullSetup, self).setUp()
57
58        # Setup a sample site for each test
59        app = University()
60        self.dc_root = tempfile.mkdtemp()
61        app['datacenter'].setStoragePath(self.dc_root)
62
63        # Prepopulate the ZODB...
64        self.getRootFolder()['app'] = app
65        # we add the site immediately after creation to the
66        # ZODB. Catalogs and other local utilities are not setup
67        # before that step.
68        self.app = self.getRootFolder()['app']
69        # Set site here. Some of the following setup code might need
70        # to access grok.getSite() and should get our new app then
71        setSite(app)
72
73        self.root_path = 'http://localhost/app/applicants'
74        self.manage_root_path = self.root_path + '/@@manage'
75        self.add_container_path = self.root_path + '/@@add'
76        self.container_path = 'http://localhost/app/applicants/app2009'
77        self.manage_container_path = self.container_path + '/@@manage'
78
79        # Add an applicants container
80        applicantscontainer = ApplicantsContainer()
81        applicantscontainer.ac_prefix = 'APP'
82        applicantscontainer.prefix = 'app'
83        applicantscontainer.year = 2009
84        applicantscontainer.application_category = 'basic'
85        self.app['applicants']['app2009'] = applicantscontainer
86
87        # Populate university
88        certificate = createObject('waeup.Certificate')
89        certificate.code = 'CERT1'
90        certificate.application_category = 'basic'
91        self.app['faculties']['fac1'] = Faculty()
92        self.app['faculties']['fac1']['dep1'] = Department()
93        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
94            certificate)
95
96        # Put the prepopulated site into test ZODB and prepare test
97        # browser
98        self.browser = Browser()
99        self.browser.handleErrors = False
100
101        # Create 5 access codes with prefix'FOO' and cost 9.99 each
102        pin_container = self.app['accesscodes']
103        pin_container.createBatch(
104            datetime.now(), 'some_userid', 'APP', 9.99, 5)
105        pins = pin_container[pin_container.keys()[0]].values()
106        self.pins = [x.representation for x in pins]
107        self.existing_pin = self.pins[0]
108        parts = self.existing_pin.split('-')[1:]
109        self.existing_series, self.existing_number = parts
110
111        # Add an applicant
112        self.applicant = Applicant()
113        self.pin_applicant = unicode(self.pins[1])
114        self.applicant.access_code = self.pin_applicant
115        app['applicants']['app2009'][self.pin_applicant] = self.applicant
116
117    def tearDown(self):
118        super(ApplicantsFullSetup, self).tearDown()
119        clearSite()
120        shutil.rmtree(self.dc_root)
121
122class ApplicantsRootUITests(ApplicantsFullSetup):
123    # Tests for ApplicantsRoot class
124
125    layer = FunctionalLayer
126
127    def test_anonymous_access(self):
128        # Anonymous users can access applicants root
129        self.browser.open(self.root_path)
130        self.assertEqual(self.browser.headers['Status'], '200 Ok')
131        self.assertFalse(
132            'Manage' in self.browser.contents)
133
134    def test_anonymous_no_actions(self):
135        # Make sure anonymous users cannot access actions
136        self.browser.open(self.root_path)
137        self.assertRaises(
138            LookupError, self.browser.getControl, "Add local role")
139        # Manage screen neither linked nor accessible for anonymous
140        self.assertRaises(
141            LinkNotFoundError,
142            self.browser.getLink, 'Manage application section')
143        self.assertRaises(
144            Unauthorized, self.browser.open, self.manage_root_path)
145        # Add container screen not accessible for anonymous
146        self.assertRaises(
147            Unauthorized, self.browser.open, self.add_container_path)
148        return
149
150    def test_manage_access(self):
151        # Managers can access the manage pages of applicants root
152        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
153        self.browser.open(self.root_path)
154        self.assertTrue('Manage application section' in self.browser.contents)
155        # There is a manage link
156        link = self.browser.getLink('Manage application section')
157        link.click()
158        self.assertEqual(self.browser.headers['Status'], '200 Ok')
159        self.assertEqual(self.browser.url, self.manage_root_path)
160
161    def test_manage_actions_access(self):
162        # Managers can access the action on manage screen
163        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
164        self.browser.open(self.manage_root_path)
165        self.browser.getControl("Add local role").click()
166        self.assertTrue('No user selected' in self.browser.contents)
167        return
168
169    def test_local_roles_add_delete(self):
170        # Managers can assign and delete local roles of applicants root
171        myusers = self.app['users']
172        myusers.addUser('bob', 'bobssecret')
173        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
174        self.browser.open(self.manage_root_path)
175        self.browser.getControl(name="user").value = ['bob']
176        self.browser.getControl(name="local_role").value = [
177            'waeup.ApplicationsOfficer']
178        self.browser.getControl("Add local role").click()
179        self.assertTrue('<td>bob</td>' in self.browser.contents)
180        # Remove the role assigned
181        ctrl = self.browser.getControl(name='role_id')
182        ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
183        self.browser.getControl("Remove selected local roles").click()
184        self.assertTrue('Successfully removed:' in self.browser.contents)
185        self.assertFalse('<td>bob</td>' in self.browser.contents)
186        return
187
188    def test_add_delete_container(self):
189        # Managers can add and delete applicants containers
190        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
191        self.browser.open(self.manage_root_path)
192        self.browser.getControl("Cancel").click()
193        self.assertEqual(self.browser.url, self.root_path)
194        self.browser.open(self.manage_root_path)
195        self.browser.getControl("Add applicants container").click()
196        self.assertEqual(self.browser.headers['Status'], '200 Ok')
197        self.assertEqual(self.browser.url, self.add_container_path)
198        self.browser.getControl(name="form.prefix").value = ['app']
199        self.browser.getControl("Add applicants container").click()
200        self.assertTrue(
201            'There were errors' in self.browser.contents)
202        self.browser.getControl(name="form.prefix").value = ['app']
203        self.browser.getControl(name="form.year").value = ['2010']
204        self.browser.getControl(name="form.provider").value = [
205            'waeup.sirp.applicants.ApplicantsContainer']
206        self.browser.getControl(name="form.ac_prefix").value = ['APP']
207        self.browser.getControl(name="form.application_category").value = ['basic']
208        self.browser.getControl("Add applicants container").click()
209        self.assertTrue('Added:' in self.browser.contents)
210        self.browser.open(self.add_container_path)
211        self.browser.getControl("Cancel").click()
212        self.assertEqual(self.browser.url, self.manage_root_path + '#tab-1')
213        self.browser.open(self.add_container_path)
214        self.browser.getControl(name="form.prefix").value = ['app']
215        self.browser.getControl(name="form.year").value = ['2010']
216        self.browser.getControl(name="form.provider").value = [
217            'waeup.sirp.applicants.ApplicantsContainer']
218        self.browser.getControl(name="form.ac_prefix").value = ['APP']
219        self.browser.getControl(name="form.application_category").value = ['basic']
220        self.browser.getControl("Add applicants container").click()
221        self.assertTrue('exists already in the database' in self.browser.contents)
222        self.browser.open(self.manage_root_path)
223        ctrl = self.browser.getControl(name='val_id')
224        ctrl.getControl(value='app2010').selected = True
225        self.browser.getControl("Remove selected", index=0).click()
226        self.assertTrue('Successfully removed:' in self.browser.contents)
227        self.browser.open(self.add_container_path)
228        self.browser.getControl(name="form.prefix").value = ['app']
229        self.browser.getControl(name="form.year").value = ['2010']
230        self.browser.getControl(name="form.provider").value = [
231            'waeup.sirp.applicants.ApplicantsContainer']
232        self.browser.getControl(name="form.ac_prefix").value = ['APP']
233        self.browser.getControl(name="form.application_category").value = ['basic']
234        self.browser.getControl("Add applicants container").click()
235        del self.app['applicants']['app2010']
236        ctrl = self.browser.getControl(name='val_id')
237        ctrl.getControl(value='app2010').selected = True
238        self.browser.getControl("Remove selected", index=0).click()
239        self.assertMatches('...Could not delete...', self.browser.contents)
240        return
241
242    def test_add_delete_applicants(self):
243        # Managers can add and delete applicants
244        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
245        self.add_applicant_path = self.container_path + '/addapplicant'
246        self.container_manage_path = self.container_path + '/@@manage'
247        self.browser.open(self.container_manage_path)
248        self.browser.getControl("Add applicant").click()
249        self.assertEqual(self.browser.headers['Status'], '200 Ok')
250        self.assertEqual(self.browser.url, self.add_applicant_path)
251        self.browser.getControl(name="form.ac_series").value = self.existing_series
252        self.browser.getControl(name="form.ac_number").value = self.existing_number
253        self.browser.getControl("Create application record").click()
254        self.assertTrue('Application initialized' in self.browser.contents)
255        self.browser.open(self.add_applicant_path)
256        self.browser.getControl(name="form.ac_series").value = '123'
257        self.browser.getControl(name="form.ac_number").value = '456'
258        self.browser.getControl("Create application record").click()
259        self.assertTrue('is not a valid access code' in self.browser.contents)
260        self.browser.open(self.container_manage_path)
261        self.assertEqual(self.browser.headers['Status'], '200 Ok')
262        ctrl = self.browser.getControl(name='val_id')
263        ctrl.getControl(value=self.existing_pin).selected = True
264        self.browser.getControl("Remove selected", index=0).click()
265        self.assertTrue('Successfully removed:' in self.browser.contents)
266        self.browser.open(self.add_applicant_path)
267        existing_pin = self.pins[2]
268        parts = existing_pin.split('-')[1:]
269        existing_series, existing_number = parts
270        self.browser.getControl(name="form.ac_series").value = existing_series
271        self.browser.getControl(name="form.ac_number").value = existing_number
272        self.browser.getControl("Create application record").click()
273        self.assertTrue('Application initialized' in self.browser.contents)
274        self.browser.open(self.container_manage_path)
275        self.assertTrue(existing_pin in self.browser.contents)
276        del self.app['applicants']['app2009'][existing_pin]
277        ctrl = self.browser.getControl(name='val_id')
278        ctrl.getControl(value=existing_pin).selected = True
279        self.browser.getControl("Remove selected", index=0).click()
280        self.assertMatches('...Could not delete...', self.browser.contents)
281        return
282
283    def test_manage_and_view_applicant(self):
284        # Managers can manage applicants
285        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
286        self.applicant_path = 'http://localhost/app/applicants/app2009/%s' % self.pin_applicant
287        self.applicant_view_path = self.applicant_path + '/index'
288        self.applicant_manage_path = self.applicant_path + '/edit_full'
289        self.applicant_slip_path = self.applicant_path + '/application_slip.pdf'
290        self.browser.open(self.applicant_manage_path)
291        self.assertEqual(self.browser.headers['Status'], '200 Ok')
292        self.browser.getControl(name="form.email").value = 'abc'
293        self.browser.getControl("Save").click()
294        self.assertMatches('...Invalid email address...', self.browser.contents)
295        self.browser.getControl(name="form.email").value = 'abc@def.gh'
296        self.browser.getControl(name="form.firstname").value = 'John'
297        self.browser.getControl(name="form.lastname").value = 'Tester'
298        self.browser.getControl(name="form.course1").value = ['CERT1']
299        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
300        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
301        self.browser.getControl(name="form.lga").value = ['foreigner']
302        self.browser.getControl(name="form.sex").value = ['m']
303        self.browser.getControl(name="transition").value = ['start']
304        self.browser.getControl("Save").click()
305        self.assertMatches('...Form has been saved...', self.browser.contents)
306        self.assertMatches('...Application started by zope.mgr...', self.browser.contents)
307        self.browser.open(self.applicant_view_path)
308        self.assertEqual(self.browser.headers['Status'], '200 Ok')
309        self.browser.open(self.applicant_slip_path)
310        self.assertEqual(self.browser.headers['Status'], '200 Ok')
311        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
312        self.browser.open(self.applicant_manage_path)
313        self.browser.getControl(name="form.course_admitted").value = []
314        self.browser.getControl("Save").click()
315        self.browser.open(self.applicant_slip_path)
316        self.assertEqual(self.browser.headers['Status'], '200 Ok')
317        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
318        return
319
320    def test_view_applicant(self):
321        # Applicants can login and view their application
322        self.login_path = 'http://localhost/app/applicants/app2009/login'
323        self.browser.open(self.login_path)
324        pin = self.pins[2]
325        parts = pin.split('-')[1:]
326        existing_series, existing_number = parts
327        ac_series = self.browser.getControl(name="form.ac_series")
328        ac_series.value = existing_series
329        ac_number = self.browser.getControl(name="form.ac_number")
330        ac_number.value = existing_number
331        self.browser.getControl(name="SUBMIT").click()
332        self.assertTrue(self.browser.url != self.login_path)
333        self.assertEqual(self.browser.headers['Status'], '200 Ok')
334        return
335
336    def test_passport_edit_view(self):
337        # We get a default image after login
338        login_path = 'http://localhost/app/applicants/app2009/login'
339        self.browser.open(login_path)
340        pin = self.pins[2]
341        parts = pin.split('-')[1:]
342        existing_series, existing_number = parts
343        ac_series = self.browser.getControl(name="form.ac_series")
344        ac_series.value = existing_series
345        ac_number = self.browser.getControl(name="form.ac_number")
346        ac_number.value = existing_number
347        self.browser.getControl(name="SUBMIT").click()
348        pin = self.pins[2]
349        appl = self.getRootFolder()['app']['applicants']['app2009']
350        appl = appl[pin]
351        passp = appl.passport
352        #import pdb; pdb.set_trace()
353        passp_len = len(passp.file.read())
354        self.assertEqual(passp_len, PH_LEN)
355        #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg')
356        image_url = "%s/%s" % (self.browser.url, 'passport.jpg')
357        #self.browser.open(image_url)
358        self.browser.open('passport.jpg')
359        self.assertEqual(self.browser.headers['status'], '200 Ok')
360        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
361        self.assertTrue('JFIF' in self.browser.contents)
362        self.assertEqual(
363            self.browser.headers['content-length'], str(PH_LEN))
364
365
366    def test_edit_applicant(self):
367        # Applicants can edit their record
368        self.login_path = 'http://localhost/app/applicants/app2009/login'
369        self.browser.open(self.login_path)
370        pin = self.pins[2]
371        parts = pin.split('-')[1:]
372        existing_series, existing_number = parts
373        ac_series = self.browser.getControl(name="form.ac_series")
374        ac_series.value = existing_series
375        ac_number = self.browser.getControl(name="form.ac_number")
376        ac_number.value = existing_number
377        self.browser.getControl(name="SUBMIT").click()
378        self.assertTrue(self.browser.url != self.login_path)
379        self.assertEqual(self.browser.headers['Status'], '200 Ok')
380        self.browser.getControl(name="form.firstname").value = 'John'
381        self.browser.getControl(name="form.lastname").value = 'Tester'
382        self.browser.getControl(name="form.course1").value = ['CERT1']
383        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
384        self.browser.getControl(name="form.lga").value = ['foreigner']
385        self.browser.getControl(name="form.sex").value = ['m']
386        self.browser.getControl("Save").click()
387        self.assertMatches('...Form has been saved...', self.browser.contents)
388        return
389
390class ApplicantsContainerUITests(ApplicantsFullSetup):
391    # Tests for ApplicantsContainer class views and pages
392
393    layer = FunctionalLayer
394
395    def test_anonymous_access(self):
396        # Anonymous users can access applicants containers
397        self.browser.open(self.container_path)
398        self.assertEqual(self.browser.headers['Status'], '200 Ok')
399        self.assertFalse(
400            'Manage' in self.browser.contents)
401        return
402
403    def test_manage_access(self):
404        # Managers can access the manage pages of applicants
405        # containers and can perform actions
406        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
407        self.browser.open(self.manage_container_path)
408        self.assertEqual(self.browser.headers['Status'], '200 Ok')
409        self.assertEqual(self.browser.url, self.manage_container_path)
410        self.browser.getControl("Save").click()
411        self.assertTrue('Data saved' in self.browser.contents)
412        self.browser.getControl("Remove selected", index=0).click()
413        self.assertTrue('No applicant selected' in self.browser.contents)
414        self.browser.getControl("Add local role").click()
415        self.assertTrue('No user selected' in self.browser.contents)
416        self.browser.getControl("Cancel", index=0).click()
417        self.assertEqual(self.browser.url, self.container_path)
418        return
419
420    def test_local_roles_add_delete(self):
421        # Managers can assign and delete local roles of applicants containers
422        myusers = self.app['users']
423        myusers.addUser('bob', 'bobssecret')
424        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
425        self.browser.open(self.manage_container_path)
426        self.browser.getControl(name="user").value = ['bob']
427        self.browser.getControl(name="local_role").value = [
428            'waeup.ApplicationsOfficer']
429        self.browser.getControl("Add local role").click()
430        self.assertTrue('<td>bob</td>' in self.browser.contents)
431        ctrl = self.browser.getControl(name='role_id')
432        ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
433        self.browser.getControl("Remove selected local roles").click()
434        self.assertTrue('Successfully removed:' in self.browser.contents)
435        self.assertFalse('<td>bob</td>' in self.browser.contents)
436        return
437
438class LoginTest(FunctionalTestCase):
439    # Here we check login view of applicants containers.
440    #
441    # Tests in here do only cover login attempts without any PINs
442    # created before.
443
444    layer = FunctionalLayer
445
446    def setUp(self):
447        super(LoginTest, self).setUp()
448
449        # Setup a sample site for each test
450        app = University()
451        self.dc_root = tempfile.mkdtemp()
452        app['datacenter'].setStoragePath(self.dc_root)
453        self.login_path = 'http://localhost/app/applicants/testapplicants/login'
454
455        # Add an applicants container where we can login (or not)
456        applicantscontainer = ApplicantsContainer()
457        applicantscontainer.ac_prefix = 'APP'
458        app['applicants']['testapplicants'] = applicantscontainer
459
460        # Put the prepopulated site into test ZODB and prepare test
461        # browser
462        self.getRootFolder()['app'] = app
463        self.browser = Browser()
464        self.browser.handleErrors = False
465
466    def tearDown(self):
467        super(LoginTest, self).tearDown()
468        shutil.rmtree(self.dc_root)
469
470    def test_anonymous_access(self):
471        # Anonymous users can access a login page
472        self.browser.open(self.login_path)
473        self.assertEqual(self.browser.headers['Status'], '200 Ok')
474        return
475
476    def test_anonymous_invalid_creds(self):
477        # Anonymous users giving invalid credentials stay at the page
478        self.browser.open(self.login_path)
479        # We do not give credentials but send the form as-is
480        submit = self.browser.getControl(name='SUBMIT')
481        submit.click()
482        # We are still at the same page...
483        self.assertEqual(self.browser.url, self.login_path)
484        self.assertEqual(self.browser.headers['Status'], '200 Ok')
485        return
486
487    def test_anonymous_invalid_creds_warning(self):
488        # Entering wrong credentials will yield a warning
489        self.browser.open(self.login_path)
490        # We do not give credentials but send the form as-is
491        submit = self.browser.getControl(name='SUBMIT')
492        submit.click()
493        self.assertTrue(
494            'Entered credentials are invalid' in self.browser.contents)
495        return
496
497    def test_manager_no_warnings(self):
498        # Browsing the login screen as a manager, won't raise warnings
499        # Authenticate ourself as manager
500        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
501        self.browser.open(self.login_path)
502        # Submit the form w/o any credentials
503        self.browser.getControl(name="SUBMIT").click()
504        self.assertTrue(
505            'Entered credentials are invalid' not in self.browser.contents)
506        return
507
508    def test_manager_no_redirect(self):
509        # Browsing the login screen as a manager won't trigger a redirect
510        # Authenticate ourself as manager
511        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
512        self.browser.open(self.login_path)
513        # Submit the form w/o any credentials
514        self.browser.getControl(name="SUBMIT").click()
515        self.assertEqual(self.browser.url, self.login_path)
516        return
517
518    def test_display_entered_values(self):
519        # After submit the entered values are displayed in the form
520        self.browser.open(self.login_path)
521        # Enter some value we can look for after submit
522        ac_series = self.browser.getControl(name="form.ac_series")
523        ac_series.value = '666'
524        self.browser.getControl(name="SUBMIT").click()
525        self.assertTrue('666' in self.browser.contents)
526        return
527
528class LoginTestWithPINs(LoginTest):
529    # Here we check login view of applicants containers with PINs provided.
530
531    # As setting up pins is time-consuming we only set them up when
532    # really needed (i.e. in this separate TestCase).
533
534    layer = FunctionalLayer
535
536    def setUp(self):
537        super(LoginTestWithPINs, self).setUp()
538
539        # Create 5 access codes with prefix'FOO' and cost 9.99 each
540        pin_container = self.getRootFolder()['app']['accesscodes']
541        pin_container.createBatch(
542            datetime.now(), 'some_userid', 'APP', 9.99, 5)
543        pins = pin_container[pin_container.keys()[0]].values()
544        self.pins = [x.representation for x in pins]
545        self.existing_pin = self.pins[0]
546        parts = self.existing_pin.split('-')[1:]
547        self.existing_series, self.existing_number = parts
548        self.browser.handleErrors = False
549
550    def tearDown(self):
551        super(LoginTestWithPINs, self).tearDown()
552
553    def test_anonymous_valid_login(self):
554        # If we enter valid credentials, we get to the applicants form
555        self.browser.open(self.login_path)
556        # Enter some value we can look for after submit
557        ac_series = self.browser.getControl(name="form.ac_series")
558        ac_series.value = self.existing_series
559        ac_number = self.browser.getControl(name="form.ac_number")
560        ac_number.value = self.existing_number
561        self.browser.getControl(name="SUBMIT").click()
562        # We should be redirected to applicants form.
563        self.assertTrue(self.browser.url != self.login_path)
564        # Applicants see their Access Code in the contact form
565        self.browser.getLink("Contact").click()
566        self.assertTrue(
567            'Access Code:' in self.browser.contents)
568        return
569
570    def test_anonymous_invalid_login(self):
571        # If we enter wrong credentials we won't get far
572        self.browser.open(self.login_path)
573        # Enter some value we can look for after submit
574        ac_series = self.browser.getControl(name="form.ac_series")
575        ac_series.value = 'illegal series'
576        ac_number = self.browser.getControl(name="form.ac_number")
577        ac_number.value = 'invalid number'
578        self.browser.getControl(name="SUBMIT").click()
579        # We get a warning message
580        self.assertTrue(
581            'Entered credentials are invalid' in self.browser.contents)
582        # We stay at the login page (no redirect)
583        self.assertTrue(self.browser.url == self.login_path)
584        return
585
586class ApplicantsPassportTests(ApplicantsFullSetup):
587    # Tests for uploading/browsing the passport image of appplicants
588
589    layer = FunctionalLayer
590
591    def setUp(self):
592        super(ApplicantsPassportTests, self).setUp()
593        self.login_path = 'http://localhost/app/applicants/app2009/login'
594        self.pin = self.pins[2]
595        self.existing_series, self.existing_number = self.pin.split('-')[1:]
596        self.edit_path = 'http://localhost/app/applicants/app2009/%s/edit' % (
597            self.pin)
598        self.edit_full_path = 'http://localhost/app/applicants/%s/%s/%s' % (
599            'app2009', self.pin, 'edit_full')
600
601    def tearDown(self):
602        super(ApplicantsPassportTests, self).tearDown()
603
604    def login(self):
605        # Perform an applicant login. This creates an applicant record.
606        #
607        # This helper also sets `self.applicant`, which is the
608        # applicant object created.
609        self.browser.open(self.login_path)
610        ac_series = self.browser.getControl(name="form.ac_series")
611        ac_series.value = self.existing_series
612        ac_number = self.browser.getControl(name="form.ac_number")
613        ac_number.value = self.existing_number
614        self.browser.getControl(name="SUBMIT").click()
615        self.applicant = self.app['applicants']['app2009'][self.pin]
616
617    def fill_correct_values(self):
618        # Fill the edit form with suitable values
619        self.browser.getControl(name="form.firstname").value = 'John'
620        self.browser.getControl(name="form.lastname").value = 'Tester'
621        self.browser.getControl(name="form.course1").value = ['CERT1']
622        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
623        self.browser.getControl(name="form.lga").value = ['foreigner']
624        self.browser.getControl(name="form.sex").value = ['m']
625
626    def image_url(self, filename):
627        return self.edit_path.replace('edit', filename)
628
629    def test_after_login_default_browsable(self):
630        # After login we see the placeholder image in the edit view
631        self.login()
632        self.assertEqual(self.browser.url, self.edit_path)
633        # There is a correct <img> link included
634        self.assertTrue(
635            '<img src="placeholder_m.jpg" />' in self.browser.contents)
636        # Browsing the link shows a real image
637        self.browser.open(self.image_url('placeholder_m.jpg'))
638        self.assertEqual(
639            self.browser.headers['content-type'], 'image/jpeg')
640        self.assertEqual(len(self.browser.contents), PH_LEN)
641
642    def test_after_login_default_stored_in_imagestorage(self):
643        # After login the applicants placeholder image is stored in
644        # an imagestorage
645        storage = self.app['images']
646        # In the beginning, the storage is empty
647        self.assertEqual([x for x in storage.keys()], [])
648        self.login()
649        # After login, it is filled
650        self.assertEqual(
651            [x for x in storage.keys()],
652            [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])
653        file_id = self.applicant.passport.data
654        self.assertEqual(
655            file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
656        # The stored image can be fetched
657        fd = storage.retrieveFile(file_id)
658        file_len = len(fd.read())
659        self.assertEqual(file_len, PH_LEN)
660
661    def test_after_submit_default_browsable(self):
662        # After submitting an applicant form the default image is
663        # still visible
664        self.login()
665        self.browser.getControl("Save").click() # submit form
666        # There is a correct <img> link included
667        self.assertTrue(
668            '<img src="placeholder_m.jpg" />' in self.browser.contents)
669        # Browsing the link shows a real image
670        self.browser.open(self.image_url('placeholder_m.jpg'))
671        self.assertEqual(
672            self.browser.headers['content-type'], 'image/jpeg')
673        self.assertEqual(len(self.browser.contents), PH_LEN)
674
675    def test_after_submit_default_stored_in_imagestorage(self):
676        # After submitting an applicant form the default image is
677        # correctly stored in an imagestorage
678        self.login()
679        self.browser.getControl("Save").click() # submit form
680        storage = self.app['images']
681        self.assertEqual(
682            [x for x in storage.keys()],
683            [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])
684        file_id = self.applicant.passport.data
685        self.assertEqual(
686            file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
687        # The stored image can be fetched
688        fd = storage.retrieveFile(file_id)
689        file_len = len(fd.read())
690        self.assertEqual(file_len, PH_LEN)
691
692    def test_uploaded_image_browsable_w_errors(self):
693        # We can upload a different image and browse it after submit,
694        # even if there are still errors in the form
695        self.login()
696        # Create a pseudo image file and select it to be uploaded in form
697        photo_content = 'I pretend to be a graphics file'
698        pseudo_image = StringIO(photo_content)
699        ctrl = self.browser.getControl(name='form.passport')
700        file_ctrl = ctrl.mech_control
701        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
702        self.browser.getControl("Save").click() # submit form
703        # There is a correct <img> link included
704        self.assertTrue(
705            '<img src="myphoto.jpg" />' in self.browser.contents)
706        # Browsing the link shows a real image
707        self.browser.open(self.image_url('myphoto.jpg'))
708        self.assertEqual(
709            self.browser.headers['content-type'], 'image/jpeg')
710        self.assertEqual(self.browser.contents, photo_content)
711
712    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
713        # After uploading a new passport pic the file is correctly
714        # stored in an imagestorage
715        self.login()
716        # Create a pseudo image file and select it to be uploaded in form
717        pseudo_image = StringIO('I pretend to be a graphics file')
718        ctrl = self.browser.getControl(name='form.passport')
719        file_ctrl = ctrl.mech_control
720        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
721        self.browser.getControl("Save").click() # submit form
722        storage = self.app['images']
723        self.assertTrue(
724            u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
725        # The stored image can be fetched
726        fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
727        file_len = len(fd.read())
728        self.assertEqual(file_len, 31)
729        # The image uploaded belongs to the applicant
730        file_id = self.applicant.passport.data
731        self.assertEqual(
732            file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
733
734    def test_uploaded_image_browsable_wo_errors(self):
735        # We can upload a different image and browse it after submit,
736        # if there are no errors in form
737        self.login()
738        self.fill_correct_values() # fill other fields with correct values
739        # Create a pseudo image file and select it to be uploaded in form
740        pseudo_image = StringIO('I pretend to be a graphics file')
741        ctrl = self.browser.getControl(name='form.passport')
742        file_ctrl = ctrl.mech_control
743        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
744        self.browser.getControl("Save").click() # submit form
745        # There is a correct <img> link included
746        self.assertTrue(
747            '<img src="myphoto.jpg" />' in self.browser.contents)
748        # Browsing the link shows a real image
749        self.browser.open(self.image_url('myphoto.jpg'))
750        self.assertEqual(
751            self.browser.headers['content-type'], 'image/jpeg')
752        self.assertEqual(len(self.browser.contents), 31)
753
754    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
755        # After uploading a new passport pic the file is correctly
756        # stored in an imagestorage if form contains no errors
757        self.login()
758        self.fill_correct_values() # fill other fields with correct values
759        # Create a pseudo image file and select it to be uploaded in form
760        pseudo_image = StringIO('I pretend to be a graphics file')
761        ctrl = self.browser.getControl(name='form.passport')
762        file_ctrl = ctrl.mech_control
763        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
764        self.browser.getControl("Save").click() # submit form
765        storage = self.app['images']
766        self.assertTrue(
767            u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
768        # The stored image can be fetched
769        fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
770        #fd = storage.retrieveFile(file_id)
771        file_len = len(fd.read())
772        self.assertEqual(file_len, 31)
773        # The image uploaded belongs to the applicant
774        file_id = self.applicant.passport.data
775        self.assertEqual(
776            file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
777
778    def test_uploaded_images_equal(self):
779        # Make sure uploaded images do really differ if we eject a
780        # change notfication (and do not if we don't)
781        self.login() # Create applicant form
782        self.fill_correct_values() # fill other fields with correct values
783        self.browser.getControl("Save").click() # submit form
784        # Now go on as an officer
785        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
786        self.browser.open(self.edit_full_path)
787
788        # Create a pseudo image file and select it to be uploaded in form
789        pseudo_image = StringIO('I pretend to be a graphics file')
790        ctrl = self.browser.getControl(name='form.passport')
791        file_ctrl = ctrl.mech_control
792        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
793        passport0 = self.applicant.passport
794        self.browser.getControl("Save").click() # submit form with changed pic
795        passport1 = self.applicant.passport
796        self.browser.getControl("Save").click() # submit form w/o changes
797        passport2 = self.applicant.passport
798        self.assertTrue(passport0 != passport1)
799        self.assertTrue(passport1 == passport2)
800        self.assertTrue(passport1 is passport2)
801        return
802
803    def test_final_submit(self):
804        # Make sure that a correctly filled form with passport picture
805        # can be submitted
806        self.login() # Create applicant form
807        self.fill_correct_values() # fill other fields with correct values
808        # Create a pseudo image file and select it to be uploaded in form
809        pseudo_image = StringIO('I pretend to be a graphics file')
810        ctrl = self.browser.getControl(name='form.passport')
811        file_ctrl = ctrl.mech_control
812        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
813        self.browser.getControl("Final Submit").click() # (finally) submit form
814        self.assertEqual(self.browser.headers['Status'], '200 Ok')
815        self.assertTrue(
816            'Passport confirmation box not ticked' in self.browser.contents)
817        self.browser.getControl(name="confirm_passport").value = True
818        IWorkflowInfo(self.applicant).fireTransition('submit')
819        self.browser.getControl("Final Submit").click() # submit form again
820        self.assertTrue(
821            'Wrong state' in self.browser.contents)
822        IWorkflowInfo(self.applicant).fireTransition('reset1')
823        # Now do the whole thing again but with correct state
824        self.login()
825        self.fill_correct_values()
826        pseudo_image = StringIO('I pretend to be a graphics file')
827        ctrl = self.browser.getControl(name='form.passport')
828        file_ctrl = ctrl.mech_control
829        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
830        self.browser.getControl(name="confirm_passport").value = True
831        self.browser.getControl("Final Submit").click()
832        self.assertTrue(
833            '... submitted ...' in self.browser.contents)
834        self.browser.goBack(count=1)
835        self.browser.getControl("Save").click()
836        self.assertTrue(
837            'The requested form is locked' in self.browser.contents)
838        self.browser.goBack(count=1)
839        #import pdb; pdb.set_trace()
840        self.browser.getControl("Final Submit").click()
841        self.assertTrue(
842            'The requested form is locked' in self.browser.contents)
843        return
844
845    def test_locking(self):
846        # Make sure that locked forms can't be submitted
847        self.login() # Create applicant form
848        self.fill_correct_values() # fill other fields with correct values
849        # Create a pseudo image file and select it to be uploaded in form
850        pseudo_image = StringIO('I pretend to be a graphics file')
851        ctrl = self.browser.getControl(name='form.passport')
852        file_ctrl = ctrl.mech_control
853        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
854        self.browser.getControl("Save").click()
855        self.browser.getLink("Logout").click()
856
857        # Login as manager and lock the form
858        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
859        self.browser.open(self.edit_full_path)
860        self.browser.getControl(name="form.locked").value = True
861        self.browser.getControl("Save").click()
862        self.browser.getLink("Logout").click()
863
864        # Login as applicant again and try to open the edit form
865        self.login()
866        self.browser.open(self.edit_path)
867        self.assertEqual(self.browser.headers['Status'], '200 Ok')
868        #print self.browser.contents
869        self.assertTrue(
870            'The requested form is locked' in self.browser.contents)
871        return
Note: See TracBrowser for help on using the repository browser.