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

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

Do not block application record if password has been set for the first time and
let students re-use the ApplicantRegistrationPage? in case they have entered a wrong
email address.

  • Property svn:keywords set to Id
File size: 48.1 KB
Line 
1## $Id: test_browser.py 8627 2012-06-05 06:04:00Z 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.assertTrue(IUserAccount(self.applicant).checkPassword('apwd'))
492        self.browser.getControl("Save").click()
493        self.assertMatches('...Form has been saved...', self.browser.contents)
494        # Applicants don't see manage and search links ...
495        self.browser.open(self.root_path)
496        self.assertEqual(self.browser.headers['Status'], '200 Ok')
497        self.assertFalse('Search' in self.browser.contents)
498        self.assertFalse('Manage application section' in self.browser.contents)
499        # ... and can't access the manage page
500        self.assertRaises(
501            Unauthorized, self.browser.open, self.manage_path)
502        return
503
504    def image_url(self, filename):
505        return self.edit_path.replace('edit', filename)
506
507    def test_after_login_default_browsable(self):
508        # After login we see the placeholder image in the edit view
509        self.login()
510        self.assertEqual(self.browser.url, self.view_path)
511        self.browser.open(self.edit_path)
512        # There is a correct <img> link included
513        self.assertTrue(
514              '<img src="passport.jpg" height="180px" />' in self.browser.contents)
515        # Browsing the link shows a real image
516        self.browser.open(self.image_url('passport.jpg'))
517        self.assertEqual(
518            self.browser.headers['content-type'], 'image/jpeg')
519        self.assertEqual(len(self.browser.contents), PH_LEN)
520
521    def test_after_submit_default_browsable(self):
522        # After submitting an applicant form the default image is
523        # still visible
524        self.login()
525        self.browser.open(self.edit_path)
526        self.browser.getControl("Save").click() # submit form
527        # There is a correct <img> link included
528        self.assertTrue(
529            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
530        # Browsing the link shows a real image
531        self.browser.open(self.image_url('passport.jpg'))
532        self.assertEqual(
533            self.browser.headers['content-type'], 'image/jpeg')
534        self.assertEqual(len(self.browser.contents), PH_LEN)
535
536    def test_uploaded_image_respects_file_size_restriction(self):
537        # When we upload an image that is too big ( > 10 KB) we will
538        # get an error message
539        self.login()
540        self.browser.open(self.edit_path)
541        # Create a pseudo image file and select it to be uploaded in form
542        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
543        pseudo_image = StringIO(photo_content)
544        ctrl = self.browser.getControl(name='form.passport')
545        file_ctrl = ctrl.mech_control
546        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
547        self.browser.getControl("Save").click() # submit form
548        # There is a correct <img> link included
549        self.assertTrue(
550            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
551        # We get a warning message
552        self.assertTrue(
553            'Uploaded image is too big' in self.browser.contents)
554        # Browsing the image gives us the default image, not the
555        # uploaded one.
556        self.browser.open(self.image_url('passport.jpg'))
557        self.assertEqual(
558            self.browser.headers['content-type'], 'image/jpeg')
559        self.assertEqual(len(self.browser.contents), PH_LEN)
560        # There is really no file stored for the applicant
561        img = getUtility(IExtFileStore).getFile(
562            IFileStoreNameChooser(self.applicant).chooseName())
563        self.assertTrue(img is None)
564
565    def test_uploaded_image_browsable_w_errors(self):
566        # We can upload a different image and browse it after submit,
567        # even if there are still errors in the form
568        self.login()
569        self.browser.open(self.edit_path)
570        # Create a pseudo image file and select it to be uploaded in form
571        photo_content = 'I pretend to be a graphics file'
572        pseudo_image = StringIO(photo_content)
573        ctrl = self.browser.getControl(name='form.passport')
574        file_ctrl = ctrl.mech_control
575        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
576        self.browser.getControl("Save").click() # submit form
577        # There is a correct <img> link included
578        self.assertTrue(
579            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
580        # Browsing the link shows a real image
581        self.browser.open(self.image_url('passport.jpg'))
582        self.assertEqual(
583            self.browser.headers['content-type'], 'image/jpeg')
584        self.assertEqual(self.browser.contents, photo_content)
585
586    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
587        # After uploading a new passport pic the file is correctly
588        # stored in an imagestorage
589        self.login()
590        self.browser.open(self.edit_path)
591        # Create a pseudo image file and select it to be uploaded in form
592        pseudo_image = StringIO('I pretend to be a graphics file')
593        ctrl = self.browser.getControl(name='form.passport')
594        file_ctrl = ctrl.mech_control
595        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
596        self.browser.getControl("Save").click() # submit form
597        storage = getUtility(IExtFileStore)
598        file_id = IFileStoreNameChooser(self.applicant).chooseName()
599        pseudo_image.seek(0) # reset our file data source
600        self.assertEqual(
601            storage.getFile(file_id).read(), pseudo_image.read())
602        return
603
604    def test_uploaded_image_browsable_wo_errors(self):
605        # We can upload a different image and browse it after submit,
606        # if there are no errors in form
607        self.login()
608        self.browser.open(self.edit_path)
609        self.fill_correct_values() # fill other fields with correct values
610        # Create a pseudo image file and select it to be uploaded in form
611        pseudo_image = StringIO('I pretend to be a graphics file')
612        ctrl = self.browser.getControl(name='form.passport')
613        file_ctrl = ctrl.mech_control
614        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
615        self.browser.getControl("Save").click() # submit form
616        # There is a correct <img> link included
617        self.assertTrue(
618            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
619        # Browsing the link shows a real image
620        self.browser.open(self.image_url('passport.jpg'))
621        self.assertEqual(
622            self.browser.headers['content-type'], 'image/jpeg')
623        self.assertEqual(len(self.browser.contents), 31)
624
625    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
626        # After uploading a new passport pic the file is correctly
627        # stored in an imagestorage if form contains no errors
628        self.login()
629        self.browser.open(self.edit_path)
630        self.fill_correct_values() # fill other fields with correct values
631        # Create a pseudo image file and select it to be uploaded in form
632        pseudo_image = StringIO('I pretend to be a graphics file')
633        ctrl = self.browser.getControl(name='form.passport')
634        file_ctrl = ctrl.mech_control
635        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
636        self.browser.getControl("Save").click() # submit form
637        storage = getUtility(IExtFileStore)
638        file_id = IFileStoreNameChooser(self.applicant).chooseName()
639        # The stored image can be fetched
640        fd = storage.getFile(file_id)
641        file_len = len(fd.read())
642        self.assertEqual(file_len, 31)
643
644    def test_uploaded_images_equal(self):
645        # Make sure uploaded images do really differ if we eject a
646        # change notfication (and do not if we don't)
647        self.login()
648        self.browser.open(self.edit_path)
649        self.fill_correct_values() # fill other fields with correct values
650        self.browser.getControl("Save").click() # submit form
651        # Now go on as an officer
652        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
653        self.browser.open(self.manage_path)
654
655        # Create a pseudo image file and select it to be uploaded in form
656        pseudo_image = StringIO('I pretend to be a graphics file')
657        ctrl = self.browser.getControl(name='form.passport')
658        file_ctrl = ctrl.mech_control
659        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
660        file_id = IFileStoreNameChooser(self.applicant).chooseName()
661        setSite(self.app)
662        passport0 = getUtility(IExtFileStore).getFile(file_id)
663        self.browser.getControl("Save").click() # submit form with changed pic
664        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
665        self.browser.getControl("Save").click() # submit form w/o changes
666        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
667        self.assertTrue(passport0 is None)
668        self.assertTrue(passport0 != passport1)
669        self.assertTrue(passport1 == passport2)
670        return
671
672    def test_pay_application_fee(self):
673        self.login()
674        self.browser.open(self.edit_path)
675        # Payment tickets can't be created before the form has been validated
676        self.browser.getControl("Add online payment ticket").click()
677        self.assertTrue('Required input is missing' in self.browser.contents)
678        self.fill_correct_values()
679        # We have to save the form otherwise the filled fields will be cleared
680        # after adding an online payment, because adding an online payment
681        # requires a filled form but does not save it
682        self.browser.getControl("Save").click()
683        self.browser.getControl("Add online payment ticket").click()
684        # Session object missing
685        self.assertTrue(
686            'Session configuration object is not available'
687            in self.browser.contents)
688        configuration = SessionConfiguration()
689        configuration.academic_session = 2009
690        configuration.application_fee = 200.0
691        self.app['configuration'].addSessionConfiguration(configuration)
692        self.browser.open(self.edit_path)
693        self.browser.getControl("Add online payment ticket").click()
694        self.assertMatches('...Payment ticket created...',
695                           self.browser.contents)
696        self.assertMatches('...Activation Code...',
697                           self.browser.contents)
698        # Payment ticket can be removed if they haven't received a
699        # valid callback
700        self.browser.open(self.edit_path)
701        ctrl = self.browser.getControl(name='val_id')
702        value = ctrl.options[0]
703        ctrl.getControl(value=value).selected = True
704        self.browser.getControl("Remove selected", index=0).click()
705        self.assertMatches('...Successfully removed...', self.browser.contents)
706        # We will try the callback request view
707        self.browser.getControl("Add online payment ticket").click()
708        self.browser.open(self.edit_path)
709        ctrl = self.browser.getControl(name='val_id')
710        value = ctrl.options[0]
711        self.browser.getLink(value).click()
712        self.assertMatches('...Amount Authorized...',
713                           self.browser.contents)
714        payment_url = self.browser.url
715        payment_id = self.applicant.keys()[0]
716        payment = self.applicant[payment_id]
717        self.assertEqual(payment.p_item,'This is the app2009 container')
718        self.assertEqual(payment.p_session,2009)
719        self.assertEqual(payment.p_category,'application')
720        self.assertEqual(payment.amount_auth,200.0)
721        # The pdf payment slip can't yet be opened
722        #self.browser.open(payment_url + '/payment_receipt.pdf')
723        #self.assertMatches('...Ticket not yet paid...',
724        #                   self.browser.contents)
725        # Approve payment
726        # Applicants can't approve payments
727        self.assertRaises(
728            Unauthorized, self.browser.open, payment_url + '/approve')
729        # We approve the payment by bypassing the view
730        payment.approve()
731        # The payment slip can be downloaded now
732        self.browser.open(payment_url)
733        self.browser.getLink("Download payment slip").click()
734        self.assertEqual(self.browser.headers['Status'], '200 Ok')
735        self.assertEqual(self.browser.headers['Content-Type'],
736                         'application/pdf')
737        # Applicant is is not yet in state 'paid' because it was only
738        # the payment which we set to paid
739        self.browser.open(self.view_path)
740        self.assertMatches('...started...',
741                           self.browser.contents)
742        state = IWorkflowState(self.applicant).getState()
743        self.assertTrue(state == 'started')
744        # Let's logout and approve the payment as manager
745        self.browser.getLink("Logout").click()
746        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
747        # First we reset the payment
748        payment.r_amount_approved = 0.0
749        payment.r_code = u''
750        payment.p_state = 'unpaid'
751        payment.r_desc = u''
752        payment.payment_date = None
753        self.browser.open(payment_url)
754        self.browser.getLink("Approve payment").click()
755        self.assertEqual(payment.p_state, 'paid')
756        self.assertEqual(payment.r_amount_approved, 200.0)
757        self.assertEqual(payment.r_code, 'AP')
758        state = IWorkflowState(self.applicant).getState()
759        self.assertTrue(state == 'paid')
760        return
761
762    def test_final_submit(self):
763        # Make sure that a correctly filled form with passport picture
764        # can be submitted (only) after payment
765        self.login()
766        self.browser.getLink("Edit application record").click()
767        self.assertFalse('Final Submit' in self.browser.contents)
768        IWorkflowInfo(self.applicant).fireTransition('pay')
769        self.browser.open(self.edit_path)
770        self.assertTrue('Final Submit' in self.browser.contents)
771        self.fill_correct_values() # fill other fields with correct values
772        self.browser.getControl("Final Submit").click()
773        # We forgot to upload a passport picture
774        self.assertTrue(
775            'No passport picture uploaded' in self.browser.contents)
776        # Create a pseudo image file and select it to be uploaded in form
777        pseudo_image = StringIO('I pretend to be a graphics file')
778        ctrl = self.browser.getControl(name='form.passport')
779        file_ctrl = ctrl.mech_control
780        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
781        self.browser.getControl("Final Submit").click() # (finally) submit form
782        # The picture has been uploaded but the form cannot be submitted
783        # since the passport confirmation box was not ticked
784        self.assertTrue(
785            'Passport picture confirmation box not ticked'
786            in self.browser.contents)
787        self.browser.getControl(name="confirm_passport").value = True
788        self.applicantscontainer.enddate = datetime.now(pytz.utc)
789        self.browser.getControl("Final Submit").click()
790        self.assertTrue(
791            'Application period has expired' in self.browser.contents)
792        self.applicantscontainer.enddate = datetime.now(
793            pytz.utc) + timedelta(days=10)
794        self.browser.getControl(name="confirm_passport").value = True
795        self.browser.getControl("Final Submit").click()
796        self.assertTrue(
797            '... submitted ...' in self.browser.contents)
798        self.browser.goBack(count=1)
799        self.browser.getControl("Save").click()
800        self.assertTrue(
801            'The requested form is locked' in self.browser.contents)
802        self.browser.goBack(count=1)
803        self.browser.getControl("Final Submit").click()
804        self.assertTrue(
805            'The requested form is locked' in self.browser.contents)
806        return
807
808    def test_locking(self):
809        # Make sure that locked forms can't be submitted
810        self.login()
811        self.browser.open(self.edit_path)
812        self.fill_correct_values() # fill other fields with correct values
813        # Create a pseudo image file and select it to be uploaded in form
814        pseudo_image = StringIO('I pretend to be a graphics file')
815        ctrl = self.browser.getControl(name='form.passport')
816        file_ctrl = ctrl.mech_control
817        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
818        self.browser.getControl("Save").click()
819        # Now we lock the form
820        self.applicant.locked = True
821        self.browser.open(self.edit_path)
822        self.assertEqual(self.browser.headers['Status'], '200 Ok')
823        self.assertTrue(
824            'The requested form is locked' in self.browser.contents)
825        return
826
827    def test_certificate_removed(self):
828        self.login()
829        self.browser.open(self.edit_path)
830        self.fill_correct_values()
831        self.browser.getControl("Save").click()
832        self.browser.open(self.view_path)
833        self.assertTrue(
834            'Unnamed Certificate' in self.browser.contents)
835        self.browser.open(self.edit_path)
836        self.assertTrue(
837            '<option selected="selected" value="CERT1">' in self.browser.contents)
838        # Now we remove the certificate
839        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
840        # The certificate is still shown in display mode
841        self.browser.open(self.view_path)
842        self.assertTrue(
843            'Unnamed Certificate' in self.browser.contents)
844        # The certificate is still selectable in edit mode so that it won't
845        # be automatically replaced by another (arbitrary) certificate
846        self.browser.open(self.edit_path)
847        self.assertTrue(
848            '<option selected="selected" value="CERT1">' in self.browser.contents)
849        # Consequently, the certificate is still shown after saving the form
850        self.browser.getControl("Save").click()
851        self.browser.open(self.view_path)
852        self.assertTrue(
853            'Unnamed Certificate' in self.browser.contents)
854        # Even if we add a new certificate the previous (removed)
855        # certificate is shown
856        certificate = createObject('waeup.Certificate')
857        certificate.code = 'CERT2'
858        certificate.title = 'New Certificate'
859        certificate.application_category = 'basic'
860        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
861            certificate)
862        self.browser.open(self.edit_path)
863        self.assertTrue(
864            '<option selected="selected" value="CERT1">'
865            in self.browser.contents)
866
867class ApplicantRegisterTests(ApplicantsFullSetup):
868    # Tests for applicant registration
869
870    layer = FunctionalLayer
871
872    def test_register_applicant_create(self):
873        # An applicant can register himself.
874        self.browser.open(self.container_path)
875        self.browser.getLink("Register for application").click()
876        # Fill the edit form with suitable values
877        self.browser.getControl(name="form.firstname").value = 'Anna'
878        self.browser.getControl(name="form.lastname").value = 'Kurios'
879        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
880        self.browser.getControl(name="form.phone.country").value = ['+234']
881        self.browser.getControl(name="form.phone.area").value = '555'
882        self.browser.getControl(name="form.phone.ext").value = '6666666'
883        self.browser.getControl("Get login credentials").click()
884        self.assertEqual(self.browser.url,
885            self.container_path + '/registration_complete?email=xx%40yy.zz')
886        # The new applicant can be found in the catalog via the email address
887        cat = getUtility(ICatalog, name='applicants_catalog')
888        results = list(
889            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
890        applicant = results[0]
891        self.assertEqual(applicant.lastname,'Kurios')
892        # The application_id has been copied to the reg_number
893        self.assertEqual(applicant.applicant_id, applicant.reg_number)
894        # The applicant can be found in the catalog via the reg_number
895        results = list(
896            cat.searchResults(
897            reg_number=(applicant.reg_number, applicant.reg_number)))
898        self.assertEqual(applicant,results[0])
899        return
900
901    def test_register_applicant_update(self):
902        # We change the application mode and check if applicants
903        # can find and update imported records instead of creating new records.
904        # First we check what happens if record does not exist.
905        self.applicantscontainer.mode = 'update'
906        self.browser.open(self.container_path + '/register')
907        self.browser.getControl(name="form.firstname").value = 'John'
908        self.browser.getControl(name="form.reg_number").value = 'anynumber'
909        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
910        self.browser.getControl("Get login credentials").click()
911        self.assertTrue('No application record found.'
912            in self.browser.contents)
913        # Even with the correct reg_number we can't register
914        # because firstname attribute is not set.
915        self.applicantscontainer.mode = 'update'
916        self.browser.open(self.container_path + '/register')
917        self.browser.getControl(name="form.firstname").value = 'John'
918        self.browser.getControl(name="form.reg_number").value = '1234'
919        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
920        self.browser.getControl("Get login credentials").click()
921        self.assertTrue('An error occurred.' in self.browser.contents)
922        # Let's set this attribute manually
923        # and try to register with a wrong name.
924        self.applicant.firstname = u'John'
925        self.browser.open(self.container_path + '/register')
926        self.browser.getControl(name="form.firstname").value = 'Johnny'
927        self.browser.getControl(name="form.reg_number").value = '1234'
928        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
929        self.browser.getControl("Get login credentials").click()
930        # Anonymous is not informed that firstname verification failed.
931        # It seems that the record doesn't exist.
932        self.assertTrue('No application record found.'
933            in self.browser.contents)
934        # Even with the correct firstname we can't register if a
935        # password has been set and used.
936        IWorkflowState(self.applicant).setState('started')
937        self.browser.getControl(name="form.firstname").value = 'John'
938        self.browser.getControl(name="form.reg_number").value = '1234'
939        self.browser.getControl("Get login credentials").click()
940        self.assertTrue('Your password has already been set and used.'
941            in self.browser.contents)
942        #IUserAccount(
943        #    self.app['applicants']['app2009'][
944        #    self.applicant.application_number]).context.password = None
945        # Even without unsetting the password we can re-register if state
946        # is 'initialized'
947        IWorkflowState(self.applicant).setState('initialized')
948        self.browser.open(self.container_path + '/register')
949        # The firstname field, used for verification, is not case-sensitive.
950        self.browser.getControl(name="form.firstname").value = 'jOhn'
951        self.browser.getControl(name="form.reg_number").value = '1234'
952        self.browser.getControl(name="form.email").value = 'new@yy.zz'
953        self.browser.getControl("Get login credentials").click()
954        # Yeah, we succeded ...
955        self.assertTrue('Your registration was successful.'
956            in self.browser.contents)
957        # ... and  applicant can be found in the catalog via the email address
958        cat = getUtility(ICatalog, name='applicants_catalog')
959        results = list(
960            cat.searchResults(
961            email=('new@yy.zz', 'new@yy.zz')))
962        self.assertEqual(self.applicant,results[0])
963        return
964
965    def test_register_applicant_wo_phone(self):
966        # We don't require the phone number when registering
967        self.browser.open(self.container_path)
968        self.browser.getLink("Register for application").click()
969        # Fill the edit form with suitable values
970        self.browser.getControl(name="form.firstname").value = 'John'
971        self.browser.getControl(name="form.lastname").value = 'Tester'
972        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
973        self.browser.getControl("Get login credentials").click()
974        self.assertEqual(self.browser.url,
975            self.container_path + '/registration_complete?email=xx%40yy.zz')
976        return
977
978    def test_change_password_request(self):
979        self.browser.open('http://localhost/app/sendpw')
980        self.browser.getControl(name="form.identifier").value = '1234'
981        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
982        self.browser.getControl("Get login credentials").click()
983        self.assertTrue('No record found' in self.browser.contents)
984        self.applicant.email = 'aa@aa.ng'
985        # Update the catalog
986        notify(grok.ObjectModifiedEvent(self.applicant))
987        self.browser.open('http://localhost/app/sendpw')
988        self.browser.getControl(name="form.identifier").value = '1234'
989        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
990        self.browser.getControl("Get login credentials").click()
991        self.assertTrue(
992            'An email with your user name and password has been sent'
993            in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.