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

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

Improve test.

  • Property svn:keywords set to Id
File size: 47.9 KB
Line 
1## $Id: test_browser.py 8602 2012-06-02 11:30:59Z 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 pytz
22import shutil
23import tempfile
24import grok
25from StringIO import StringIO
26from datetime import datetime, date, timedelta
27from mechanize import LinkNotFoundError
28from zope.event import notify
29from zope.catalog.interfaces import ICatalog
30from zope.component import createObject, getUtility
31from zope.component.hooks import setSite, clearSite
32from zope.security.interfaces import Unauthorized
33from zope.testbrowser.testing import Browser
34from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
35from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
36from waeup.kofa.app import University
37from waeup.kofa.configuration import SessionConfiguration
38from waeup.kofa.applicants.container import ApplicantsContainer
39from waeup.kofa.applicants.applicant import Applicant
40from waeup.kofa.interfaces import (
41    IExtFileStore, IFileStoreNameChooser, IUserAccount)
42from waeup.kofa.university.faculty import Faculty
43from waeup.kofa.university.department import Department
44
45PH_LEN = 2059  # Length of placeholder file
46
47class ApplicantsFullSetup(FunctionalTestCase):
48    # A test case that only contains a setup and teardown
49    #
50    # Complete setup for applicants handlings is rather complex and
51    # requires lots of things created before we can start. This is a
52    # setup that does all this, creates a university, creates PINs,
53    # etc.  so that we do not have to bother with that in different
54    # test cases.
55
56    layer = FunctionalLayer
57
58    def setUp(self):
59        super(ApplicantsFullSetup, self).setUp()
60
61        # Setup a sample site for each test
62        app = University()
63        self.dc_root = tempfile.mkdtemp()
64        app['datacenter'].setStoragePath(self.dc_root)
65
66        # Prepopulate the ZODB...
67        self.getRootFolder()['app'] = app
68        # we add the site immediately after creation to the
69        # ZODB. Catalogs and other local utilities are not setup
70        # before that step.
71        self.app = self.getRootFolder()['app']
72        # Set site here. Some of the following setup code might need
73        # to access grok.getSite() and should get our new app then
74        setSite(app)
75
76        self.login_path = 'http://localhost/app/login'
77        self.root_path = 'http://localhost/app/applicants'
78        self.search_path = 'http://localhost/app/applicants/search'
79        self.manage_root_path = self.root_path + '/@@manage'
80        self.add_container_path = self.root_path + '/@@add'
81        self.container_path = 'http://localhost/app/applicants/app2009'
82        self.manage_container_path = self.container_path + '/@@manage'
83
84        # Add an applicants container
85        applicantscontainer = ApplicantsContainer()
86        applicantscontainer.code = u'app2009'
87        applicantscontainer.prefix = 'app'
88        applicantscontainer.year = 2009
89        applicantscontainer.title = u'This is the app2009 container'
90        applicantscontainer.application_category = 'basic'
91        applicantscontainer.mode = 'create'
92        delta = timedelta(days=10)
93        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
94        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
95        self.app['applicants']['app2009'] = applicantscontainer
96        self.applicantscontainer = self.app['applicants']['app2009']
97
98        # Populate university
99        certificate = createObject('waeup.Certificate')
100        certificate.code = 'CERT1'
101        certificate.application_category = 'basic'
102        certificate.start_level = 100
103        certificate.end_level = 500
104        self.certificate = certificate
105        self.app['faculties']['fac1'] = Faculty()
106        # The code has explicitely to be set, otherwise we don't
107        # find created students in their department
108        self.app['faculties']['fac1']['dep1'] = Department(code='dep1')
109        self.department = self.app['faculties']['fac1']['dep1']
110        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
111            certificate)
112
113        # Put the prepopulated site into test ZODB and prepare test
114        # browser
115        self.browser = Browser()
116        self.browser.handleErrors = False
117
118        # Create 5 access codes with prefix'FOO' and cost 9.99 each
119        pin_container = self.app['accesscodes']
120        pin_container.createBatch(
121            datetime.now(), 'some_userid', 'APP', 9.99, 5)
122        pins = pin_container[pin_container.keys()[0]].values()
123        self.pins = [x.representation for x in pins]
124        self.existing_pin = self.pins[0]
125        parts = self.existing_pin.split('-')[1:]
126        self.existing_series, self.existing_number = parts
127
128        # Add an applicant
129        self.applicant = createObject('waeup.Applicant')
130        # reg_number is the only field which has to be preset here
131        # because managers are allowed to edit this required field
132        self.applicant.reg_number = u'1234'
133        self.applicant.course1 = certificate
134        app['applicants']['app2009'].addApplicant(self.applicant)
135        IUserAccount(
136            self.app['applicants']['app2009'][
137            self.applicant.application_number]).setPassword('apwd')
138        self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % (
139            'app2009', self.applicant.application_number, 'manage')
140        self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % (
141            'app2009', self.applicant.application_number, 'edit')
142        self.view_path = 'http://localhost/app/applicants/%s/%s' % (
143            'app2009', self.applicant.application_number)
144
145    def login(self):
146        # Perform an applicant login. This creates an applicant record.
147        #
148        # This helper also sets `self.applicant`, which is the
149        # applicant object created.
150        self.browser.open(self.login_path)
151        self.browser.getControl(
152            name="form.login").value = self.applicant.applicant_id
153        self.browser.getControl(name="form.password").value = 'apwd'
154        self.browser.getControl("Login").click()
155
156    def fill_correct_values(self):
157        # Fill the edit form with suitable values
158        self.browser.getControl(name="form.firstname").value = 'John'
159        self.browser.getControl(name="form.lastname").value = 'Tester'
160        self.browser.getControl(name="form.course1").value = ['CERT1']
161        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
162        self.browser.getControl(name="form.sex").value = ['m']
163        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
164
165    def tearDown(self):
166        super(ApplicantsFullSetup, self).tearDown()
167        clearSite()
168        shutil.rmtree(self.dc_root)
169
170class ApplicantsRootUITests(ApplicantsFullSetup):
171    # Tests for ApplicantsRoot class
172
173    layer = FunctionalLayer
174
175    def test_anonymous_access(self):
176        # Anonymous users can access applicants root
177        self.browser.open(self.root_path)
178        self.assertEqual(self.browser.headers['Status'], '200 Ok')
179        self.assertFalse(
180            'Manage ' in self.browser.contents)
181        return
182
183    def test_anonymous_no_actions(self):
184        # Make sure anonymous users cannot access actions
185        self.browser.open(self.root_path)
186        self.assertRaises(
187            LookupError, self.browser.getControl, "Add local role")
188        # Manage screen neither linked nor accessible for anonymous
189        self.assertRaises(
190            LinkNotFoundError,
191            self.browser.getLink, 'Manage application section')
192        self.assertRaises(
193            Unauthorized, self.browser.open, self.manage_root_path)
194        # Add container screen not accessible for anonymous
195        self.assertRaises(
196            Unauthorized, self.browser.open, self.add_container_path)
197        return
198
199    def test_manage_access(self):
200        # Managers can access the manage pages of applicants root
201        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
202        self.browser.open(self.root_path)
203        self.assertTrue('Manage application section' in self.browser.contents)
204        # There is a manage link
205        link = self.browser.getLink('Manage application section')
206        link.click()
207        self.assertEqual(self.browser.headers['Status'], '200 Ok')
208        self.assertEqual(self.browser.url, self.manage_root_path)
209        return
210
211    def test_search(self):
212        # Managers can access the manage pages of applicants root
213        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
214        self.browser.open(self.manage_path)
215        self.fill_correct_values()
216        self.browser.getControl("Save").click()
217        self.browser.open(self.root_path)
218        self.assertTrue('Manage application section' in self.browser.contents)
219        # There is a search link
220        link = self.browser.getLink('Search applicants')
221        link.click()
222        self.assertEqual(self.browser.headers['Status'], '200 Ok')
223        # We can find an applicant ...
224        # ... via his name
225        self.browser.getControl(name="searchtype").value = ['fullname']
226        self.browser.getControl(name="searchterm").value = 'John'
227        self.browser.getControl("Search").click()
228        self.assertTrue('John Tester' in self.browser.contents)
229        self.browser.getControl(name="searchtype").value = ['fullname']
230        self.browser.getControl(name="searchterm").value = 'Tester'
231        self.browser.getControl("Search").click()
232        self.assertTrue('John Tester' in self.browser.contents)
233        self.browser.open(self.search_path)
234        # ... and via his reg_number ...
235        self.browser.getControl(name="searchtype").value = ['reg_number']
236        self.browser.getControl(name="searchterm").value = '2345'
237        self.browser.getControl("Search").click()
238        self.assertFalse('John Tester' in self.browser.contents)
239        self.browser.getControl(name="searchtype").value = ['reg_number']
240        self.browser.getControl(name="searchterm").value = '1234'
241        self.browser.getControl("Search").click()
242        self.assertTrue('John Tester' in self.browser.contents)
243        # ... and not via his application_number ...
244        self.browser.getControl(name="searchtype").value = ['applicant_id']
245        self.browser.getControl(
246            name="searchterm").value = self.applicant.application_number
247        self.browser.getControl("Search").click()
248        self.assertFalse('John Tester' in self.browser.contents)
249        # ... but ia his applicant_id ...
250        self.browser.getControl(name="searchtype").value = ['applicant_id']
251        self.browser.getControl(
252            name="searchterm").value = self.applicant.applicant_id
253        self.browser.getControl("Search").click()
254        self.assertTrue('John Tester' in self.browser.contents)
255        # ... and via his email
256        self.browser.getControl(name="searchtype").value = ['email']
257        self.browser.getControl(name="searchterm").value = 'xx@yy.zz'
258        self.browser.getControl("Search").click()
259        self.assertTrue('John Tester' in self.browser.contents)
260        return
261
262    def test_manage_actions_access(self):
263        # Managers can access the action on manage screen
264        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
265        self.browser.open(self.manage_root_path)
266        self.browser.getControl("Add local role").click()
267        self.assertTrue('No user selected' in self.browser.contents)
268        return
269
270    # We have no local roles yet
271    #def test_local_roles_add_delete(self):
272    #    # Managers can assign and delete local roles of applicants root
273    #    myusers = self.app['users']
274    #    myusers.addUser('bob', 'bobssecret')
275    #    self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
276    #    self.browser.open(self.manage_root_path)
277    #    self.browser.getControl(name="user").value = ['bob']
278    #    self.browser.getControl(name="local_role").value = [
279    #        'waeup.local.ApplicationsOfficer']
280    #    self.browser.getControl("Add local role").click()
281    #    self.assertTrue('<td>bob</td>' in self.browser.contents)
282    #    # Remove the role assigned
283    #    ctrl = self.browser.getControl(name='role_id')
284    #    ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
285    #    self.browser.getControl("Remove selected local roles").click()
286    #    self.assertTrue('Successfully removed:' in self.browser.contents)
287    #    self.assertFalse('<td>bob</td>' in self.browser.contents)
288    #    return
289
290    def test_add_delete_container(self):
291        # Managers can add and delete applicants containers
292        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
293        self.browser.open(self.manage_root_path)
294        self.browser.getControl("Add applicants container").click()
295        self.assertEqual(self.browser.headers['Status'], '200 Ok')
296        self.assertEqual(self.browser.url, self.add_container_path)
297        self.browser.getControl(name="form.prefix").value = ['app']
298        self.browser.getControl("Add applicants container").click()
299        self.assertTrue(
300            'There were errors' in self.browser.contents)
301        self.browser.getControl(name="form.prefix").value = ['app']
302        self.browser.getControl(name="form.year").value = ['2010']
303        self.browser.getControl(name="form.mode").value = ['create']
304        self.browser.getControl(
305            name="form.application_category").value = ['basic']
306        self.browser.getControl("Add applicants container").click()
307        self.assertTrue('Added:' in self.browser.contents)
308        self.browser.getLink("app2010").click()
309        self.assertTrue('Manage applicants container'
310            in self.browser.contents)
311        self.browser.open(self.add_container_path)
312        self.browser.getControl("Cancel").click()
313        self.assertEqual(self.browser.url, self.manage_root_path)
314        self.browser.open(self.add_container_path)
315        self.browser.getControl(name="form.prefix").value = ['app']
316        self.browser.getControl(name="form.year").value = ['2010']
317        self.browser.getControl(name="form.mode").value = ['create']
318        self.browser.getControl(
319            name="form.application_category").value = ['basic']
320        self.browser.getControl("Add applicants container").click()
321        self.assertTrue('exists already in the database'
322                        in self.browser.contents)
323        self.browser.open(self.manage_root_path)
324        ctrl = self.browser.getControl(name='val_id')
325        ctrl.getControl(value='app2010').selected = True
326        self.browser.getControl("Remove selected", index=0).click()
327        self.assertTrue('Successfully removed:' in self.browser.contents)
328        self.browser.open(self.add_container_path)
329        self.browser.getControl(name="form.prefix").value = ['app']
330        self.browser.getControl(name="form.year").value = ['2010']
331        self.browser.getControl(name="form.mode").value = ['create']
332        #self.browser.getControl(name="form.ac_prefix").value = ['APP']
333        self.browser.getControl(
334            name="form.application_category").value = ['basic']
335        self.browser.getControl("Add applicants container").click()
336        del self.app['applicants']['app2010']
337        ctrl = self.browser.getControl(name='val_id')
338        ctrl.getControl(value='app2010').selected = True
339        self.browser.getControl("Remove selected", index=0).click()
340        self.assertMatches('...Could not delete...', self.browser.contents)
341        return
342
343class ApplicantsContainerUITests(ApplicantsFullSetup):
344    # Tests for ApplicantsContainer class views and pages
345
346    layer = FunctionalLayer
347
348    def test_anonymous_access(self):
349        # Anonymous users can access applicants containers
350        self.browser.open(self.container_path)
351        self.assertEqual(self.browser.headers['Status'], '200 Ok')
352        self.assertFalse(
353            'Manage ' in self.browser.contents)
354        return
355
356    def test_manage_access(self):
357        # Managers can access the manage pages of applicants
358        # containers and can perform actions
359        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
360        self.browser.open(self.manage_container_path)
361        self.assertEqual(self.browser.headers['Status'], '200 Ok')
362        self.assertEqual(self.browser.url, self.manage_container_path)
363        self.browser.getControl("Save").click()
364        self.assertTrue('Form has been saved' in self.browser.contents)
365        self.browser.getControl("Remove selected", index=0).click()
366        self.assertTrue('No applicant selected' in self.browser.contents)
367        self.browser.getControl("Add local role").click()
368        self.assertTrue('No user selected' in self.browser.contents)
369        self.browser.getControl("Cancel", index=0).click()
370        self.assertEqual(self.browser.url, self.container_path)
371        return
372
373    def test_statistics(self):
374        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
375        self.browser.open(self.container_path)
376        self.browser.getLink("Container statistics").click()
377        self.assertTrue('<td>initialized</td>' in self.browser.contents)
378        self.assertTrue('<td>1</td>' in self.browser.contents)
379        self.assertEqual(self.applicantscontainer.statistics[0],
380            {'not admitted': 0, 'started': 0, 'created': 0,
381            'admitted': 0, 'submitted': 0, 'initialized': 1, 'paid': 0})
382        self.assertEqual(self.applicantscontainer.statistics[1],
383            {u'fac1': 0})
384        IWorkflowState(self.applicant).setState('submitted')
385        self.assertEqual(self.applicantscontainer.statistics[0],
386            {'not admitted': 0, 'started': 0, 'created': 0,
387            'admitted': 0, 'submitted': 1, 'initialized': 0, 'paid': 0})
388        self.assertEqual(self.applicantscontainer.statistics[1],
389            {u'fac1': 1})
390        return
391
392    def test_add_delete_applicants(self):
393        # Managers can add and delete applicants
394        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
395        self.add_applicant_path = self.container_path + '/addapplicant'
396        self.container_manage_path = self.container_path + '/@@manage'
397        self.browser.open(self.container_manage_path)
398        self.browser.getControl("Add applicant").click()
399        self.assertEqual(self.browser.headers['Status'], '200 Ok')
400        self.assertEqual(self.browser.url, self.add_applicant_path)
401        self.browser.getControl(name="form.firstname").value = 'Alois'
402        self.browser.getControl(name="form.middlename").value = 'Kofi'
403        self.browser.getControl(name="form.lastname").value = 'Bettermann'
404        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
405        self.browser.getControl("Create application record").click()
406        self.assertTrue('Application initialized' in self.browser.contents)
407        self.browser.open(self.container_manage_path)
408        self.assertEqual(self.browser.headers['Status'], '200 Ok')
409        ctrl = self.browser.getControl(name='val_id')
410        value = ctrl.options[0]
411        ctrl.getControl(value=value).selected = True
412        self.browser.getControl("Remove selected", index=0).click()
413        self.assertTrue('Successfully removed:' in self.browser.contents)
414        self.browser.open(self.add_applicant_path)
415        self.browser.getControl(name="form.firstname").value = 'Albert'
416        self.browser.getControl(name="form.lastname").value = 'Einstein'
417        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
418        self.browser.getControl("Create application record").click()
419        self.assertTrue('Application initialized' in self.browser.contents)
420        return
421
422class ApplicantUITests(ApplicantsFullSetup):
423    # Tests for uploading/browsing the passport image of appplicants
424
425    layer = FunctionalLayer
426
427    def test_manage_and_view_applicant(self):
428        # Managers can manage applicants
429        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
430        self.slip_path = self.view_path + '/application_slip.pdf'
431        self.browser.open(self.manage_path)
432        self.assertEqual(self.browser.headers['Status'], '200 Ok')
433        self.fill_correct_values()
434        # Fire transition
435        self.browser.getControl(name="transition").value = ['start']
436        self.browser.getControl("Save").click()
437        # Be sure that the empty phone field does not show wrong error message
438        self.assertFalse('Required input is missing' in self.browser.contents)
439        self.assertMatches('...Form has been saved...', self.browser.contents)
440        self.assertMatches('...Application started by Manager...',
441                           self.browser.contents)
442        self.browser.open(self.view_path)
443        self.assertEqual(self.browser.headers['Status'], '200 Ok')
444        # Change course_admitted
445        self.browser.open(self.manage_path)
446        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
447        self.browser.getControl("Save").click()
448        self.assertMatches('...Form has been saved...', self.browser.contents)
449        # Change password
450        self.browser.getControl(name="password").value = 'secret'
451        self.browser.getControl(name="control_password").value = 'secre'
452        self.browser.getControl("Save").click()
453        self.assertMatches('...Passwords do not match...',
454                           self.browser.contents)
455        self.browser.getControl(name="password").value = 'secret'
456        self.browser.getControl(name="control_password").value = 'secret'
457        self.browser.getControl("Save").click()
458        self.assertMatches('...Form has been saved...', self.browser.contents)
459        # Open pdf slip
460        self.browser.open(self.slip_path)
461        self.assertEqual(self.browser.headers['Status'], '200 Ok')
462        self.assertEqual(self.browser.headers['Content-Type'],
463                         'application/pdf')
464        # Managers can view applicants even if certificate has been removed
465        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
466        self.browser.open(self.view_path)
467        self.assertEqual(self.browser.headers['Status'], '200 Ok')
468        self.browser.open(self.slip_path)
469        self.assertEqual(self.browser.headers['Status'], '200 Ok')
470        return
471
472    def test_passport_edit_view(self):
473        # We get a default image after login
474        self.browser.open(self.login_path)
475        self.login()
476        self.browser.open(self.browser.url + '/passport.jpg')
477        self.assertEqual(self.browser.headers['status'], '200 Ok')
478        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
479        self.assertTrue('JFIF' in self.browser.contents)
480        self.assertEqual(
481            self.browser.headers['content-length'], str(PH_LEN))
482
483    def test_applicant_access(self):
484        # Applicants can edit their record
485        self.browser.open(self.login_path)
486        self.login()
487        self.browser.open(self.edit_path)
488        self.assertTrue(self.browser.url != self.login_path)
489        self.assertEqual(self.browser.headers['Status'], '200 Ok')
490        self.fill_correct_values()
491        self.browser.getControl("Save").click()
492        self.assertMatches('...Form has been saved...', self.browser.contents)
493        # Applicants don't see manage and search links ...
494        self.browser.open(self.root_path)
495        self.assertEqual(self.browser.headers['Status'], '200 Ok')
496        self.assertFalse('Search' in self.browser.contents)
497        self.assertFalse('Manage application section' in self.browser.contents)
498        # ... and can't access the manage page
499        self.assertRaises(
500            Unauthorized, self.browser.open, self.manage_path)
501        return
502
503    def image_url(self, filename):
504        return self.edit_path.replace('edit', filename)
505
506    def test_after_login_default_browsable(self):
507        # After login we see the placeholder image in the edit view
508        self.login()
509        self.assertEqual(self.browser.url, self.view_path)
510        self.browser.open(self.edit_path)
511        # There is a correct <img> link included
512        self.assertTrue(
513              '<img src="passport.jpg" height="180px" />' in self.browser.contents)
514        # Browsing the link shows a real image
515        self.browser.open(self.image_url('passport.jpg'))
516        self.assertEqual(
517            self.browser.headers['content-type'], 'image/jpeg')
518        self.assertEqual(len(self.browser.contents), PH_LEN)
519
520    def test_after_submit_default_browsable(self):
521        # After submitting an applicant form the default image is
522        # still visible
523        self.login()
524        self.browser.open(self.edit_path)
525        self.browser.getControl("Save").click() # submit form
526        # There is a correct <img> link included
527        self.assertTrue(
528            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
529        # Browsing the link shows a real image
530        self.browser.open(self.image_url('passport.jpg'))
531        self.assertEqual(
532            self.browser.headers['content-type'], 'image/jpeg')
533        self.assertEqual(len(self.browser.contents), PH_LEN)
534
535    def test_uploaded_image_respects_file_size_restriction(self):
536        # When we upload an image that is too big ( > 10 KB) we will
537        # get an error message
538        self.login()
539        self.browser.open(self.edit_path)
540        # Create a pseudo image file and select it to be uploaded in form
541        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
542        pseudo_image = StringIO(photo_content)
543        ctrl = self.browser.getControl(name='form.passport')
544        file_ctrl = ctrl.mech_control
545        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
546        self.browser.getControl("Save").click() # submit form
547        # There is a correct <img> link included
548        self.assertTrue(
549            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
550        # We get a warning message
551        self.assertTrue(
552            'Uploaded image is too big' in self.browser.contents)
553        # Browsing the image gives us the default image, not the
554        # uploaded one.
555        self.browser.open(self.image_url('passport.jpg'))
556        self.assertEqual(
557            self.browser.headers['content-type'], 'image/jpeg')
558        self.assertEqual(len(self.browser.contents), PH_LEN)
559        # There is really no file stored for the applicant
560        img = getUtility(IExtFileStore).getFile(
561            IFileStoreNameChooser(self.applicant).chooseName())
562        self.assertTrue(img is None)
563
564    def test_uploaded_image_browsable_w_errors(self):
565        # We can upload a different image and browse it after submit,
566        # even if there are still errors in the form
567        self.login()
568        self.browser.open(self.edit_path)
569        # Create a pseudo image file and select it to be uploaded in form
570        photo_content = 'I pretend to be a graphics file'
571        pseudo_image = StringIO(photo_content)
572        ctrl = self.browser.getControl(name='form.passport')
573        file_ctrl = ctrl.mech_control
574        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
575        self.browser.getControl("Save").click() # submit form
576        # There is a correct <img> link included
577        self.assertTrue(
578            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
579        # Browsing the link shows a real image
580        self.browser.open(self.image_url('passport.jpg'))
581        self.assertEqual(
582            self.browser.headers['content-type'], 'image/jpeg')
583        self.assertEqual(self.browser.contents, photo_content)
584
585    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
586        # After uploading a new passport pic the file is correctly
587        # stored in an imagestorage
588        self.login()
589        self.browser.open(self.edit_path)
590        # Create a pseudo image file and select it to be uploaded in form
591        pseudo_image = StringIO('I pretend to be a graphics file')
592        ctrl = self.browser.getControl(name='form.passport')
593        file_ctrl = ctrl.mech_control
594        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
595        self.browser.getControl("Save").click() # submit form
596        storage = getUtility(IExtFileStore)
597        file_id = IFileStoreNameChooser(self.applicant).chooseName()
598        pseudo_image.seek(0) # reset our file data source
599        self.assertEqual(
600            storage.getFile(file_id).read(), pseudo_image.read())
601        return
602
603    def test_uploaded_image_browsable_wo_errors(self):
604        # We can upload a different image and browse it after submit,
605        # if there are no errors in form
606        self.login()
607        self.browser.open(self.edit_path)
608        self.fill_correct_values() # fill other fields with correct values
609        # Create a pseudo image file and select it to be uploaded in form
610        pseudo_image = StringIO('I pretend to be a graphics file')
611        ctrl = self.browser.getControl(name='form.passport')
612        file_ctrl = ctrl.mech_control
613        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
614        self.browser.getControl("Save").click() # submit form
615        # There is a correct <img> link included
616        self.assertTrue(
617            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
618        # Browsing the link shows a real image
619        self.browser.open(self.image_url('passport.jpg'))
620        self.assertEqual(
621            self.browser.headers['content-type'], 'image/jpeg')
622        self.assertEqual(len(self.browser.contents), 31)
623
624    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
625        # After uploading a new passport pic the file is correctly
626        # stored in an imagestorage if form contains no errors
627        self.login()
628        self.browser.open(self.edit_path)
629        self.fill_correct_values() # fill other fields with correct values
630        # Create a pseudo image file and select it to be uploaded in form
631        pseudo_image = StringIO('I pretend to be a graphics file')
632        ctrl = self.browser.getControl(name='form.passport')
633        file_ctrl = ctrl.mech_control
634        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
635        self.browser.getControl("Save").click() # submit form
636        storage = getUtility(IExtFileStore)
637        file_id = IFileStoreNameChooser(self.applicant).chooseName()
638        # The stored image can be fetched
639        fd = storage.getFile(file_id)
640        file_len = len(fd.read())
641        self.assertEqual(file_len, 31)
642
643    def test_uploaded_images_equal(self):
644        # Make sure uploaded images do really differ if we eject a
645        # change notfication (and do not if we don't)
646        self.login()
647        self.browser.open(self.edit_path)
648        self.fill_correct_values() # fill other fields with correct values
649        self.browser.getControl("Save").click() # submit form
650        # Now go on as an officer
651        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
652        self.browser.open(self.manage_path)
653
654        # Create a pseudo image file and select it to be uploaded in form
655        pseudo_image = StringIO('I pretend to be a graphics file')
656        ctrl = self.browser.getControl(name='form.passport')
657        file_ctrl = ctrl.mech_control
658        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
659        file_id = IFileStoreNameChooser(self.applicant).chooseName()
660        setSite(self.app)
661        passport0 = getUtility(IExtFileStore).getFile(file_id)
662        self.browser.getControl("Save").click() # submit form with changed pic
663        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
664        self.browser.getControl("Save").click() # submit form w/o changes
665        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
666        self.assertTrue(passport0 is None)
667        self.assertTrue(passport0 != passport1)
668        self.assertTrue(passport1 == passport2)
669        return
670
671    def test_pay_application_fee(self):
672        self.login()
673        self.browser.open(self.edit_path)
674        # Payment tickets can't be created before the form has been validated
675        self.browser.getControl("Add online payment ticket").click()
676        self.assertTrue('Required input is missing' in self.browser.contents)
677        self.fill_correct_values()
678        # We have to save the form otherwise the filled fields will be cleared
679        # after adding an online payment, because adding an online payment
680        # requires a filled form but does not save it
681        self.browser.getControl("Save").click()
682        self.browser.getControl("Add online payment ticket").click()
683        # Session object missing
684        self.assertTrue(
685            'Session configuration object is not available'
686            in self.browser.contents)
687        configuration = SessionConfiguration()
688        configuration.academic_session = 2009
689        configuration.application_fee = 200.0
690        self.app['configuration'].addSessionConfiguration(configuration)
691        self.browser.open(self.edit_path)
692        self.browser.getControl("Add online payment ticket").click()
693        self.assertMatches('...Payment ticket created...',
694                           self.browser.contents)
695        self.assertMatches('...Activation Code...',
696                           self.browser.contents)
697        # Payment ticket can be removed if they haven't received a
698        # valid callback
699        self.browser.open(self.edit_path)
700        ctrl = self.browser.getControl(name='val_id')
701        value = ctrl.options[0]
702        ctrl.getControl(value=value).selected = True
703        self.browser.getControl("Remove selected", index=0).click()
704        self.assertMatches('...Successfully removed...', self.browser.contents)
705        # We will try the callback request view
706        self.browser.getControl("Add online payment ticket").click()
707        self.browser.open(self.edit_path)
708        ctrl = self.browser.getControl(name='val_id')
709        value = ctrl.options[0]
710        self.browser.getLink(value).click()
711        self.assertMatches('...Amount Authorized...',
712                           self.browser.contents)
713        payment_url = self.browser.url
714        payment_id = self.applicant.keys()[0]
715        payment = self.applicant[payment_id]
716        self.assertEqual(payment.p_item,'This is the app2009 container')
717        self.assertEqual(payment.p_session,2009)
718        self.assertEqual(payment.p_category,'application')
719        self.assertEqual(payment.amount_auth,200.0)
720        # The pdf payment slip can't yet be opened
721        #self.browser.open(payment_url + '/payment_receipt.pdf')
722        #self.assertMatches('...Ticket not yet paid...',
723        #                   self.browser.contents)
724        # Approve payment
725        # Applicants can't approve payments
726        self.assertRaises(
727            Unauthorized, self.browser.open, payment_url + '/approve')
728        # We approve the payment by bypassing the view
729        payment.approve()
730        # The payment slip can be downloaded now
731        self.browser.open(payment_url)
732        self.browser.getLink("Download payment slip").click()
733        self.assertEqual(self.browser.headers['Status'], '200 Ok')
734        self.assertEqual(self.browser.headers['Content-Type'],
735                         'application/pdf')
736        # Applicant is is not yet in state 'paid' because it was only
737        # the payment which we set to paid
738        self.browser.open(self.view_path)
739        self.assertMatches('...started...',
740                           self.browser.contents)
741        state = IWorkflowState(self.applicant).getState()
742        self.assertTrue(state == 'started')
743        # Let's logout and approve the payment as manager
744        self.browser.getLink("Logout").click()
745        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
746        # First we reset the payment
747        payment.r_amount_approved = 0.0
748        payment.r_code = u''
749        payment.p_state = 'unpaid'
750        payment.r_desc = u''
751        payment.payment_date = None
752        self.browser.open(payment_url)
753        self.browser.getLink("Approve payment").click()
754        self.assertEqual(payment.p_state, 'paid')
755        self.assertEqual(payment.r_amount_approved, 200.0)
756        self.assertEqual(payment.r_code, 'AP')
757        state = IWorkflowState(self.applicant).getState()
758        self.assertTrue(state == 'paid')
759        return
760
761    def test_final_submit(self):
762        # Make sure that a correctly filled form with passport picture
763        # can be submitted (only) after payment
764        self.login()
765        self.browser.getLink("Edit application record").click()
766        self.assertFalse('Final Submit' in self.browser.contents)
767        IWorkflowInfo(self.applicant).fireTransition('pay')
768        self.browser.open(self.edit_path)
769        self.assertTrue('Final Submit' in self.browser.contents)
770        self.fill_correct_values() # fill other fields with correct values
771        self.browser.getControl("Final Submit").click()
772        # We forgot to upload a passport picture
773        self.assertTrue(
774            'No passport picture uploaded' in self.browser.contents)
775        # Create a pseudo image file and select it to be uploaded in form
776        pseudo_image = StringIO('I pretend to be a graphics file')
777        ctrl = self.browser.getControl(name='form.passport')
778        file_ctrl = ctrl.mech_control
779        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
780        self.browser.getControl("Final Submit").click() # (finally) submit form
781        # The picture has been uploaded but the form cannot be submitted
782        # since the passport confirmation box was not ticked
783        self.assertTrue(
784            'Passport picture confirmation box not ticked'
785            in self.browser.contents)
786        self.browser.getControl(name="confirm_passport").value = True
787        self.applicantscontainer.enddate = datetime.now(pytz.utc)
788        self.browser.getControl("Final Submit").click()
789        self.assertTrue(
790            'Application period has expired' in self.browser.contents)
791        self.applicantscontainer.enddate = datetime.now(
792            pytz.utc) + timedelta(days=10)
793        self.browser.getControl(name="confirm_passport").value = True
794        self.browser.getControl("Final Submit").click()
795        self.assertTrue(
796            '... submitted ...' in self.browser.contents)
797        self.browser.goBack(count=1)
798        self.browser.getControl("Save").click()
799        self.assertTrue(
800            'The requested form is locked' in self.browser.contents)
801        self.browser.goBack(count=1)
802        self.browser.getControl("Final Submit").click()
803        self.assertTrue(
804            'The requested form is locked' in self.browser.contents)
805        return
806
807    def test_locking(self):
808        # Make sure that locked forms can't be submitted
809        self.login()
810        self.browser.open(self.edit_path)
811        self.fill_correct_values() # fill other fields with correct values
812        # Create a pseudo image file and select it to be uploaded in form
813        pseudo_image = StringIO('I pretend to be a graphics file')
814        ctrl = self.browser.getControl(name='form.passport')
815        file_ctrl = ctrl.mech_control
816        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
817        self.browser.getControl("Save").click()
818        # Now we lock the form
819        self.applicant.locked = True
820        self.browser.open(self.edit_path)
821        self.assertEqual(self.browser.headers['Status'], '200 Ok')
822        self.assertTrue(
823            'The requested form is locked' in self.browser.contents)
824        return
825
826    def test_certificate_removed(self):
827        self.login()
828        self.browser.open(self.edit_path)
829        self.fill_correct_values()
830        self.browser.getControl("Save").click()
831        self.browser.open(self.view_path)
832        self.assertTrue(
833            'Unnamed Certificate' in self.browser.contents)
834        self.browser.open(self.edit_path)
835        self.assertTrue(
836            '<option selected="selected" value="CERT1">' in self.browser.contents)
837        # Now we remove the certificate
838        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
839        # The certificate is still shown in display mode
840        self.browser.open(self.view_path)
841        self.assertTrue(
842            'Unnamed Certificate' in self.browser.contents)
843        # The certificate is still selectable in edit mode so that it won't
844        # be automatically replaced by another (arbitrary) certificate
845        self.browser.open(self.edit_path)
846        self.assertTrue(
847            '<option selected="selected" value="CERT1">' in self.browser.contents)
848        # Consequently, the certificate is still shown after saving the form
849        self.browser.getControl("Save").click()
850        self.browser.open(self.view_path)
851        self.assertTrue(
852            'Unnamed Certificate' in self.browser.contents)
853        # Even if we add a new certificate the previous (removed)
854        # certificate is shown
855        certificate = createObject('waeup.Certificate')
856        certificate.code = 'CERT2'
857        certificate.title = 'New Certificate'
858        certificate.application_category = 'basic'
859        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
860            certificate)
861        self.browser.open(self.edit_path)
862        self.assertTrue(
863            '<option selected="selected" value="CERT1">'
864            in self.browser.contents)
865
866class ApplicantRegisterTests(ApplicantsFullSetup):
867    # Tests for applicant registration
868
869    layer = FunctionalLayer
870
871    def test_register_applicant_create(self):
872        # An applicant can register himself.
873        self.browser.open(self.container_path)
874        self.browser.getLink("Register for application").click()
875        # Fill the edit form with suitable values
876        self.browser.getControl(name="form.firstname").value = 'Anna'
877        self.browser.getControl(name="form.lastname").value = 'Kurios'
878        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
879        self.browser.getControl(name="form.phone.country").value = ['+234']
880        self.browser.getControl(name="form.phone.area").value = '555'
881        self.browser.getControl(name="form.phone.ext").value = '6666666'
882        self.browser.getControl("Get login credentials").click()
883        self.assertEqual(self.browser.url,
884            self.container_path + '/registration_complete?email=xx%40yy.zz')
885        # The new applicant can be found in the catalog via the email address
886        cat = getUtility(ICatalog, name='applicants_catalog')
887        results = list(
888            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
889        applicant = results[0]
890        self.assertEqual(applicant.lastname,'Kurios')
891        # The application_id has been copied to the reg_number
892        self.assertEqual(applicant.applicant_id, applicant.reg_number)
893        # The applicant can be found in the catalog via the reg_number
894        results = list(
895            cat.searchResults(
896            reg_number=(applicant.reg_number, applicant.reg_number)))
897        self.assertEqual(applicant,results[0])
898        return
899
900    def test_register_applicant_update(self):
901        # We change the application mode and check if applicants
902        # can find and update imported records instead of creating new records.
903        # First we check what happens if record does not exist.
904        self.applicantscontainer.mode = 'update'
905        self.browser.open(self.container_path + '/register')
906        self.browser.getControl(name="form.firstname").value = 'John'
907        self.browser.getControl(name="form.reg_number").value = 'anynumber'
908        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
909        self.browser.getControl("Get login credentials").click()
910        self.assertTrue('No application record found.'
911            in self.browser.contents)
912        # Even with the correct reg_number we can't register
913        # because firstname attribute is not set.
914        self.applicantscontainer.mode = 'update'
915        self.browser.open(self.container_path + '/register')
916        self.browser.getControl(name="form.firstname").value = 'John'
917        self.browser.getControl(name="form.reg_number").value = '1234'
918        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
919        self.browser.getControl("Get login credentials").click()
920        self.assertTrue('An error occurred.' in self.browser.contents)
921        # Let's set this attribute manually
922        # and try to register with a wrong name.
923        self.applicant.firstname = u'John'
924        self.browser.open(self.container_path + '/register')
925        self.browser.getControl(name="form.firstname").value = 'Johnny'
926        self.browser.getControl(name="form.reg_number").value = '1234'
927        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
928        self.browser.getControl("Get login credentials").click()
929        # Anonymous is not informed that firstname verification failed.
930        # It seems that the record doesn't exist.
931        self.assertTrue('No application record found.'
932            in self.browser.contents)
933        # Even with the correct firstname we can't register because
934        # password has already been set.
935        self.browser.getControl(name="form.firstname").value = 'John'
936        self.browser.getControl(name="form.reg_number").value = '1234'
937        self.browser.getControl("Get login credentials").click()
938        self.assertTrue('Your password has already been set.'
939            in self.browser.contents)
940        # We unset the password and try to register again.
941        IUserAccount(
942            self.app['applicants']['app2009'][
943            self.applicant.application_number]).context.password = None
944        self.browser.open(self.container_path + '/register')
945        # The firstname field, used for verification, is not case-sensitive.
946        self.browser.getControl(name="form.firstname").value = 'jOhn'
947        self.browser.getControl(name="form.reg_number").value = '1234'
948        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
949        self.browser.getControl("Get login credentials").click()
950        # Yeah, we succeded ...
951        self.assertTrue('Your registration was successful.'
952            in self.browser.contents)
953        # ... and  applicant can be found in the catalog via the email address
954        cat = getUtility(ICatalog, name='applicants_catalog')
955        results = list(
956            cat.searchResults(
957            email=('xx@yy.zz', 'xx@yy.zz')))
958        self.assertEqual(self.applicant,results[0])
959        return
960
961    def test_register_applicant_wo_phone(self):
962        # We don't require the phone number when registering
963        self.browser.open(self.container_path)
964        self.browser.getLink("Register for application").click()
965        # Fill the edit form with suitable values
966        self.browser.getControl(name="form.firstname").value = 'John'
967        self.browser.getControl(name="form.lastname").value = 'Tester'
968        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
969        self.browser.getControl("Get login credentials").click()
970        self.assertEqual(self.browser.url,
971            self.container_path + '/registration_complete?email=xx%40yy.zz')
972        return
973
974    def test_change_password_request(self):
975        self.browser.open('http://localhost/app/sendpw')
976        self.browser.getControl(name="form.identifier").value = '1234'
977        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
978        self.browser.getControl("Get login credentials").click()
979        self.assertTrue('No record found' in self.browser.contents)
980        self.applicant.email = 'aa@aa.ng'
981        # Update the catalog
982        notify(grok.ObjectModifiedEvent(self.applicant))
983        self.browser.open('http://localhost/app/sendpw')
984        self.browser.getControl(name="form.identifier").value = '1234'
985        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
986        self.browser.getControl("Get login credentials").click()
987        self.assertTrue(
988            'An email with your user name and password has been sent'
989            in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.