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

Last change on this file since 9506 was 7795, checked in by uli, 13 years ago

Merge changes from ulif-schoolgrades back into trunk.

  • Property svn:keywords set to Id
File size: 38.7 KB
Line 
1## $Id: test_browser.py 7795 2012-03-08 03:30:30Z uli $
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.getLink("app2010").click()
255        self.assertTrue('<h1>General Studies 2010/2011</h1>'
256            in self.browser.contents)
257        self.browser.open(self.add_container_path)
258        self.browser.getControl("Cancel").click()
259        self.assertEqual(self.browser.url, self.manage_root_path)
260        self.browser.open(self.add_container_path)
261        self.browser.getControl(name="form.prefix").value = ['app']
262        self.browser.getControl(name="form.year").value = ['2010']
263        self.browser.getControl(name="form.provider").value = [
264            'waeup.sirp.applicants.ApplicantsContainer']
265        self.browser.getControl(
266            name="form.application_category").value = ['basic']
267        self.browser.getControl("Add applicants container").click()
268        self.assertTrue('exists already in the database'
269                        in self.browser.contents)
270        self.browser.open(self.manage_root_path)
271        ctrl = self.browser.getControl(name='val_id')
272        ctrl.getControl(value='app2010').selected = True
273        self.browser.getControl("Remove selected", index=0).click()
274        self.assertTrue('Successfully removed:' in self.browser.contents)
275        self.browser.open(self.add_container_path)
276        self.browser.getControl(name="form.prefix").value = ['app']
277        self.browser.getControl(name="form.year").value = ['2010']
278        self.browser.getControl(name="form.provider").value = [
279            'waeup.sirp.applicants.ApplicantsContainer']
280        #self.browser.getControl(name="form.ac_prefix").value = ['APP']
281        self.browser.getControl(
282            name="form.application_category").value = ['basic']
283        self.browser.getControl("Add applicants container").click()
284        del self.app['applicants']['app2010']
285        ctrl = self.browser.getControl(name='val_id')
286        ctrl.getControl(value='app2010').selected = True
287        self.browser.getControl("Remove selected", index=0).click()
288        self.assertMatches('...Could not delete...', self.browser.contents)
289        return
290
291class ApplicantsContainerUITests(ApplicantsFullSetup):
292    # Tests for ApplicantsContainer class views and pages
293
294    layer = FunctionalLayer
295
296    def test_anonymous_access(self):
297        # Anonymous users can access applicants containers
298        self.browser.open(self.container_path)
299        self.assertEqual(self.browser.headers['Status'], '200 Ok')
300        self.assertFalse(
301            'Manage' in self.browser.contents)
302        return
303
304    def test_manage_access(self):
305        # Managers can access the manage pages of applicants
306        # containers and can perform actions
307        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
308        self.browser.open(self.manage_container_path)
309        self.assertEqual(self.browser.headers['Status'], '200 Ok')
310        self.assertEqual(self.browser.url, self.manage_container_path)
311        self.browser.getControl("Save").click()
312        self.assertTrue('Form has been saved' in self.browser.contents)
313        self.browser.getControl("Remove selected", index=0).click()
314        self.assertTrue('No applicant selected' in self.browser.contents)
315        self.browser.getControl("Add local role").click()
316        self.assertTrue('No user selected' in self.browser.contents)
317        self.browser.getControl("Cancel", index=0).click()
318        self.assertEqual(self.browser.url, self.container_path)
319        return
320
321    def test_add_delete_applicants(self):
322        # Managers can add and delete applicants
323        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
324        self.add_applicant_path = self.container_path + '/addapplicant'
325        self.container_manage_path = self.container_path + '/@@manage'
326        self.browser.open(self.container_manage_path)
327        self.browser.getControl("Add applicant").click()
328        self.assertEqual(self.browser.headers['Status'], '200 Ok')
329        self.assertEqual(self.browser.url, self.add_applicant_path)
330        self.browser.getControl(name="form.firstname").value = 'Alois'
331        self.browser.getControl(name="form.middlename").value = 'Kofi'
332        self.browser.getControl(name="form.lastname").value = 'Bettermann'
333        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
334        self.browser.getControl("Create application record").click()
335        self.assertTrue('Application initialized' in self.browser.contents)
336        self.browser.open(self.container_manage_path)
337        self.assertEqual(self.browser.headers['Status'], '200 Ok')
338        ctrl = self.browser.getControl(name='val_id')
339        value = ctrl.options[0]
340        ctrl.getControl(value=value).selected = True
341        self.browser.getControl("Remove selected", index=0).click()
342        self.assertTrue('Successfully removed:' in self.browser.contents)
343        self.browser.open(self.add_applicant_path)
344        self.browser.getControl(name="form.firstname").value = 'Albert'
345        self.browser.getControl(name="form.lastname").value = 'Einstein'
346        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
347        self.browser.getControl("Create application record").click()
348        self.assertTrue('Application initialized' in self.browser.contents)
349        return
350
351class ApplicantUITests(ApplicantsFullSetup):
352    # Tests for uploading/browsing the passport image of appplicants
353
354    layer = FunctionalLayer
355
356    def test_manage_and_view_applicant(self):
357        # Managers can manage applicants
358        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
359        self.slip_path = self.view_path + '/application_slip.pdf'
360        self.browser.open(self.manage_path)
361        self.assertEqual(self.browser.headers['Status'], '200 Ok')
362        self.fill_correct_values()
363        # Fire transition
364        self.browser.getControl(name="transition").value = ['start']
365        self.browser.getControl("Save").click()
366        # Be sure that the empty phone field does not show wrong error message
367        self.assertFalse('Required input is missing' in self.browser.contents)
368        self.assertMatches('...Form has been saved...', self.browser.contents)
369        self.assertMatches('...Application started by Manager...',
370                           self.browser.contents)
371        self.browser.open(self.view_path)
372        self.assertEqual(self.browser.headers['Status'], '200 Ok')
373        # Change course_admitted
374        self.browser.open(self.manage_path)
375        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
376        self.browser.getControl("Save").click()
377        self.assertMatches('...Form has been saved...', self.browser.contents)
378        # Change password
379        self.browser.getControl(name="password").value = 'secret'
380        self.browser.getControl(name="control_password").value = 'secre'
381        self.browser.getControl("Save").click()
382        self.assertMatches('...Passwords do not match...',
383                           self.browser.contents)
384        self.browser.getControl(name="password").value = 'secret'
385        self.browser.getControl(name="control_password").value = 'secret'
386        self.browser.getControl("Save").click()
387        self.assertMatches('...Form has been saved...', self.browser.contents)
388        # Open pdf slip
389        self.browser.open(self.slip_path)
390        self.assertEqual(self.browser.headers['Status'], '200 Ok')
391        self.assertEqual(self.browser.headers['Content-Type'],
392                         'application/pdf')
393        # Managers can view applicants even if certificate has been removed
394        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
395        self.browser.open(self.view_path)
396        self.assertEqual(self.browser.headers['Status'], '200 Ok')
397        self.browser.open(self.slip_path)
398        self.assertEqual(self.browser.headers['Status'], '200 Ok')
399        return
400
401    def test_passport_edit_view(self):
402        # We get a default image after login
403        self.browser.open(self.login_path)
404        self.login()
405        self.browser.open(self.browser.url + '/passport.jpg')
406        self.assertEqual(self.browser.headers['status'], '200 Ok')
407        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
408        self.assertTrue('JFIF' in self.browser.contents)
409        self.assertEqual(
410            self.browser.headers['content-length'], str(PH_LEN))
411
412    def test_edit_applicant(self):
413        # Applicants can edit their record
414        self.browser.open(self.login_path)
415        self.login()
416        self.browser.open(self.edit_path)
417        self.assertTrue(self.browser.url != self.login_path)
418        self.assertEqual(self.browser.headers['Status'], '200 Ok')
419        self.fill_correct_values()
420        self.browser.getControl("Save").click()
421        self.assertMatches('...Form has been saved...', self.browser.contents)
422        return
423
424    def image_url(self, filename):
425        return self.edit_path.replace('edit', filename)
426
427    def test_after_login_default_browsable(self):
428        # After login we see the placeholder image in the edit view
429        self.login()
430        self.assertEqual(self.browser.url, self.view_path)
431        self.browser.open(self.edit_path)
432        # There is a correct <img> link included
433        self.assertTrue(
434              '<img src="passport.jpg" />' in self.browser.contents)
435        # Browsing the link shows a real image
436        self.browser.open(self.image_url('passport.jpg'))
437        self.assertEqual(
438            self.browser.headers['content-type'], 'image/jpeg')
439        self.assertEqual(len(self.browser.contents), PH_LEN)
440
441    def test_after_submit_default_browsable(self):
442        # After submitting an applicant form the default image is
443        # still visible
444        self.login()
445        self.browser.open(self.edit_path)
446        self.browser.getControl("Save").click() # submit form
447        # There is a correct <img> link included
448        self.assertTrue(
449            '<img src="passport.jpg" />' in self.browser.contents)
450        # Browsing the link shows a real image
451        self.browser.open(self.image_url('passport.jpg'))
452        self.assertEqual(
453            self.browser.headers['content-type'], 'image/jpeg')
454        self.assertEqual(len(self.browser.contents), PH_LEN)
455
456    def test_uploaded_image_respects_file_size_restriction(self):
457        # When we upload an image that is too big ( > 10 KB) we will
458        # get an error message
459        self.login()
460        self.browser.open(self.edit_path)
461        # Create a pseudo image file and select it to be uploaded in form
462        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
463        pseudo_image = StringIO(photo_content)
464        ctrl = self.browser.getControl(name='form.passport')
465        file_ctrl = ctrl.mech_control
466        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
467        self.browser.getControl("Save").click() # submit form
468        # There is a correct <img> link included
469        self.assertTrue(
470            '<img src="passport.jpg" />' in self.browser.contents)
471        # We get a warning message
472        self.assertTrue(
473            'Uploaded image is too big' in self.browser.contents)
474        # Browsing the image gives us the default image, not the
475        # uploaded one.
476        self.browser.open(self.image_url('passport.jpg'))
477        self.assertEqual(
478            self.browser.headers['content-type'], 'image/jpeg')
479        self.assertEqual(len(self.browser.contents), PH_LEN)
480        # There is really no file stored for the applicant
481        img = getUtility(IExtFileStore).getFile(
482            IFileStoreNameChooser(self.applicant).chooseName())
483        self.assertTrue(img is None)
484
485    def test_uploaded_image_browsable_w_errors(self):
486        # We can upload a different image and browse it after submit,
487        # even if there are still errors in the form
488        self.login()
489        self.browser.open(self.edit_path)
490        # Create a pseudo image file and select it to be uploaded in form
491        photo_content = 'I pretend to be a graphics file'
492        pseudo_image = StringIO(photo_content)
493        ctrl = self.browser.getControl(name='form.passport')
494        file_ctrl = ctrl.mech_control
495        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
496        self.browser.getControl("Save").click() # submit form
497        # There is a correct <img> link included
498        self.assertTrue(
499            '<img src="passport.jpg" />' in self.browser.contents)
500        # Browsing the link shows a real image
501        self.browser.open(self.image_url('passport.jpg'))
502        self.assertEqual(
503            self.browser.headers['content-type'], 'image/jpeg')
504        self.assertEqual(self.browser.contents, photo_content)
505
506    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
507        # After uploading a new passport pic the file is correctly
508        # stored in an imagestorage
509        self.login()
510        self.browser.open(self.edit_path)
511        # Create a pseudo image file and select it to be uploaded in form
512        pseudo_image = StringIO('I pretend to be a graphics file')
513        ctrl = self.browser.getControl(name='form.passport')
514        file_ctrl = ctrl.mech_control
515        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
516        self.browser.getControl("Save").click() # submit form
517        storage = getUtility(IExtFileStore)
518        file_id = IFileStoreNameChooser(self.applicant).chooseName()
519        pseudo_image.seek(0) # reset our file data source
520        self.assertEqual(
521            storage.getFile(file_id).read(), pseudo_image.read())
522        return
523
524    def test_uploaded_image_browsable_wo_errors(self):
525        # We can upload a different image and browse it after submit,
526        # if there are no errors in form
527        self.login()
528        self.browser.open(self.edit_path)
529        self.fill_correct_values() # fill other fields with correct values
530        # Create a pseudo image file and select it to be uploaded in form
531        pseudo_image = StringIO('I pretend to be a graphics file')
532        ctrl = self.browser.getControl(name='form.passport')
533        file_ctrl = ctrl.mech_control
534        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
535        self.browser.getControl("Save").click() # submit form
536        # There is a correct <img> link included
537        self.assertTrue(
538            '<img src="passport.jpg" />' in self.browser.contents)
539        # Browsing the link shows a real image
540        self.browser.open(self.image_url('passport.jpg'))
541        self.assertEqual(
542            self.browser.headers['content-type'], 'image/jpeg')
543        self.assertEqual(len(self.browser.contents), 31)
544
545    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
546        # After uploading a new passport pic the file is correctly
547        # stored in an imagestorage if form contains no errors
548        self.login()
549        self.browser.open(self.edit_path)
550        self.fill_correct_values() # fill other fields with correct values
551        # Create a pseudo image file and select it to be uploaded in form
552        pseudo_image = StringIO('I pretend to be a graphics file')
553        ctrl = self.browser.getControl(name='form.passport')
554        file_ctrl = ctrl.mech_control
555        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
556        self.browser.getControl("Save").click() # submit form
557        storage = getUtility(IExtFileStore)
558        file_id = IFileStoreNameChooser(self.applicant).chooseName()
559        # The stored image can be fetched
560        fd = storage.getFile(file_id)
561        file_len = len(fd.read())
562        self.assertEqual(file_len, 31)
563
564    def test_uploaded_images_equal(self):
565        # Make sure uploaded images do really differ if we eject a
566        # change notfication (and do not if we don't)
567        self.login()
568        self.browser.open(self.edit_path)
569        self.fill_correct_values() # fill other fields with correct values
570        self.browser.getControl("Save").click() # submit form
571        # Now go on as an officer
572        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
573        self.browser.open(self.manage_path)
574
575        # Create a pseudo image file and select it to be uploaded in form
576        pseudo_image = StringIO('I pretend to be a graphics file')
577        ctrl = self.browser.getControl(name='form.passport')
578        file_ctrl = ctrl.mech_control
579        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
580        file_id = IFileStoreNameChooser(self.applicant).chooseName()
581        setSite(self.app)
582        passport0 = getUtility(IExtFileStore).getFile(file_id)
583        self.browser.getControl("Save").click() # submit form with changed pic
584        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
585        self.browser.getControl("Save").click() # submit form w/o changes
586        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
587        self.assertTrue(passport0 is None)
588        self.assertTrue(passport0 != passport1)
589        self.assertTrue(passport1 == passport2)
590        return
591
592    def test_pay_acceptance_fee(self):
593        self.login()
594        self.browser.open(self.edit_path)
595        # Payment tickets can't be created before the form has been validated
596        self.browser.getControl("Add online payment ticket").click()
597        self.assertTrue('Required input is missing' in self.browser.contents)
598        self.fill_correct_values()
599        # We have to save the form otherwise the filled fields will be cleared
600        # after adding an online payment, because adding an online payment
601        # requires a filled form but does not save it
602        self.browser.getControl("Save").click()
603        self.browser.getControl("Add online payment ticket").click()
604        # Session object missing
605        self.assertTrue(
606            'Session configuration object is not available'
607            in self.browser.contents)
608        configuration = SessionConfiguration()
609        configuration.academic_session = 2009
610        configuration.acceptance_fee = 200
611        self.app['configuration'].addSessionConfiguration(configuration)
612        self.browser.open(self.edit_path)
613        self.fill_correct_values()
614        self.browser.getControl("Add online payment ticket").click()
615        self.assertMatches('...Payment ticket created...',
616                           self.browser.contents)
617        # Payment ticket can be removed if they haven't received a
618        # valid callback
619        ctrl = self.browser.getControl(name='val_id')
620        value = ctrl.options[0]
621        ctrl.getControl(value=value).selected = True
622        self.browser.getControl("Remove selected", index=0).click()
623        self.assertMatches('...Successfully removed...', self.browser.contents)
624        # We will try the callback request view
625        self.browser.getControl("Add online payment ticket").click()
626        ctrl = self.browser.getControl(name='val_id')
627        value = ctrl.options[0]
628        self.browser.getLink(value).click()
629        self.assertMatches('...Amount Authorized...',
630                           self.browser.contents)
631        payment_url = self.browser.url
632        # The pdf payment receipt can't yet be opened
633        self.browser.open(payment_url + '/payment_receipt.pdf')
634        self.assertMatches('...Ticket not yet paid...',
635                           self.browser.contents)
636        # Request callback
637        self.browser.open(payment_url)
638        self.browser.getLink("Request callback").click()
639        self.assertMatches('...Valid callback received...',
640                          self.browser.contents)
641        # Callback can't be applied twice
642        self.browser.open(payment_url + '/callback')
643        self.assertMatches(
644            "...Transition 'pay' requires 'started' as source state...",
645            self.browser.contents)
646        # The payment receipt can be downloaded now
647        self.browser.open(payment_url)
648        self.browser.getLink("Download payment receipt").click()
649        self.assertEqual(self.browser.headers['Status'], '200 Ok')
650        self.assertEqual(self.browser.headers['Content-Type'],
651                         'application/pdf')
652        # Applicant is is in state 'paid'
653        self.browser.open(self.view_path)
654        self.assertMatches('...paid...',
655                           self.browser.contents)
656        state = IWorkflowState(self.applicant).getState()
657        self.assertTrue(state == 'paid')
658
659    def test_final_submit(self):
660        # Make sure that a correctly filled form with passport picture
661        # can be submitted (only) after payment
662        self.login()
663        self.browser.getLink("Edit application record").click()
664        self.assertFalse('Final Submit' in self.browser.contents)
665        IWorkflowInfo(self.applicant).fireTransition('pay')
666        self.browser.open(self.edit_path)
667        self.assertTrue('Final Submit' in self.browser.contents)
668        self.fill_correct_values() # fill other fields with correct values
669        self.browser.getControl("Final Submit").click()
670        # We forgot to upload a passport picture
671        self.assertTrue(
672            'No passport picture uploaded' in self.browser.contents)
673        # Create a pseudo image file and select it to be uploaded in form
674        pseudo_image = StringIO('I pretend to be a graphics file')
675        ctrl = self.browser.getControl(name='form.passport')
676        file_ctrl = ctrl.mech_control
677        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
678        self.browser.getControl("Final Submit").click() # (finally) submit form
679        # The picture has been uploaded but the form cannot be submitted
680        # since the passport confirmation box was not ticked
681        self.assertTrue(
682            'Passport picture confirmation box not ticked'
683            in self.browser.contents)
684        self.browser.getControl(name="confirm_passport").value = True
685        self.browser.getControl("Final Submit").click()
686        self.assertTrue(
687            '... submitted ...' in self.browser.contents)
688        self.browser.goBack(count=1)
689        self.browser.getControl("Save").click()
690        self.assertTrue(
691            'The requested form is locked' in self.browser.contents)
692        self.browser.goBack(count=1)
693        self.browser.getControl("Final Submit").click()
694        self.assertTrue(
695            'The requested form is locked' in self.browser.contents)
696        return
697
698    def test_locking(self):
699        # Make sure that locked forms can't be submitted
700        self.login()
701        self.browser.open(self.edit_path)
702        self.fill_correct_values() # fill other fields with correct values
703        # Create a pseudo image file and select it to be uploaded in form
704        pseudo_image = StringIO('I pretend to be a graphics file')
705        ctrl = self.browser.getControl(name='form.passport')
706        file_ctrl = ctrl.mech_control
707        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
708        self.browser.getControl("Save").click()
709        # Now we lock the form
710        self.applicant.locked = True
711        self.browser.open(self.edit_path)
712        self.assertEqual(self.browser.headers['Status'], '200 Ok')
713        self.assertTrue(
714            'The requested form is locked' in self.browser.contents)
715        return
716
717    def test_certificate_removed(self):
718        self.login()
719        self.browser.open(self.edit_path)
720        self.fill_correct_values()
721        self.browser.getControl("Save").click()
722        self.browser.open(self.view_path)
723        self.assertTrue(
724            'Unnamed Certificate' in self.browser.contents)
725        self.browser.open(self.edit_path)
726        self.assertTrue(
727            '<option selected="selected" value="CERT1">' in self.browser.contents)
728        # Now we remove the certificate
729        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
730        # The certificate is still shown in display mode
731        self.browser.open(self.view_path)
732        self.assertTrue(
733            'Unnamed Certificate' in self.browser.contents)
734        # The certificate is still selectable in edit mode so that it won't
735        # be automatically replaced by another (arbitrary) certificate
736        self.browser.open(self.edit_path)
737        self.assertTrue(
738            '<option selected="selected" value="CERT1">' in self.browser.contents)
739        # Consequently, the certificate is still shown after saving the form
740        self.browser.getControl("Save").click()
741        self.browser.open(self.view_path)
742        self.assertTrue(
743            'Unnamed Certificate' in self.browser.contents)
744        # Even if we add a new certificate the previous (removed)
745        # certificate is shown
746        certificate = createObject('waeup.Certificate')
747        certificate.code = 'CERT2'
748        certificate.title = 'New Certificate'
749        certificate.application_category = 'basic'
750        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
751            certificate)
752        self.browser.open(self.edit_path)
753        self.assertTrue(
754            '<option selected="selected" value="CERT1">'
755            in self.browser.contents)
756
757    def test_school_grades(self):
758        # we can add school grades
759        self.login()
760        self.browser.open(self.edit_path)
761        self.fill_correct_values() # Fill other fields
762        # add a new (empty) row with subject/grade
763        self.browser.getControl(name="form.school_grades.add").click()
764        # pick first subject (after <no value>)...
765        ctrl_subj = self.browser.getControl(
766            name="form.school_grades.0.subject")
767        ctrl_subj.value = [ctrl_subj.options[1]]
768        display_subj = ctrl_subj.displayOptions[1]
769        # pick first grade (after <no value>)...
770        ctrl_grade = self.browser.getControl(
771            name="form.school_grades.0.grade")
772        ctrl_grade.value = [ctrl_grade.options[1]]
773        display_grade = ctrl_grade.displayOptions[1]
774        # save everything
775        self.browser.getControl("Save").click()
776        self.assertTrue(
777            '<input type="hidden" name="form.school_grades.count" value="1" />'
778            in self.browser.contents)
779        # we can also see the new subject/grade in display view
780        self.browser.open(self.view_path)
781        self.assertTrue(
782            display_subj in self.browser.contents)
783        self.assertTrue(
784            display_grade in self.browser.contents)
785        return
786
787class ApplicantRegisterTests(ApplicantsFullSetup):
788    # Tests for applicant registration
789
790    layer = FunctionalLayer
791
792    def test_register_applicant(self):
793        # An applicant can register himself.
794        self.browser.open(self.container_path)
795        self.browser.getLink("Register for application").click()
796        # Fill the edit form with suitable values
797        self.browser.getControl(name="form.firstname").value = 'John'
798        self.browser.getControl(name="form.lastname").value = 'Tester'
799        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
800        self.browser.getControl(name="form.phone.country").value = '234'
801        self.browser.getControl(name="form.phone.area").value = '555'
802        self.browser.getControl(name="form.phone.extension").value = '6666666'
803        self.browser.getControl("Get login credentials").click()
804        self.assertEqual(self.browser.url,
805            self.container_path + '/registration_complete?email=xx%40yy.zz')
806        return
807
808    def test_register_applicant_wo_phone(self):
809        # We don't require the phone number when registering
810        self.browser.open(self.container_path)
811        self.browser.getLink("Register for application").click()
812        # Fill the edit form with suitable values
813        self.browser.getControl(name="form.firstname").value = 'John'
814        self.browser.getControl(name="form.lastname").value = 'Tester'
815        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
816        self.browser.getControl("Get login credentials").click()
817        self.assertEqual(self.browser.url,
818            self.container_path + '/registration_complete?email=xx%40yy.zz')
819        return
Note: See TracBrowser for help on using the repository browser.