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

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

Use applicants_catalog for statistics.

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