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

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

Fix typo and remove useless test.

  • Property svn:keywords set to Id
File size: 49.3 KB
Line 
1## $Id: test_browser.py 8778 2012-06-23 05:26:09Z 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_access(self):
496        # Applicants can edit their record
497        self.browser.open(self.login_path)
498        self.login()
499        self.browser.open(self.edit_path)
500        self.assertTrue(self.browser.url != self.login_path)
501        self.assertEqual(self.browser.headers['Status'], '200 Ok')
502        self.fill_correct_values()
503        self.assertTrue(IUserAccount(self.applicant).checkPassword('apwd'))
504        self.browser.getControl("Save").click()
505        self.assertMatches('...Form has been saved...', self.browser.contents)
506        # Applicants don't see manage and search links ...
507        self.browser.open(self.root_path)
508        self.assertEqual(self.browser.headers['Status'], '200 Ok')
509        self.assertFalse('Search' in self.browser.contents)
510        self.assertFalse('Manage application section' in self.browser.contents)
511        # ... and can't access the manage page
512        self.assertRaises(
513            Unauthorized, self.browser.open, self.manage_path)
514        return
515
516    def image_url(self, filename):
517        return self.edit_path.replace('edit', filename)
518
519    def test_after_login_default_browsable(self):
520        # After login we see the placeholder image in the edit view
521        self.login()
522        self.assertEqual(self.browser.url, self.view_path)
523        self.browser.open(self.edit_path)
524        # There is a correct <img> link included
525        self.assertTrue(
526              '<img src="passport.jpg" height="180px" />' in self.browser.contents)
527        # Browsing the link shows a real image
528        self.browser.open(self.image_url('passport.jpg'))
529        self.assertEqual(
530            self.browser.headers['content-type'], 'image/jpeg')
531        self.assertEqual(len(self.browser.contents), PH_LEN)
532
533    def test_after_submit_default_browsable(self):
534        # After submitting an applicant form the default image is
535        # still visible
536        self.login()
537        self.browser.open(self.edit_path)
538        self.browser.getControl("Save").click() # submit form
539        # There is a correct <img> link included
540        self.assertTrue(
541            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
542        # Browsing the link shows a real image
543        self.browser.open(self.image_url('passport.jpg'))
544        self.assertEqual(
545            self.browser.headers['content-type'], 'image/jpeg')
546        self.assertEqual(len(self.browser.contents), PH_LEN)
547
548    def test_uploaded_image_respects_file_size_restriction(self):
549        # When we upload an image that is too big ( > 10 KB) we will
550        # get an error message
551        self.login()
552        self.browser.open(self.edit_path)
553        # Create a pseudo image file and select it to be uploaded in form
554        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
555        pseudo_image = StringIO(photo_content)
556        ctrl = self.browser.getControl(name='form.passport')
557        file_ctrl = ctrl.mech_control
558        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
559        self.browser.getControl("Save").click() # submit form
560        # There is a correct <img> link included
561        self.assertTrue(
562            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
563        # We get a warning message
564        self.assertTrue(
565            'Uploaded image is too big' in self.browser.contents)
566        # Browsing the image gives us the default image, not the
567        # uploaded one.
568        self.browser.open(self.image_url('passport.jpg'))
569        self.assertEqual(
570            self.browser.headers['content-type'], 'image/jpeg')
571        self.assertEqual(len(self.browser.contents), PH_LEN)
572        # There is really no file stored for the applicant
573        img = getUtility(IExtFileStore).getFile(
574            IFileStoreNameChooser(self.applicant).chooseName())
575        self.assertTrue(img is None)
576
577    def test_uploaded_image_browsable_w_errors(self):
578        # We can upload a different image and browse it after submit,
579        # even if there are still errors in the form
580        self.login()
581        self.browser.open(self.edit_path)
582        # Create a pseudo image file and select it to be uploaded in form
583        photo_content = 'I pretend to be a graphics file'
584        pseudo_image = StringIO(photo_content)
585        ctrl = self.browser.getControl(name='form.passport')
586        file_ctrl = ctrl.mech_control
587        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
588        self.browser.getControl("Save").click() # submit form
589        # There is a correct <img> link included
590        self.assertTrue(
591            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
592        # Browsing the link shows a real image
593        self.browser.open(self.image_url('passport.jpg'))
594        self.assertEqual(
595            self.browser.headers['content-type'], 'image/jpeg')
596        self.assertEqual(self.browser.contents, photo_content)
597
598    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
599        # After uploading a new passport pic the file is correctly
600        # stored in an imagestorage
601        self.login()
602        self.browser.open(self.edit_path)
603        # Create a pseudo image file and select it to be uploaded in form
604        pseudo_image = StringIO('I pretend to be a graphics file')
605        ctrl = self.browser.getControl(name='form.passport')
606        file_ctrl = ctrl.mech_control
607        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
608        self.browser.getControl("Save").click() # submit form
609        storage = getUtility(IExtFileStore)
610        file_id = IFileStoreNameChooser(self.applicant).chooseName()
611        pseudo_image.seek(0) # reset our file data source
612        self.assertEqual(
613            storage.getFile(file_id).read(), pseudo_image.read())
614        return
615
616    def test_uploaded_image_browsable_wo_errors(self):
617        # We can upload a different image and browse it after submit,
618        # if there are no errors in form
619        self.login()
620        self.browser.open(self.edit_path)
621        self.fill_correct_values() # fill other fields with correct values
622        # Create a pseudo image file and select it to be uploaded in form
623        pseudo_image = StringIO('I pretend to be a graphics file')
624        ctrl = self.browser.getControl(name='form.passport')
625        file_ctrl = ctrl.mech_control
626        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
627        self.browser.getControl("Save").click() # submit form
628        # There is a correct <img> link included
629        self.assertTrue(
630            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
631        # Browsing the link shows a real image
632        self.browser.open(self.image_url('passport.jpg'))
633        self.assertEqual(
634            self.browser.headers['content-type'], 'image/jpeg')
635        self.assertEqual(len(self.browser.contents), 31)
636
637    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
638        # After uploading a new passport pic the file is correctly
639        # stored in an imagestorage if form contains no errors
640        self.login()
641        self.browser.open(self.edit_path)
642        self.fill_correct_values() # fill other fields with correct values
643        # Create a pseudo image file and select it to be uploaded in form
644        pseudo_image = StringIO('I pretend to be a graphics file')
645        ctrl = self.browser.getControl(name='form.passport')
646        file_ctrl = ctrl.mech_control
647        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
648        self.browser.getControl("Save").click() # submit form
649        storage = getUtility(IExtFileStore)
650        file_id = IFileStoreNameChooser(self.applicant).chooseName()
651        # The stored image can be fetched
652        fd = storage.getFile(file_id)
653        file_len = len(fd.read())
654        self.assertEqual(file_len, 31)
655
656    def test_uploaded_images_equal(self):
657        # Make sure uploaded images do really differ if we eject a
658        # change notfication (and do not if we don't)
659        self.login()
660        self.browser.open(self.edit_path)
661        self.fill_correct_values() # fill other fields with correct values
662        self.browser.getControl("Save").click() # submit form
663        # Now go on as an officer
664        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
665        self.browser.open(self.manage_path)
666
667        # Create a pseudo image file and select it to be uploaded in form
668        pseudo_image = StringIO('I pretend to be a graphics file')
669        ctrl = self.browser.getControl(name='form.passport')
670        file_ctrl = ctrl.mech_control
671        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
672        file_id = IFileStoreNameChooser(self.applicant).chooseName()
673        setSite(self.app)
674        passport0 = getUtility(IExtFileStore).getFile(file_id)
675        self.browser.getControl("Save").click() # submit form with changed pic
676        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
677        self.browser.getControl("Save").click() # submit form w/o changes
678        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
679        self.assertTrue(passport0 is None)
680        self.assertTrue(passport0 != passport1)
681        self.assertTrue(passport1 == passport2)
682        return
683
684    def test_pay_application_fee(self):
685        self.login()
686        self.browser.open(self.edit_path)
687        # Payment tickets can't be created before the form has been validated
688        self.browser.getControl("Add online payment ticket").click()
689        self.assertTrue('Required input is missing' in self.browser.contents)
690        self.fill_correct_values()
691        # We have to save the form otherwise the filled fields will be cleared
692        # after adding an online payment, because adding an online payment
693        # requires a filled form but does not save it
694        self.browser.getControl("Save").click()
695        self.browser.getControl("Add online payment ticket").click()
696        # Session object missing
697        self.assertTrue(
698            'Session configuration object is not available'
699            in self.browser.contents)
700        configuration = SessionConfiguration()
701        configuration.academic_session = 2009
702        configuration.application_fee = 200.0
703        self.app['configuration'].addSessionConfiguration(configuration)
704        self.browser.open(self.edit_path)
705        self.browser.getControl("Add online payment ticket").click()
706        self.assertMatches('...Payment ticket created...',
707                           self.browser.contents)
708        self.assertMatches('...Activation Code...',
709                           self.browser.contents)
710        # Payment ticket can be removed if they haven't received a
711        # valid callback
712        self.browser.open(self.edit_path)
713        ctrl = self.browser.getControl(name='val_id')
714        value = ctrl.options[0]
715        ctrl.getControl(value=value).selected = True
716        self.browser.getControl("Remove selected", index=0).click()
717        self.assertMatches('...Successfully removed...', self.browser.contents)
718        # We will try the callback request view
719        self.browser.getControl("Add online payment ticket").click()
720        self.browser.open(self.edit_path)
721        ctrl = self.browser.getControl(name='val_id')
722        value = ctrl.options[0]
723        self.browser.getLink(value).click()
724        self.assertMatches('...Amount Authorized...',
725                           self.browser.contents)
726        payment_url = self.browser.url
727        payment_id = self.applicant.keys()[0]
728        payment = self.applicant[payment_id]
729        self.assertEqual(payment.p_item,'This is the app2009 container')
730        self.assertEqual(payment.p_session,2009)
731        self.assertEqual(payment.p_category,'application')
732        self.assertEqual(payment.amount_auth,200.0)
733        # Applicant is payee of the payment ticket.
734        self.assertEqual(
735            IPaymentWebservice(payment).display_fullname, 'John Tester')
736        self.assertEqual(
737            IPaymentWebservice(payment).id, self.applicant.applicant_id)
738        self.assertEqual(IPaymentWebservice(payment).faculty, '')
739        self.assertEqual(IPaymentWebservice(payment).department, '')
740        # The pdf payment slip can't yet be opened
741        #self.browser.open(payment_url + '/payment_receipt.pdf')
742        #self.assertMatches('...Ticket not yet paid...',
743        #                   self.browser.contents)
744        # Approve payment
745        # Applicants can't approve payments
746        self.assertRaises(
747            Unauthorized, self.browser.open, payment_url + '/approve')
748        # We approve the payment by bypassing the view
749        payment.approve()
750        # The payment slip can be downloaded now
751        self.browser.open(payment_url)
752        self.browser.getLink("Download payment slip").click()
753        self.assertEqual(self.browser.headers['Status'], '200 Ok')
754        self.assertEqual(self.browser.headers['Content-Type'],
755                         'application/pdf')
756        # Applicant is is not yet in state 'paid' because it was only
757        # the payment which we set to paid
758        self.browser.open(self.view_path)
759        self.assertMatches('...started...',
760                           self.browser.contents)
761        self.assertTrue(self.applicant.state == 'started')
762        # Let's logout and approve the payment as manager
763        self.browser.getLink("Logout").click()
764        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
765        # First we reset the payment
766        payment.r_amount_approved = 0.0
767        payment.r_code = u''
768        payment.p_state = 'unpaid'
769        payment.r_desc = u''
770        payment.payment_date = None
771        self.browser.open(payment_url)
772        self.browser.getLink("Approve payment").click()
773        self.assertEqual(payment.p_state, 'paid')
774        self.assertEqual(payment.r_amount_approved, 200.0)
775        self.assertEqual(payment.r_code, 'AP')
776        self.assertTrue(self.applicant.state == 'paid')
777        return
778
779    def test_final_submit(self):
780        # Make sure that a correctly filled form with passport picture
781        # can be submitted (only) after payment
782        self.login()
783        self.browser.getLink("Edit application record").click()
784        self.assertFalse('Final Submit' in self.browser.contents)
785        IWorkflowInfo(self.applicant).fireTransition('pay')
786        self.browser.open(self.edit_path)
787        self.assertTrue('Final Submit' in self.browser.contents)
788        self.fill_correct_values() # fill other fields with correct values
789        self.browser.getControl("Save").click()
790        self.browser.getControl("Final Submit").click()
791        # We forgot to upload a passport picture
792        self.assertTrue(
793            'No passport picture uploaded' in self.browser.contents)
794        # Create a pseudo image file and select it to be uploaded in form
795        pseudo_image = StringIO('I pretend to be a graphics file')
796        ctrl = self.browser.getControl(name='form.passport')
797        file_ctrl = ctrl.mech_control
798        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
799        self.browser.getControl("Final Submit").click() # (finally) submit form
800        # The picture has been uploaded but the form cannot be submitted
801        # since the passport confirmation box was not ticked
802        self.assertTrue(
803            'Passport picture confirmation box not ticked'
804            in self.browser.contents)
805        self.browser.getControl(name="confirm_passport").value = True
806        # If application period has expired and strict-deadline is set
807        # applicants do notsee edit button and can't open
808        # the edit form.
809        self.applicantscontainer.enddate = datetime.now(pytz.utc)
810        self.browser.open(self.view_path)
811        self.assertFalse(
812            'Edit application record' in self.browser.contents)
813        self.browser.open(self.edit_path)
814        self.assertTrue(
815            'form is locked' in self.browser.contents)
816        # We can either postpone the enddate ...
817        self.applicantscontainer.enddate = datetime.now(
818            pytz.utc) + timedelta(days=10)
819        self.browser.open(self.edit_path)
820        self.browser.getControl(name="confirm_passport").value = True
821        self.browser.getControl("Final Submit").click()
822        self.assertTrue(
823            '... submitted ...' in self.browser.contents)
824        # ... or allow submission after deadline.
825        IWorkflowState(self.applicant).setState('paid')
826        self.applicant.locked = False
827        self.applicantscontainer.strict_deadline = False
828        self.browser.open(self.edit_path)
829        self.browser.getControl(name="confirm_passport").value = True
830        self.browser.getControl("Final Submit").click()
831        self.assertTrue(
832            '... submitted ...' in self.browser.contents)
833        self.browser.goBack(count=1)
834        self.browser.getControl("Save").click()
835        # The form is locked.
836        self.assertTrue(
837            'The requested form is locked' in self.browser.contents)
838        self.browser.goBack(count=1)
839        self.browser.getControl("Final Submit").click()
840        self.assertTrue(
841            'The requested form is locked' in self.browser.contents)
842        return
843
844    def test_locking(self):
845        # Make sure that locked forms can't be submitted
846        self.login()
847        self.browser.open(self.edit_path)
848        self.fill_correct_values() # fill other fields with correct values
849        # Create a pseudo image file and select it to be uploaded in form
850        pseudo_image = StringIO('I pretend to be a graphics file')
851        ctrl = self.browser.getControl(name='form.passport')
852        file_ctrl = ctrl.mech_control
853        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
854        self.browser.getControl("Save").click()
855        # Now we lock the form
856        self.applicant.locked = True
857        self.browser.open(self.edit_path)
858        self.assertEqual(self.browser.headers['Status'], '200 Ok')
859        self.assertTrue(
860            'The requested form is locked' in self.browser.contents)
861        return
862
863    def test_certificate_removed(self):
864        self.login()
865        self.browser.open(self.edit_path)
866        self.fill_correct_values()
867        self.browser.getControl("Save").click()
868        self.browser.open(self.view_path)
869        self.assertTrue(
870            'Unnamed Certificate' in self.browser.contents)
871        self.browser.open(self.edit_path)
872        self.assertTrue(
873            '<option selected="selected" value="CERT1">' in self.browser.contents)
874        # Now we remove the certificate
875        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
876        # The certificate is still shown in display mode
877        self.browser.open(self.view_path)
878        self.assertTrue(
879            'Unnamed Certificate' in self.browser.contents)
880        # The certificate is still selectable in edit mode so that it won't
881        # be automatically replaced by another (arbitrary) certificate
882        self.browser.open(self.edit_path)
883        self.assertTrue(
884            '<option selected="selected" value="CERT1">' in self.browser.contents)
885        # Consequently, the certificate is still shown after saving the form
886        self.browser.getControl("Save").click()
887        self.browser.open(self.view_path)
888        self.assertTrue(
889            'Unnamed Certificate' in self.browser.contents)
890        # Even if we add a new certificate the previous (removed)
891        # certificate is shown
892        certificate = createObject('waeup.Certificate')
893        certificate.code = 'CERT2'
894        certificate.title = 'New Certificate'
895        certificate.application_category = 'basic'
896        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
897            certificate)
898        self.browser.open(self.edit_path)
899        self.assertTrue(
900            '<option selected="selected" value="CERT1">'
901            in self.browser.contents)
902
903class ApplicantRegisterTests(ApplicantsFullSetup):
904    # Tests for applicant registration
905
906    layer = FunctionalLayer
907
908    def test_register_applicant_create(self):
909        # An applicant can register himself.
910        self.browser.open(self.container_path)
911        self.browser.getLink("Register for application").click()
912        # Fill the edit form with suitable values
913        self.browser.getControl(name="form.firstname").value = 'Anna'
914        self.browser.getControl(name="form.lastname").value = 'Kurios'
915        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
916        self.browser.getControl(name="form.phone.country").value = ['+234']
917        self.browser.getControl(name="form.phone.area").value = '555'
918        self.browser.getControl(name="form.phone.ext").value = '6666666'
919        self.browser.getControl("Get login credentials").click()
920        self.assertEqual(self.browser.url,
921            self.container_path + '/registration_complete?email=xx%40yy.zz')
922        # The new applicant can be found in the catalog via the email address
923        cat = getUtility(ICatalog, name='applicants_catalog')
924        results = list(
925            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
926        applicant = results[0]
927        self.assertEqual(applicant.lastname,'Kurios')
928        # The application_id has been copied to the reg_number
929        self.assertEqual(applicant.applicant_id, applicant.reg_number)
930        # The applicant can be found in the catalog via the reg_number
931        results = list(
932            cat.searchResults(
933            reg_number=(applicant.reg_number, applicant.reg_number)))
934        self.assertEqual(applicant,results[0])
935        return
936
937    def test_register_applicant_update(self):
938        # We change the application mode and check if applicants
939        # can find and update imported records instead of creating new records.
940        # First we check what happens if record does not exist.
941        self.applicantscontainer.mode = 'update'
942        self.browser.open(self.container_path + '/register')
943        self.browser.getControl(name="form.firstname").value = 'John'
944        self.browser.getControl(name="form.reg_number").value = 'anynumber'
945        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
946        self.browser.getControl("Get login credentials").click()
947        self.assertTrue('No application record found.'
948            in self.browser.contents)
949        # Even with the correct reg_number we can't register
950        # because firstname attribute is not set.
951        self.applicantscontainer.mode = 'update'
952        self.browser.open(self.container_path + '/register')
953        self.browser.getControl(name="form.firstname").value = 'John'
954        self.browser.getControl(name="form.reg_number").value = '1234'
955        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
956        self.browser.getControl("Get login credentials").click()
957        self.assertTrue('An error occurred.' in self.browser.contents)
958        # Let's set this attribute manually
959        # and try to register with a wrong name.
960        self.applicant.firstname = u'John'
961        self.browser.open(self.container_path + '/register')
962        self.browser.getControl(name="form.firstname").value = 'Johnny'
963        self.browser.getControl(name="form.reg_number").value = '1234'
964        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
965        self.browser.getControl("Get login credentials").click()
966        # Anonymous is not informed that firstname verification failed.
967        # It seems that the record doesn't exist.
968        self.assertTrue('No application record found.'
969            in self.browser.contents)
970        # Even with the correct firstname we can't register if a
971        # password has been set and used.
972        IWorkflowState(self.applicant).setState('started')
973        self.browser.getControl(name="form.firstname").value = 'John'
974        self.browser.getControl(name="form.reg_number").value = '1234'
975        self.browser.getControl("Get login credentials").click()
976        self.assertTrue('Your password has already been set and used.'
977            in self.browser.contents)
978        #IUserAccount(
979        #    self.app['applicants']['app2009'][
980        #    self.applicant.application_number]).context.password = None
981        # Even without unsetting the password we can re-register if state
982        # is 'initialized'
983        IWorkflowState(self.applicant).setState('initialized')
984        self.browser.open(self.container_path + '/register')
985        # The firstname field, used for verification, is not case-sensitive.
986        self.browser.getControl(name="form.firstname").value = 'jOhn'
987        self.browser.getControl(name="form.reg_number").value = '1234'
988        self.browser.getControl(name="form.email").value = 'new@yy.zz'
989        self.browser.getControl("Get login credentials").click()
990        # Yeah, we succeded ...
991        self.assertTrue('Your registration was successful.'
992            in self.browser.contents)
993        # ... and  applicant can be found in the catalog via the email address
994        cat = getUtility(ICatalog, name='applicants_catalog')
995        results = list(
996            cat.searchResults(
997            email=('new@yy.zz', 'new@yy.zz')))
998        self.assertEqual(self.applicant,results[0])
999        return
1000
1001    def test_change_password_request(self):
1002        self.browser.open('http://localhost/app/changepw')
1003        self.browser.getControl(name="form.identifier").value = '1234'
1004        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1005        self.browser.getControl("Get login credentials").click()
1006        self.assertTrue('No record found' in self.browser.contents)
1007        self.applicant.email = 'aa@aa.ng'
1008        # Update the catalog
1009        notify(grok.ObjectModifiedEvent(self.applicant))
1010        self.browser.open('http://localhost/app/changepw')
1011        self.browser.getControl(name="form.identifier").value = '1234'
1012        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1013        self.browser.getControl("Get login credentials").click()
1014        self.assertTrue(
1015            'An email with your user name and password has been sent'
1016            in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.