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

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

Add methods for approving payments and implement pages for approving payments (work in progress).

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