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

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

Remove uncommented pdb break points.

  • Property svn:keywords set to Id
File size: 37.2 KB
Line 
1## $Id: test_browser.py 7649 2012-02-14 15:00:47Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Test the applicant-related UI components.
20"""
21import shutil
22import tempfile
23from StringIO import StringIO
24from datetime import datetime, date, timedelta
25from mechanize import LinkNotFoundError
26from zope.component import createObject, getUtility
27from zope.component.hooks import setSite, clearSite
28from zope.security.interfaces import Unauthorized
29from zope.testbrowser.testing import Browser
30from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
31from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
32from waeup.sirp.app import University
33from waeup.sirp.configuration import SessionConfiguration
34from waeup.sirp.applicants.container import ApplicantsContainer
35from waeup.sirp.applicants.applicant import Applicant
36from waeup.sirp.interfaces import (
37    IExtFileStore, IFileStoreNameChooser, IUserAccount)
38from waeup.sirp.university.faculty import Faculty
39from waeup.sirp.university.department import Department
40
41PH_LEN = 2059  # Length of placeholder file
42
43class ApplicantsFullSetup(FunctionalTestCase):
44    # A test case that only contains a setup and teardown
45    #
46    # Complete setup for applicants handlings is rather complex and
47    # requires lots of things created before we can start. This is a
48    # setup that does all this, creates a university, creates PINs,
49    # etc.  so that we do not have to bother with that in different
50    # test cases.
51
52    layer = FunctionalLayer
53
54    def setUp(self):
55        super(ApplicantsFullSetup, self).setUp()
56
57        # Setup a sample site for each test
58        app = University()
59        self.dc_root = tempfile.mkdtemp()
60        app['datacenter'].setStoragePath(self.dc_root)
61
62        # Prepopulate the ZODB...
63        self.getRootFolder()['app'] = app
64        # we add the site immediately after creation to the
65        # ZODB. Catalogs and other local utilities are not setup
66        # before that step.
67        self.app = self.getRootFolder()['app']
68        # Set site here. Some of the following setup code might need
69        # to access grok.getSite() and should get our new app then
70        setSite(app)
71
72        self.login_path = 'http://localhost/app/login'
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.code = u'app2009'
82        applicantscontainer.prefix = 'app'
83        applicantscontainer.year = 2009
84        applicantscontainer.application_category = 'basic'
85        delta = timedelta(days=10)
86        applicantscontainer.startdate = date.today() - delta
87        applicantscontainer.enddate = date.today() + delta
88        self.app['applicants']['app2009'] = applicantscontainer
89        self.applicantscontainer = self.app['applicants']['app2009']
90
91        # Populate university
92        certificate = createObject('waeup.Certificate')
93        certificate.code = 'CERT1'
94        certificate.application_category = 'basic'
95        certificate.start_level = 100
96        certificate.end_level = 500
97        self.certificate = certificate
98        self.app['faculties']['fac1'] = Faculty()
99        # The code has explicitely to be set, otherwise we don't
100        # find created students in their department
101        self.app['faculties']['fac1']['dep1'] = Department(code='dep1')
102        self.department = self.app['faculties']['fac1']['dep1']
103        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
104            certificate)
105
106        # Put the prepopulated site into test ZODB and prepare test
107        # browser
108        self.browser = Browser()
109        self.browser.handleErrors = False
110
111        # Create 5 access codes with prefix'FOO' and cost 9.99 each
112        pin_container = self.app['accesscodes']
113        pin_container.createBatch(
114            datetime.now(), 'some_userid', 'APP', 9.99, 5)
115        pins = pin_container[pin_container.keys()[0]].values()
116        self.pins = [x.representation for x in pins]
117        self.existing_pin = self.pins[0]
118        parts = self.existing_pin.split('-')[1:]
119        self.existing_series, self.existing_number = parts
120
121        # Add an applicant
122        self.applicant = Applicant()
123        # reg_number is the only field which has to be preset here
124        # because managers are allowed to edit this required field
125        self.applicant.reg_number = u'1234'
126        app['applicants']['app2009'].addApplicant(self.applicant)
127        IUserAccount(
128            self.app['applicants']['app2009'][
129            self.applicant.application_number]).setPassword('apwd')
130        self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % (
131            'app2009', self.applicant.application_number, 'manage')
132        self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % (
133            'app2009', self.applicant.application_number, 'edit')
134        self.view_path = 'http://localhost/app/applicants/%s/%s' % (
135            'app2009', self.applicant.application_number)
136
137    def login(self):
138        # Perform an applicant login. This creates an applicant record.
139        #
140        # This helper also sets `self.applicant`, which is the
141        # applicant object created.
142        self.browser.open(self.login_path)
143        self.browser.getControl(
144            name="form.login").value = self.applicant.applicant_id
145        self.browser.getControl(name="form.password").value = 'apwd'
146        self.browser.getControl("Login").click()
147
148    def fill_correct_values(self):
149        # Fill the edit form with suitable values
150        self.browser.getControl(name="form.firstname").value = 'John'
151        self.browser.getControl(name="form.lastname").value = 'Tester'
152        self.browser.getControl(name="form.course1").value = ['CERT1']
153        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
154        self.browser.getControl(name="form.lga").value = ['foreigner']
155        self.browser.getControl(name="form.sex").value = ['m']
156        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
157
158    def tearDown(self):
159        super(ApplicantsFullSetup, self).tearDown()
160        clearSite()
161        shutil.rmtree(self.dc_root)
162
163class ApplicantsRootUITests(ApplicantsFullSetup):
164    # Tests for ApplicantsRoot class
165
166    layer = FunctionalLayer
167
168    def test_anonymous_access(self):
169        # Anonymous users can access applicants root
170        self.browser.open(self.root_path)
171        self.assertEqual(self.browser.headers['Status'], '200 Ok')
172        self.assertFalse(
173            'Manage' in self.browser.contents)
174        return
175
176    def test_anonymous_no_actions(self):
177        # Make sure anonymous users cannot access actions
178        self.browser.open(self.root_path)
179        self.assertRaises(
180            LookupError, self.browser.getControl, "Add local role")
181        # Manage screen neither linked nor accessible for anonymous
182        self.assertRaises(
183            LinkNotFoundError,
184            self.browser.getLink, 'Manage application section')
185        self.assertRaises(
186            Unauthorized, self.browser.open, self.manage_root_path)
187        # Add container screen not accessible for anonymous
188        self.assertRaises(
189            Unauthorized, self.browser.open, self.add_container_path)
190        return
191
192    def test_manage_access(self):
193        # Managers can access the manage pages of applicants root
194        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
195        self.browser.open(self.root_path)
196        self.assertTrue('Manage application section' in self.browser.contents)
197        # There is a manage link
198        link = self.browser.getLink('Manage application section')
199        link.click()
200        self.assertEqual(self.browser.headers['Status'], '200 Ok')
201        self.assertEqual(self.browser.url, self.manage_root_path)
202        return
203
204    def test_manage_actions_access(self):
205        # Managers can access the action on manage screen
206        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
207        self.browser.open(self.manage_root_path)
208        self.browser.getControl("Add local role").click()
209        self.assertTrue('No user selected' in self.browser.contents)
210        return
211
212    # We have no local roles yet
213    #def test_local_roles_add_delete(self):
214    #    # Managers can assign and delete local roles of applicants root
215    #    myusers = self.app['users']
216    #    myusers.addUser('bob', 'bobssecret')
217    #    self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
218    #    self.browser.open(self.manage_root_path)
219    #    self.browser.getControl(name="user").value = ['bob']
220    #    self.browser.getControl(name="local_role").value = [
221    #        'waeup.local.ApplicationsOfficer']
222    #    self.browser.getControl("Add local role").click()
223    #    self.assertTrue('<td>bob</td>' in self.browser.contents)
224    #    # Remove the role assigned
225    #    ctrl = self.browser.getControl(name='role_id')
226    #    ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
227    #    self.browser.getControl("Remove selected local roles").click()
228    #    self.assertTrue('Successfully removed:' in self.browser.contents)
229    #    self.assertFalse('<td>bob</td>' in self.browser.contents)
230    #    return
231
232    def test_add_delete_container(self):
233        # Managers can add and delete applicants containers
234        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
235        self.browser.open(self.manage_root_path)
236        self.browser.getControl("Cancel").click()
237        self.assertEqual(self.browser.url, self.root_path)
238        self.browser.open(self.manage_root_path)
239        self.browser.getControl("Add applicants container").click()
240        self.assertEqual(self.browser.headers['Status'], '200 Ok')
241        self.assertEqual(self.browser.url, self.add_container_path)
242        self.browser.getControl(name="form.prefix").value = ['app']
243        self.browser.getControl("Add applicants container").click()
244        self.assertTrue(
245            'There were errors' in self.browser.contents)
246        self.browser.getControl(name="form.prefix").value = ['app']
247        self.browser.getControl(name="form.year").value = ['2010']
248        self.browser.getControl(name="form.provider").value = [
249            'waeup.sirp.applicants.ApplicantsContainer']
250        self.browser.getControl(
251            name="form.application_category").value = ['basic']
252        self.browser.getControl("Add applicants container").click()
253        self.assertTrue('Added:' in self.browser.contents)
254        self.browser.open(self.add_container_path)
255        self.browser.getControl("Cancel").click()
256        self.assertEqual(self.browser.url, self.manage_root_path)
257        self.browser.open(self.add_container_path)
258        self.browser.getControl(name="form.prefix").value = ['app']
259        self.browser.getControl(name="form.year").value = ['2010']
260        self.browser.getControl(name="form.provider").value = [
261            'waeup.sirp.applicants.ApplicantsContainer']
262        self.browser.getControl(
263            name="form.application_category").value = ['basic']
264        self.browser.getControl("Add applicants container").click()
265        self.assertTrue('exists already in the database'
266                        in self.browser.contents)
267        self.browser.open(self.manage_root_path)
268        ctrl = self.browser.getControl(name='val_id')
269        ctrl.getControl(value='app2010').selected = True
270        self.browser.getControl("Remove selected", index=0).click()
271        self.assertTrue('Successfully removed:' in self.browser.contents)
272        self.browser.open(self.add_container_path)
273        self.browser.getControl(name="form.prefix").value = ['app']
274        self.browser.getControl(name="form.year").value = ['2010']
275        self.browser.getControl(name="form.provider").value = [
276            'waeup.sirp.applicants.ApplicantsContainer']
277        #self.browser.getControl(name="form.ac_prefix").value = ['APP']
278        self.browser.getControl(
279            name="form.application_category").value = ['basic']
280        self.browser.getControl("Add applicants container").click()
281        del self.app['applicants']['app2010']
282        ctrl = self.browser.getControl(name='val_id')
283        ctrl.getControl(value='app2010').selected = True
284        self.browser.getControl("Remove selected", index=0).click()
285        self.assertMatches('...Could not delete...', self.browser.contents)
286        return
287
288class ApplicantsContainerUITests(ApplicantsFullSetup):
289    # Tests for ApplicantsContainer class views and pages
290
291    layer = FunctionalLayer
292
293    def test_anonymous_access(self):
294        # Anonymous users can access applicants containers
295        self.browser.open(self.container_path)
296        self.assertEqual(self.browser.headers['Status'], '200 Ok')
297        self.assertFalse(
298            'Manage' in self.browser.contents)
299        return
300
301    def test_manage_access(self):
302        # Managers can access the manage pages of applicants
303        # containers and can perform actions
304        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
305        self.browser.open(self.manage_container_path)
306        self.assertEqual(self.browser.headers['Status'], '200 Ok')
307        self.assertEqual(self.browser.url, self.manage_container_path)
308        self.browser.getControl("Save").click()
309        self.assertTrue('Form has been saved' in self.browser.contents)
310        self.browser.getControl("Remove selected", index=0).click()
311        self.assertTrue('No applicant selected' in self.browser.contents)
312        self.browser.getControl("Add local role").click()
313        self.assertTrue('No user selected' in self.browser.contents)
314        self.browser.getControl("Cancel", index=0).click()
315        self.assertEqual(self.browser.url, self.container_path)
316        return
317
318    def test_add_delete_applicants(self):
319        # Managers can add and delete applicants
320        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
321        self.add_applicant_path = self.container_path + '/addapplicant'
322        self.container_manage_path = self.container_path + '/@@manage'
323        self.browser.open(self.container_manage_path)
324        self.browser.getControl("Add applicant").click()
325        self.assertEqual(self.browser.headers['Status'], '200 Ok')
326        self.assertEqual(self.browser.url, self.add_applicant_path)
327        self.browser.getControl(name="form.firstname").value = 'Alois'
328        self.browser.getControl(name="form.middlename").value = 'Kofi'
329        self.browser.getControl(name="form.lastname").value = 'Bettermann'
330        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
331        self.browser.getControl("Create application record").click()
332        self.assertTrue('Application initialized' in self.browser.contents)
333        self.browser.open(self.container_manage_path)
334        self.assertEqual(self.browser.headers['Status'], '200 Ok')
335        ctrl = self.browser.getControl(name='val_id')
336        value = ctrl.options[0]
337        ctrl.getControl(value=value).selected = True
338        self.browser.getControl("Remove selected", index=0).click()
339        self.assertTrue('Successfully removed:' in self.browser.contents)
340        self.browser.open(self.add_applicant_path)
341        self.browser.getControl(name="form.firstname").value = 'Albert'
342        self.browser.getControl(name="form.lastname").value = 'Einstein'
343        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
344        self.browser.getControl("Create application record").click()
345        self.assertTrue('Application initialized' in self.browser.contents)
346        return
347
348class ApplicantUITests(ApplicantsFullSetup):
349    # Tests for uploading/browsing the passport image of appplicants
350
351    layer = FunctionalLayer
352
353    def test_manage_and_view_applicant(self):
354        # Managers can manage applicants
355        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
356        self.slip_path = self.view_path + '/application_slip.pdf'
357        self.browser.open(self.manage_path)
358        self.assertEqual(self.browser.headers['Status'], '200 Ok')
359        self.fill_correct_values()
360        # Fire transition
361        self.browser.getControl(name="transition").value = ['start']
362        self.browser.getControl("Save").click()
363        # Be sure that the empty phone field does not show wrong error message
364        self.assertFalse('Required input is missing' in self.browser.contents)
365        self.assertMatches('...Form has been saved...', self.browser.contents)
366        self.assertMatches('...Application started by Manager...',
367                           self.browser.contents)
368        self.browser.open(self.view_path)
369        self.assertEqual(self.browser.headers['Status'], '200 Ok')
370        # Change course_admitted
371        self.browser.open(self.manage_path)
372        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
373        self.browser.getControl("Save").click()
374        self.assertMatches('...Form has been saved...', self.browser.contents)
375        # Change password
376        self.browser.getControl(name="password").value = 'secret'
377        self.browser.getControl(name="control_password").value = 'secre'
378        self.browser.getControl("Save").click()
379        self.assertMatches('...Passwords do not match...',
380                           self.browser.contents)
381        self.browser.getControl(name="password").value = 'secret'
382        self.browser.getControl(name="control_password").value = 'secret'
383        self.browser.getControl("Save").click()
384        self.assertMatches('...Form has been saved...', self.browser.contents)
385        # Open pdf slip
386        self.browser.open(self.slip_path)
387        self.assertEqual(self.browser.headers['Status'], '200 Ok')
388        self.assertEqual(self.browser.headers['Content-Type'],
389                         'application/pdf')
390        # Managers can view applicants even if certificate has been removed
391        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
392        self.browser.open(self.view_path)
393        self.assertEqual(self.browser.headers['Status'], '200 Ok')
394        self.browser.open(self.slip_path)
395        self.assertEqual(self.browser.headers['Status'], '200 Ok')
396        return
397
398    def test_passport_edit_view(self):
399        # We get a default image after login
400        self.browser.open(self.login_path)
401        self.login()
402        self.browser.open(self.browser.url + '/passport.jpg')
403        self.assertEqual(self.browser.headers['status'], '200 Ok')
404        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
405        self.assertTrue('JFIF' in self.browser.contents)
406        self.assertEqual(
407            self.browser.headers['content-length'], str(PH_LEN))
408
409    def test_edit_applicant(self):
410        # Applicants can edit their record
411        self.browser.open(self.login_path)
412        self.login()
413        self.browser.open(self.edit_path)
414        self.assertTrue(self.browser.url != self.login_path)
415        self.assertEqual(self.browser.headers['Status'], '200 Ok')
416        self.fill_correct_values()
417        self.browser.getControl("Save").click()
418        self.assertMatches('...Form has been saved...', self.browser.contents)
419        return
420
421    def image_url(self, filename):
422        return self.edit_path.replace('edit', filename)
423
424    def test_after_login_default_browsable(self):
425        # After login we see the placeholder image in the edit view
426        self.login()
427        self.assertEqual(self.browser.url, self.view_path)
428        self.browser.open(self.edit_path)
429        # There is a correct <img> link included
430        self.assertTrue(
431              '<img src="passport.jpg" />' in self.browser.contents)
432        # Browsing the link shows a real image
433        self.browser.open(self.image_url('passport.jpg'))
434        self.assertEqual(
435            self.browser.headers['content-type'], 'image/jpeg')
436        self.assertEqual(len(self.browser.contents), PH_LEN)
437
438    def test_after_submit_default_browsable(self):
439        # After submitting an applicant form the default image is
440        # still visible
441        self.login()
442        self.browser.open(self.edit_path)
443        self.browser.getControl("Save").click() # submit form
444        # There is a correct <img> link included
445        self.assertTrue(
446            '<img src="passport.jpg" />' in self.browser.contents)
447        # Browsing the link shows a real image
448        self.browser.open(self.image_url('passport.jpg'))
449        self.assertEqual(
450            self.browser.headers['content-type'], 'image/jpeg')
451        self.assertEqual(len(self.browser.contents), PH_LEN)
452
453    def test_uploaded_image_respects_file_size_restriction(self):
454        # When we upload an image that is too big ( > 10 KB) we will
455        # get an error message
456        self.login()
457        self.browser.open(self.edit_path)
458        # Create a pseudo image file and select it to be uploaded in form
459        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
460        pseudo_image = StringIO(photo_content)
461        ctrl = self.browser.getControl(name='form.passport')
462        file_ctrl = ctrl.mech_control
463        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
464        self.browser.getControl("Save").click() # submit form
465        # There is a correct <img> link included
466        self.assertTrue(
467            '<img src="passport.jpg" />' in self.browser.contents)
468        # We get a warning message
469        self.assertTrue(
470            'Uploaded image is too big' in self.browser.contents)
471        # Browsing the image gives us the default image, not the
472        # uploaded one.
473        self.browser.open(self.image_url('passport.jpg'))
474        self.assertEqual(
475            self.browser.headers['content-type'], 'image/jpeg')
476        self.assertEqual(len(self.browser.contents), PH_LEN)
477        # There is really no file stored for the applicant
478        img = getUtility(IExtFileStore).getFile(
479            IFileStoreNameChooser(self.applicant).chooseName())
480        self.assertTrue(img is None)
481
482    def test_uploaded_image_browsable_w_errors(self):
483        # We can upload a different image and browse it after submit,
484        # even if there are still errors in the form
485        self.login()
486        self.browser.open(self.edit_path)
487        # Create a pseudo image file and select it to be uploaded in form
488        photo_content = 'I pretend to be a graphics file'
489        pseudo_image = StringIO(photo_content)
490        ctrl = self.browser.getControl(name='form.passport')
491        file_ctrl = ctrl.mech_control
492        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
493        self.browser.getControl("Save").click() # submit form
494        # There is a correct <img> link included
495        self.assertTrue(
496            '<img src="passport.jpg" />' in self.browser.contents)
497        # Browsing the link shows a real image
498        self.browser.open(self.image_url('passport.jpg'))
499        self.assertEqual(
500            self.browser.headers['content-type'], 'image/jpeg')
501        self.assertEqual(self.browser.contents, photo_content)
502
503    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
504        # After uploading a new passport pic the file is correctly
505        # stored in an imagestorage
506        self.login()
507        self.browser.open(self.edit_path)
508        # Create a pseudo image file and select it to be uploaded in form
509        pseudo_image = StringIO('I pretend to be a graphics file')
510        ctrl = self.browser.getControl(name='form.passport')
511        file_ctrl = ctrl.mech_control
512        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
513        self.browser.getControl("Save").click() # submit form
514        storage = getUtility(IExtFileStore)
515        file_id = IFileStoreNameChooser(self.applicant).chooseName()
516        pseudo_image.seek(0) # reset our file data source
517        self.assertEqual(
518            storage.getFile(file_id).read(), pseudo_image.read())
519        return
520
521    def test_uploaded_image_browsable_wo_errors(self):
522        # We can upload a different image and browse it after submit,
523        # if there are no errors in form
524        self.login()
525        self.browser.open(self.edit_path)
526        self.fill_correct_values() # fill other fields with correct values
527        # Create a pseudo image file and select it to be uploaded in form
528        pseudo_image = StringIO('I pretend to be a graphics file')
529        ctrl = self.browser.getControl(name='form.passport')
530        file_ctrl = ctrl.mech_control
531        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
532        self.browser.getControl("Save").click() # submit form
533        # There is a correct <img> link included
534        self.assertTrue(
535            '<img src="passport.jpg" />' in self.browser.contents)
536        # Browsing the link shows a real image
537        self.browser.open(self.image_url('passport.jpg'))
538        self.assertEqual(
539            self.browser.headers['content-type'], 'image/jpeg')
540        self.assertEqual(len(self.browser.contents), 31)
541
542    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
543        # After uploading a new passport pic the file is correctly
544        # stored in an imagestorage if form contains no errors
545        self.login()
546        self.browser.open(self.edit_path)
547        self.fill_correct_values() # fill other fields with correct values
548        # Create a pseudo image file and select it to be uploaded in form
549        pseudo_image = StringIO('I pretend to be a graphics file')
550        ctrl = self.browser.getControl(name='form.passport')
551        file_ctrl = ctrl.mech_control
552        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
553        self.browser.getControl("Save").click() # submit form
554        storage = getUtility(IExtFileStore)
555        file_id = IFileStoreNameChooser(self.applicant).chooseName()
556        # The stored image can be fetched
557        fd = storage.getFile(file_id)
558        file_len = len(fd.read())
559        self.assertEqual(file_len, 31)
560
561    def test_uploaded_images_equal(self):
562        # Make sure uploaded images do really differ if we eject a
563        # change notfication (and do not if we don't)
564        self.login()
565        self.browser.open(self.edit_path)
566        self.fill_correct_values() # fill other fields with correct values
567        self.browser.getControl("Save").click() # submit form
568        # Now go on as an officer
569        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
570        self.browser.open(self.manage_path)
571
572        # Create a pseudo image file and select it to be uploaded in form
573        pseudo_image = StringIO('I pretend to be a graphics file')
574        ctrl = self.browser.getControl(name='form.passport')
575        file_ctrl = ctrl.mech_control
576        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
577        file_id = IFileStoreNameChooser(self.applicant).chooseName()
578        setSite(self.app)
579        passport0 = getUtility(IExtFileStore).getFile(file_id)
580        self.browser.getControl("Save").click() # submit form with changed pic
581        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
582        self.browser.getControl("Save").click() # submit form w/o changes
583        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
584        self.assertTrue(passport0 is None)
585        self.assertTrue(passport0 != passport1)
586        self.assertTrue(passport1 == passport2)
587        return
588
589    def test_pay_acceptance_fee(self):
590        self.login()
591        self.browser.open(self.edit_path)
592        # Payment tickets can't be created before the form has been validated
593        self.browser.getControl("Add online payment ticket").click()
594        self.assertTrue('Required input is missing' in self.browser.contents)
595        self.fill_correct_values()
596        # We have to save the form otherwise the filled fields will be cleared
597        # after adding an online payment, because adding an online payment
598        # requires a filled form but does not save it
599        self.browser.getControl("Save").click()
600        self.browser.getControl("Add online payment ticket").click()
601        # Session object missing
602        self.assertTrue(
603            'Session configuration object is not available'
604            in self.browser.contents)
605        configuration = SessionConfiguration()
606        configuration.academic_session = 2009
607        configuration.acceptance_fee = 200
608        self.app['configuration'].addSessionConfiguration(configuration)
609        self.browser.open(self.edit_path)
610        self.fill_correct_values()
611        self.browser.getControl("Add online payment ticket").click()
612        self.assertMatches('...Payment ticket created...',
613                           self.browser.contents)
614        # Payment ticket can be removed if they haven't received a
615        # valid callback
616        ctrl = self.browser.getControl(name='val_id')
617        value = ctrl.options[0]
618        ctrl.getControl(value=value).selected = True
619        self.browser.getControl("Remove selected", index=0).click()
620        self.assertMatches('...Successfully removed...', self.browser.contents)
621        # We will try the callback request view
622        self.browser.getControl("Add online payment ticket").click()
623        ctrl = self.browser.getControl(name='val_id')
624        value = ctrl.options[0]
625        self.browser.getLink(value).click()
626        self.assertMatches('...Amount Authorized...',
627                           self.browser.contents)
628        payment_url = self.browser.url
629        # The pdf payment receipt can't yet be opened
630        self.browser.open(payment_url + '/payment_receipt.pdf')
631        self.assertMatches('...Ticket not yet paid...',
632                           self.browser.contents)
633        # Request callback
634        self.browser.open(payment_url)
635        self.browser.getLink("Request callback").click()
636        self.assertMatches('...Valid callback received...',
637                          self.browser.contents)
638        # Callback can't be applied twice
639        self.browser.open(payment_url + '/callback')
640        self.assertMatches(
641            "...Transition 'pay' requires 'started' as source state...",
642            self.browser.contents)
643        # The payment receipt can be downloaded now
644        self.browser.open(payment_url)
645        self.browser.getLink("Download payment receipt").click()
646        self.assertEqual(self.browser.headers['Status'], '200 Ok')
647        self.assertEqual(self.browser.headers['Content-Type'],
648                         'application/pdf')
649        # Applicant is is in state 'paid'
650        self.browser.open(self.view_path)
651        self.assertMatches('...paid...',
652                           self.browser.contents)
653        state = IWorkflowState(self.applicant).getState()
654        self.assertTrue(state == 'paid')
655
656    def test_final_submit(self):
657        # Make sure that a correctly filled form with passport picture
658        # can be submitted (only) after payment
659        self.login()
660        self.browser.getLink("Edit application record").click()
661        self.assertFalse('Final Submit' in self.browser.contents)
662        IWorkflowInfo(self.applicant).fireTransition('pay')
663        self.browser.open(self.edit_path)
664        self.assertTrue('Final Submit' in self.browser.contents)
665        self.fill_correct_values() # fill other fields with correct values
666        self.browser.getControl("Final Submit").click()
667        # We forgot to upload a passport picture
668        self.assertTrue(
669            'No passport picture uploaded' in self.browser.contents)
670        # Create a pseudo image file and select it to be uploaded in form
671        pseudo_image = StringIO('I pretend to be a graphics file')
672        ctrl = self.browser.getControl(name='form.passport')
673        file_ctrl = ctrl.mech_control
674        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
675        self.browser.getControl("Final Submit").click() # (finally) submit form
676        # The picture has been uploaded but the form cannot be submitted
677        # since the passport confirmation box was not ticked
678        self.assertTrue(
679            'Passport picture confirmation box not ticked'
680            in self.browser.contents)
681        self.browser.getControl(name="confirm_passport").value = True
682        self.browser.getControl("Final Submit").click()
683        self.assertTrue(
684            '... submitted ...' in self.browser.contents)
685        self.browser.goBack(count=1)
686        self.browser.getControl("Save").click()
687        self.assertTrue(
688            'The requested form is locked' in self.browser.contents)
689        self.browser.goBack(count=1)
690        self.browser.getControl("Final Submit").click()
691        self.assertTrue(
692            'The requested form is locked' in self.browser.contents)
693        return
694
695    def test_locking(self):
696        # Make sure that locked forms can't be submitted
697        self.login()
698        self.browser.open(self.edit_path)
699        self.fill_correct_values() # fill other fields with correct values
700        # Create a pseudo image file and select it to be uploaded in form
701        pseudo_image = StringIO('I pretend to be a graphics file')
702        ctrl = self.browser.getControl(name='form.passport')
703        file_ctrl = ctrl.mech_control
704        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
705        self.browser.getControl("Save").click()
706        # Now we lock the form
707        self.applicant.locked = True
708        self.browser.open(self.edit_path)
709        self.assertEqual(self.browser.headers['Status'], '200 Ok')
710        self.assertTrue(
711            'The requested form is locked' in self.browser.contents)
712        return
713
714    def test_certificate_removed(self):
715        self.login()
716        self.browser.open(self.edit_path)
717        self.fill_correct_values()
718        self.browser.getControl("Save").click()
719        self.browser.open(self.view_path)
720        self.assertTrue(
721            'Unnamed Certificate' in self.browser.contents)
722        self.browser.open(self.edit_path)
723        self.assertTrue(
724            '<option selected="selected" value="CERT1">' in self.browser.contents)
725        # Now we remove the certificate
726        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
727        # The certificate is still shown in display mode
728        self.browser.open(self.view_path)
729        self.assertTrue(
730            'Unnamed Certificate' in self.browser.contents)
731        # The certificate is still selectable in edit mode so that it won't
732        # be automatically replaced by another (arbitrary) certificate
733        self.browser.open(self.edit_path)
734        self.assertTrue(
735            '<option selected="selected" value="CERT1">' in self.browser.contents)
736        # Consequently, the certificate is still shown after saving the form
737        self.browser.getControl("Save").click()
738        self.browser.open(self.view_path)
739        self.assertTrue(
740            'Unnamed Certificate' in self.browser.contents)
741        # Even if we add a new certificate the previous (removed)
742        # certificate is shown
743        certificate = createObject('waeup.Certificate')
744        certificate.code = 'CERT2'
745        certificate.title = 'New Certificate'
746        certificate.application_category = 'basic'
747        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
748            certificate)
749        self.browser.open(self.edit_path)
750        self.assertTrue(
751            '<option selected="selected" value="CERT1">' in self.browser.contents)
752
753class ApplicantRegisterTests(ApplicantsFullSetup):
754    # Tests for applicant registration
755
756    layer = FunctionalLayer
757
758    def test_register_applicant(self):
759        # An applicant can register himself.
760        self.browser.open(self.container_path)
761        self.browser.getLink("Register for application").click()
762        # Fill the edit form with suitable values
763        self.browser.getControl(name="form.firstname").value = 'John'
764        self.browser.getControl(name="form.lastname").value = 'Tester'
765        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
766        self.browser.getControl(name="form.phone.country").value = '234'
767        self.browser.getControl(name="form.phone.area").value = '555'
768        self.browser.getControl(name="form.phone.extension").value = '6666666'
769        self.browser.getControl("Get login credentials").click()
770        self.assertEqual(self.browser.url,
771            self.container_path + '/registration_complete?email=xx%40yy.zz')
772        return
773
774    def test_register_applicant_wo_phone(self):
775        # We don't require the phone number when registering
776        self.browser.open(self.container_path)
777        self.browser.getLink("Register for application").click()
778        # Fill the edit form with suitable values
779        self.browser.getControl(name="form.firstname").value = 'John'
780        self.browser.getControl(name="form.lastname").value = 'Tester'
781        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
782        self.browser.getControl("Get login credentials").click()
783        self.assertEqual(self.browser.url,
784            self.container_path + '/registration_complete?email=xx%40yy.zz')
785        return
Note: See TracBrowser for help on using the repository browser.