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

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

Add login tests.

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