source: main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_browser.py @ 8033

Last change on this file since 8033 was 8033, checked in by Henrik Bettermann, 12 years ago

Define two different application modes: create and update. The latter expects existing (imported) application records. The ApplicantRegistrationPage? renders different form fields and creates or updates application records depending on the selected mode.

The update registration mode is not yet secure enough. Further security features will be added.

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