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

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

Add application_fee field to ApplicantsContainers? (not used in base package).

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