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

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

Add test.

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