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

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

Use better class names. The PaymentWebservice? adapter is not a webservice. KofaPaymentRequest? is not a request but a view for a simple web service.

We do not publish the data of the payee but of the payer.

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