source: main/waeup.kofa/branches/uli-rm-bootstrap/src/waeup/kofa/applicants/tests/test_browser.py @ 17660

Last change on this file since 17660 was 14949, checked in by Henrik Bettermann, 7 years ago

Display applicant id and error messages in flash message box if student creation has failed.

  • Property svn:keywords set to Id
File size: 86.8 KB
Line 
1## $Id: test_browser.py 14949 2018-02-09 09:17:58Z 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
26import logging
27from datetime import datetime
28from StringIO import StringIO
29from datetime import datetime, date, timedelta
30from mechanize import LinkNotFoundError
31from zc.async.testing import wait_for_result
32from zope.securitypolicy.interfaces import IPrincipalRoleManager
33from zope.event import notify
34from zope.catalog.interfaces import ICatalog
35from zope.component import createObject, getUtility
36from zope.component.hooks import setSite, clearSite
37from zope.security.interfaces import Unauthorized
38from zope.testbrowser.testing import Browser
39from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
40from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
41from waeup.kofa.app import University
42from waeup.kofa.payments.interfaces import IPayer
43from waeup.kofa.configuration import SessionConfiguration
44from waeup.kofa.refereeentries import RefereeEntry
45from waeup.kofa.applicants.container import ApplicantsContainer
46from waeup.kofa.applicants.applicant import Applicant
47from waeup.kofa.interfaces import (
48    IExtFileStore, IFileStoreNameChooser, IUserAccount, IJobManager)
49from waeup.kofa.university.faculty import Faculty
50from waeup.kofa.university.department import Department
51from waeup.kofa.mandates.mandate import RefereeReportMandate
52from waeup.kofa.tests.test_async import FunctionalAsyncTestCase
53
54PH_LEN = 15911  # Length of placeholder file
55
56session_1 = datetime.now().year - 2
57container_name_1 = u'app%s' % session_1
58session_2 = datetime.now().year - 1
59container_name_2 = u'app%s' % session_2
60
61SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
62
63class ApplicantsFullSetup(FunctionalTestCase):
64    # A test case that only contains a setup and teardown
65    #
66    # Complete setup for applicants handlings is rather complex and
67    # requires lots of things created before we can start. This is a
68    # setup that does all this, creates a university, creates PINs,
69    # etc.  so that we do not have to bother with that in different
70    # test cases.
71
72    layer = FunctionalLayer
73
74    def setUp(self):
75        super(ApplicantsFullSetup, self).setUp()
76
77        # Setup a sample site for each test
78        app = University()
79        self.dc_root = tempfile.mkdtemp()
80        app['datacenter'].setStoragePath(self.dc_root)
81
82        # Prepopulate the ZODB...
83        self.getRootFolder()['app'] = app
84        # we add the site immediately after creation to the
85        # ZODB. Catalogs and other local utilities are not setup
86        # before that step.
87        self.app = self.getRootFolder()['app']
88        # Set site here. Some of the following setup code might need
89        # to access grok.getSite() and should get our new app then
90        setSite(app)
91
92        self.login_path = 'http://localhost/app/login'
93        self.root_path = 'http://localhost/app/applicants'
94        self.search_path = 'http://localhost/app/applicants/search'
95        self.manage_root_path = self.root_path + '/@@manage'
96        self.add_container_path = self.root_path + '/@@add'
97        self.container_path = 'http://localhost/app/applicants/%s' % container_name_1
98        self.manage_container_path = self.container_path + '/@@manage'
99
100        # Add an applicants container
101        applicantscontainer = ApplicantsContainer()
102        applicantscontainer.code = container_name_1
103        applicantscontainer.prefix = 'app'
104        applicantscontainer.year = session_1
105        applicantscontainer.title = u'This is the %s container' % container_name_1
106        applicantscontainer.application_category = 'basic'
107        applicantscontainer.mode = 'create'
108        applicantscontainer.strict_deadline = True
109        delta = timedelta(days=10)
110        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
111        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
112        self.app['applicants'][container_name_1] = applicantscontainer
113        self.applicantscontainer = self.app['applicants'][container_name_1]
114
115        # Populate university
116        certificate = createObject('waeup.Certificate')
117        certificate.code = 'CERT1'
118        certificate.application_category = 'basic'
119        certificate.start_level = 100
120        certificate.end_level = 500
121        certificate.study_mode = u'ug_ft'
122        self.certificate = certificate
123        self.app['faculties']['fac1'] = Faculty()
124        # The code has explicitely to be set, otherwise we don't
125        # find created students in their department
126        self.app['faculties']['fac1']['dep1'] = Department(code=u'dep1')
127        self.department = self.app['faculties']['fac1']['dep1']
128        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
129            certificate)
130
131        # Put the prepopulated site into test ZODB and prepare test
132        # browser
133        self.browser = Browser()
134        self.browser.handleErrors = False
135
136        # Create 5 access codes with prefix'FOO' and cost 9.99 each
137        pin_container = self.app['accesscodes']
138        pin_container.createBatch(
139            datetime.now(), 'some_userid', 'APP', 9.99, 5)
140        pins = pin_container[pin_container.keys()[0]].values()
141        self.pins = [x.representation for x in pins]
142        self.existing_pin = self.pins[0]
143        parts = self.existing_pin.split('-')[1:]
144        self.existing_series, self.existing_number = parts
145
146        # Add an applicant
147        self.applicant = createObject('waeup.Applicant')
148        # reg_number is the only field which has to be preset here
149        # because managers are allowed to edit this required field
150        self.applicant.firstname = u'Joan'
151        self.applicant.reg_number = u'1234'
152        self.applicant.course1 = certificate
153        app['applicants'][container_name_1].addApplicant(self.applicant)
154        IUserAccount(
155            self.app['applicants'][container_name_1][
156            self.applicant.application_number]).setPassword('apwd')
157        self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % (
158            container_name_1, self.applicant.application_number, 'manage')
159        self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % (
160            container_name_1, self.applicant.application_number, 'edit')
161        self.view_path = 'http://localhost/app/applicants/%s/%s' % (
162            container_name_1, self.applicant.application_number)
163
164    def login(self):
165        # Perform an applicant login. This creates an application record.
166        #
167        # This helper also sets `self.applicant`, which is the
168        # applicant object created.
169        self.browser.open(self.login_path)
170        self.browser.getControl(
171            name="form.login").value = self.applicant.applicant_id
172        self.browser.getControl(name="form.password").value = 'apwd'
173        self.browser.getControl("Login").click()
174
175    def fill_correct_values(self):
176        # Fill the edit form with suitable values
177        self.browser.getControl(name="form.firstname").value = 'John'
178        self.browser.getControl(name="form.middlename").value = 'Anthony'
179        self.browser.getControl(name="form.lastname").value = 'Tester'
180        self.browser.getControl(name="form.course1").value = ['CERT1']
181        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
182        self.browser.getControl(name="form.sex").value = ['m']
183        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
184
185    def tearDown(self):
186        super(ApplicantsFullSetup, self).tearDown()
187        clearSite()
188        shutil.rmtree(self.dc_root)
189
190class ApplicantsRootUITests(ApplicantsFullSetup):
191    # Tests for ApplicantsRoot class
192
193    layer = FunctionalLayer
194
195    def test_anonymous_access(self):
196        # Anonymous users can access applicants root
197        self.browser.open(self.root_path)
198        self.assertEqual(self.browser.headers['Status'], '200 Ok')
199        self.assertFalse(
200            'Manage ' in self.browser.contents)
201        return
202
203    def test_anonymous_no_actions(self):
204        # Make sure anonymous users cannot access actions
205        self.browser.open(self.root_path)
206        self.assertRaises(
207            LookupError, self.browser.getControl, "Add local role")
208        # Manage screen neither linked nor accessible for anonymous
209        self.assertRaises(
210            LinkNotFoundError,
211            self.browser.getLink, 'Manage applicants section')
212        self.assertRaises(
213            Unauthorized, self.browser.open, self.manage_root_path)
214        # Add container screen not accessible for anonymous
215        self.assertRaises(
216            Unauthorized, self.browser.open, self.add_container_path)
217        return
218
219    def test_manage_access(self):
220        # Managers can access the manage pages of applicants root
221        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
222        self.browser.open(self.root_path)
223        self.assertTrue('Manage applicants section' in self.browser.contents)
224        # There is a manage link
225        link = self.browser.getLink('Manage applicants section')
226        link.click()
227        self.assertEqual(self.browser.headers['Status'], '200 Ok')
228        self.assertEqual(self.browser.url, self.manage_root_path)
229        return
230
231    def test_hide_container(self):
232        self.browser.open(self.root_path)
233        self.assertTrue(
234            '<a href="http://localhost/app/applicants/%s">'
235            'This is the %s container</a>' % (container_name_1, container_name_1)
236            in self.browser.contents)
237        self.app['applicants'][container_name_1].hidden = True
238        self.browser.open(self.root_path)
239        # Anonymous users can't see hidden containers
240        self.assertFalse(
241            '<a href="http://localhost/app/applicants/%s">'
242            'This is the %s container</a>' % (container_name_1, container_name_1)
243            in self.browser.contents)
244        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
245        self.browser.open(self.root_path)
246        self.assertTrue(
247            '<a href="http://localhost/app/applicants/%s">'
248            'This is the %s container</a>' % (container_name_1, container_name_1)
249            in self.browser.contents)
250        return
251
252    def test_search(self):
253        # Managers can access the manage pages of applicants root
254        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
255        self.browser.open(self.manage_path)
256        self.fill_correct_values()
257        self.browser.getControl("Save").click()
258        self.browser.open(self.root_path)
259        self.assertTrue('Manage applicants section' in self.browser.contents)
260        # There is a search link
261        link = self.browser.getLink('Find applicants')
262        link.click()
263        self.assertEqual(self.browser.headers['Status'], '200 Ok')
264        # We can find an applicant ...
265        # ... via his name
266        self.browser.getControl(name="searchtype").value = ['fullname']
267        self.browser.getControl(name="searchterm").value = 'John'
268        self.browser.getControl("Find applicant").click()
269        self.assertTrue('John Anthony Tester' in self.browser.contents)
270        self.browser.getControl(name="searchtype").value = ['fullname']
271        self.browser.getControl(name="searchterm").value = 'Tester'
272        self.browser.getControl("Find applicant").click()
273        self.assertTrue('John Anthony Tester' in self.browser.contents)
274        self.browser.open(self.search_path)
275        # ... and via his reg_number ...
276        self.browser.getControl(name="searchtype").value = ['reg_number']
277        self.browser.getControl(name="searchterm").value = '2345'
278        self.browser.getControl("Find applicant").click()
279        self.assertFalse('John Anthony Tester' in self.browser.contents)
280        self.browser.getControl(name="searchtype").value = ['reg_number']
281        self.browser.getControl(name="searchterm").value = '1234'
282        self.browser.getControl("Find applicant").click()
283        self.assertTrue('John Anthony Tester' in self.browser.contents)
284        # ... and not via his application_number ...
285        self.browser.getControl(name="searchtype").value = ['applicant_id']
286        self.browser.getControl(
287            name="searchterm").value = self.applicant.application_number
288        self.browser.getControl("Find applicant").click()
289        self.assertFalse('John Anthony Tester' in self.browser.contents)
290        # ... but ia his applicant_id ...
291        self.browser.getControl(name="searchtype").value = ['applicant_id']
292        self.browser.getControl(
293            name="searchterm").value = self.applicant.applicant_id
294        self.browser.getControl("Find applicant").click()
295        self.assertTrue('John Anthony Tester' in self.browser.contents)
296        # ... and via his email
297        self.browser.getControl(name="searchtype").value = ['email']
298        self.browser.getControl(name="searchterm").value = 'xx@yy.zz'
299        self.browser.getControl("Find applicant").click()
300        self.assertTrue('John Anthony Tester' in self.browser.contents)
301        return
302
303    def test_manage_actions_access(self):
304        # Managers can access the action on manage screen
305        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
306        self.browser.open(self.manage_root_path)
307        self.browser.getControl("Add local role").click()
308        self.assertTrue('No user selected' in self.browser.contents)
309        return
310
311    def test_local_roles_add_delete(self):
312        # Managers can assign and delete local roles of applicants root
313        myusers = self.app['users']
314        myusers.addUser('bob', 'bobssecret')
315        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
316        self.browser.open('http://localhost/app/faculties/fac1/dep1/manage')
317        self.browser.getControl(name="user").value = ['bob']
318        self.browser.getControl(name="local_role").value = [
319            'waeup.local.ApplicationsManager']
320        self.browser.getControl("Add local role").click()
321        self.assertTrue('<td>bob</td>' in self.browser.contents)
322        # Remove the role assigned
323        ctrl = self.browser.getControl(name='role_id')
324        ctrl.getControl(
325            value='bob|waeup.local.ApplicationsManager').selected = True
326        self.browser.getControl("Remove selected local roles").click()
327        self.assertTrue(
328            'Local role successfully removed: bob|waeup.local.ApplicationsManager'
329            in self.browser.contents)
330        self.assertFalse('<td>bob</td>' in self.browser.contents)
331        return
332
333    def test_add_delete_container(self):
334        # Managers can add and delete applicants containers
335        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
336        self.browser.open(self.manage_root_path)
337        self.browser.getControl("Add applicants container").click()
338        self.assertEqual(self.browser.headers['Status'], '200 Ok')
339        self.assertEqual(self.browser.url, self.add_container_path)
340        self.browser.getControl(name="form.prefix").value = ['app']
341        self.browser.getControl("Add applicants container").click()
342        self.assertTrue(
343            'There were errors' in self.browser.contents)
344        self.browser.getControl(name="form.prefix").value = ['app']
345        self.browser.getControl(name="form.year").value = [str(session_2)]
346        self.browser.getControl(name="form.mode").value = ['create']
347        self.browser.getControl(
348            name="form.application_category").value = ['basic']
349        self.browser.getControl("Add applicants container").click()
350        self.assertTrue('Added:' in self.browser.contents)
351        self.browser.getLink(container_name_1).click()
352        self.assertTrue('Manage applicants container'
353            in self.browser.contents)
354        self.browser.open(self.add_container_path)
355        self.browser.getControl("Cancel").click()
356        self.assertEqual(self.browser.url, self.manage_root_path)
357        self.browser.open(self.add_container_path)
358        self.browser.getControl(name="form.prefix").value = ['app']
359        self.browser.getControl(name="form.year").value = [str(session_2)]
360        self.browser.getControl(name="form.mode").value = ['create']
361        self.browser.getControl(
362            name="form.application_category").value = ['basic']
363        self.browser.getControl("Add applicants container").click()
364        self.assertTrue('exists already in the database'
365                        in self.browser.contents)
366        self.browser.open(self.manage_root_path)
367        ctrl = self.browser.getControl(name='val_id')
368        ctrl.getControl(value=container_name_2).selected = True
369        self.browser.getControl("Remove selected", index=0).click()
370        self.assertTrue('Successfully removed:' in self.browser.contents)
371        self.browser.open(self.add_container_path)
372        self.browser.getControl(name="form.prefix").value = ['app']
373        self.browser.getControl(name="form.year").value = [str(session_2)]
374        self.browser.getControl(name="form.mode").value = ['create']
375        #self.browser.getControl(name="form.ac_prefix").value = ['APP']
376        self.browser.getControl(
377            name="form.application_category").value = ['basic']
378        self.browser.getControl("Add applicants container").click()
379        del self.app['applicants'][container_name_2]
380        ctrl = self.browser.getControl(name='val_id')
381        ctrl.getControl(value=container_name_2).selected = True
382        self.browser.getControl("Remove selected", index=0).click()
383        self.assertMatches('...Could not delete...', self.browser.contents)
384        return
385
386class ApplicantsContainerUITests(ApplicantsFullSetup):
387    # Tests for ApplicantsContainer class views and pages
388
389    layer = FunctionalLayer
390
391    def test_anonymous_access(self):
392        # Anonymous users can access applicants containers
393        self.browser.open(self.container_path)
394        self.assertEqual(self.browser.headers['Status'], '200 Ok')
395        self.assertFalse(
396            'Manage ' in self.browser.contents)
397        return
398
399    def test_manage_access(self):
400        # Managers can access the manage pages of applicants
401        # containers and can perform actions
402        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
403        self.browser.open(self.manage_container_path)
404        self.assertEqual(self.browser.headers['Status'], '200 Ok')
405        self.assertEqual(self.browser.url, self.manage_container_path)
406        self.browser.getControl(name="form.application_fee").value = '200'
407        self.browser.getControl("Save").click()
408        self.assertTrue('Form has been saved' in self.browser.contents)
409        logfile = os.path.join(
410            self.app['datacenter'].storage, 'logs', 'applicants.log')
411        logcontent = open(logfile).read()
412        self.assertTrue(
413            'zope.mgr - applicants.browser.ApplicantsContainerManageFormPage - '
414            '%s - saved: application_fee\n' % container_name_1 in logcontent)
415        self.browser.getControl("Remove selected", index=0).click()
416        self.assertTrue('No applicant selected' in self.browser.contents)
417        self.browser.getControl("Add local role").click()
418        self.assertTrue('No user selected' in self.browser.contents)
419        self.browser.getControl("Cancel", index=0).click()
420        self.assertEqual(self.browser.url, self.container_path)
421        return
422
423    def test_statistics(self):
424        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
425        self.browser.open(self.container_path)
426        self.browser.getLink("Container statistics").click()
427        self.assertTrue('<td>initialized</td>' in self.browser.contents)
428        self.assertTrue('<td>1</td>' in self.browser.contents)
429        self.assertEqual(self.applicantscontainer.statistics[0],
430            {'not admitted': 0, 'started': 0, 'created': 0,
431            'admitted': 0, 'submitted': 0, 'initialized': 1,
432            'paid': 0, 'processed': 0})
433        #self.assertEqual(self.applicantscontainer.statistics[1],
434        #    {u'fac1': 0})
435        IWorkflowState(self.applicant).setState('submitted')
436        notify(grok.ObjectModifiedEvent(self.applicant))
437        self.assertEqual(self.applicantscontainer.statistics[0],
438            {'not admitted': 0, 'started': 0, 'created': 0,
439            'admitted': 0, 'submitted': 1, 'initialized': 0, 'paid': 0,
440            'processed': 0})
441        #self.assertEqual(self.applicantscontainer.statistics[1],
442        #    {u'fac1': 1})
443        return
444
445    def test_add_delete_applicants(self):
446        # Check the global role map first
447        role_manager = IPrincipalRoleManager(grok.getSite())
448        principals = role_manager.getPrincipalsForRole('waeup.Applicant')
449        self.assertEqual(len(principals), 1)
450        self.assertEqual(principals[0][0], self.applicant.applicant_id)
451        # Managers can add and delete applicants
452        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
453        self.add_applicant_path = self.container_path + '/addapplicant'
454        self.container_manage_path = self.container_path + '/@@manage'
455        self.browser.open(self.container_manage_path)
456        self.browser.getLink("Add applicant").click()
457        self.assertEqual(self.browser.headers['Status'], '200 Ok')
458        self.assertEqual(self.browser.url, self.add_applicant_path)
459        self.browser.getControl(name="form.firstname").value = 'Alois'
460        self.browser.getControl(name="form.middlename").value = 'Kofi'
461        self.browser.getControl(name="form.lastname").value = 'Bettermann'
462        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
463        self.browser.getControl("Create application record").click()
464        self.assertTrue('Application initialized' in self.browser.contents)
465        # The global role map has been extended
466        role_manager = IPrincipalRoleManager(grok.getSite())
467        principals = role_manager.getPrincipalsForRole('waeup.Applicant')
468        self.assertEqual(len(principals), 2)
469        self.browser.open(self.container_manage_path)
470        self.assertEqual(self.browser.headers['Status'], '200 Ok')
471        ctrl = self.browser.getControl(name='val_id')
472        value = ctrl.options[0]
473        ctrl.getControl(value=value).selected = True
474        self.browser.getControl("Remove selected", index=0).click()
475        self.assertTrue('Successfully removed:' in self.browser.contents)
476        # The global role map has been reduced
477        role_manager = IPrincipalRoleManager(grok.getSite())
478        principals = role_manager.getPrincipalsForRole('waeup.Applicant')
479        self.assertEqual(len(principals), 1)
480        self.browser.open(self.add_applicant_path)
481        self.browser.getControl(name="form.firstname").value = 'Albert'
482        self.browser.getControl(name="form.lastname").value = 'Einstein'
483        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
484        self.browser.getControl("Create application record").click()
485        self.assertTrue('Application initialized' in self.browser.contents)
486        return
487
488    def test_prefill_purge_container(self):
489        # Managers can pre-fill containers in create mode
490        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
491        self.prefill_path = self.container_path + '/prefill'
492        self.container_manage_path = self.container_path + '/@@manage'
493        self.browser.open(self.container_manage_path)
494        self.browser.getLink("Pre-fill").click()
495        self.assertEqual(self.browser.headers['Status'], '200 Ok')
496        self.assertEqual(self.browser.url, self.prefill_path)
497        self.browser.getControl(name="number").value = ['10']
498        self.browser.getControl("Pre-fill").click()
499        self.assertTrue('10 application records created.' in self.browser.contents)
500        self.browser.open(self.container_manage_path)
501        self.assertTrue('This container contains 10 unused pre-filled records.'
502            in self.browser.contents)
503        self.assertEqual(self.applicantscontainer.counts[0], 11)
504        self.assertEqual(self.applicantscontainer.counts[1], 1)
505        # In update mode we can't pre-fill the container
506        self.applicantscontainer.mode = 'update'
507        self.browser.open(self.container_manage_path)
508        self.browser.getLink("Pre-fill").click()
509        self.assertTrue('Container must be in create mode to be pre-filled.'
510            in self.browser.contents)
511        self.browser.open(self.manage_root_path)
512        # Number of total records is 11
513        self.assertTrue('<td>11</td>' in self.browser.contents)
514        # The statistics have not changed
515        self.browser.open(self.container_path)
516        self.browser.getLink("Container statistics").click()
517        self.assertTrue('<td>1</td>' in self.browser.contents)
518        self.assertEqual(self.applicantscontainer.statistics[0],
519            {'not admitted': 0, 'started': 0, 'created': 0,
520            'admitted': 0, 'submitted': 0, 'initialized': 1,
521            'paid': 0, 'processed': 0})
522        # Container can be purged
523        IWorkflowState(self.applicant).setState('submitted')
524        self.browser.open(self.container_manage_path)
525        self.browser.getLink("Purge").click()
526        self.browser.getControl("Remove").click()
527        self.assertTrue('10 application records purged' in self.browser.contents)
528        self.assertEqual(self.applicantscontainer.counts[0], 1)
529        self.assertEqual(self.applicantscontainer.counts[1], 1)
530        IWorkflowState(self.applicant).setState('initialized')
531        self.browser.open(self.container_manage_path)
532        self.browser.getLink("Purge").click()
533        self.browser.getControl("Remove").click()
534        self.assertTrue('1 application records purged' in self.browser.contents)
535        self.assertEqual(self.applicantscontainer.counts[0], 0)
536        self.assertEqual(self.applicantscontainer.counts[1], 0)
537        return
538
539    def init_officer(self):
540        # Create application officer
541        self.app['users'].addUser('mrappl', 'mrapplsecret')
542        self.app['users']['mrappl'].email = 'mrappl@foo.ng'
543        self.app['users']['mrappl'].title = 'Carlo Pitter'
544        prmglobal = IPrincipalRoleManager(self.app)
545        prmglobal.assignRoleToPrincipal('waeup.ApplicationsManager', 'mrappl')
546        # Login as officer
547        self.browser.open(self.login_path)
548        self.browser.getControl(name="form.login").value = 'mrappl'
549        self.browser.getControl(name="form.password").value = 'mrapplsecret'
550        self.browser.getControl("Login").click()
551
552    def test_student_creation_permission(self):
553        self.init_officer()
554        self.browser.open(self.container_path + '/manage')
555        self.browser.getControl("Create students").click()
556        self.assertTrue('You don\'t have permission to create student records'
557            in self.browser.contents)
558        prmglobal = IPrincipalRoleManager(self.app)
559        prmglobal.assignRoleToPrincipal('waeup.StudentsCreator', 'mrappl')
560        self.browser.getControl("Create students").click()
561        self.assertTrue('No applicant selected' in self.browser.contents)
562
563
564class ApplicantUITests(ApplicantsFullSetup):
565    # Tests for uploading/browsing the passport image of appplicants
566
567    layer = FunctionalLayer
568
569    def test_manage_and_view_applicant(self):
570        # Managers can manage applicants
571        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
572        self.slip_path = self.view_path + '/application_slip.pdf'
573        self.browser.open(self.manage_path)
574        self.assertEqual(self.browser.headers['Status'], '200 Ok')
575        self.fill_correct_values()
576        # Fire transition
577        self.browser.getControl(name="transition").value = ['start']
578        self.browser.getControl("Save").click()
579        # Be sure that the empty phone field does not show wrong error message
580        self.assertFalse('Required input is missing' in self.browser.contents)
581        self.assertMatches('...Form has been saved...', self.browser.contents)
582        self.assertMatches('...Application started by Manager...',
583                           self.browser.contents)
584        self.browser.open(self.view_path)
585        self.assertEqual(self.browser.headers['Status'], '200 Ok')
586        # Change course_admitted
587        self.browser.open(self.manage_path)
588        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
589        self.browser.getControl("Save").click()
590        self.assertMatches('...Form has been saved...', self.browser.contents)
591        # Change password
592        self.browser.getControl(name="password").value = 'secret'
593        self.browser.getControl(name="control_password").value = 'secre'
594        self.browser.getControl("Save").click()
595        self.assertMatches('...Passwords do not match...',
596                           self.browser.contents)
597        self.browser.getControl(name="password").value = 'secret'
598        self.browser.getControl(name="control_password").value = 'secret'
599        self.browser.getControl("Save").click()
600        self.assertMatches('...Form has been saved...', self.browser.contents)
601        # Pdf slip can't be opened and download button is not available
602        self.assertFalse('Download application slip' in self.browser.contents)
603        self.browser.open(self.slip_path)
604        self.assertTrue(
605            'Please pay and submit before trying to download the application slip.'
606            in self.browser.contents)
607        # If applicant is in correct state the pdf slip can be opened.
608        IWorkflowState(self.applicant).setState('submitted')
609        self.browser.open(self.manage_path)
610        self.browser.getLink("Download application slip").click()
611        self.assertEqual(self.browser.headers['Status'], '200 Ok')
612        self.assertEqual(self.browser.headers['Content-Type'],
613                         'application/pdf')
614        # Managers can view applicants even if certificate has been removed
615        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
616        self.browser.open(self.view_path)
617        self.assertEqual(self.browser.headers['Status'], '200 Ok')
618        self.browser.open(self.slip_path)
619        self.assertEqual(self.browser.headers['Status'], '200 Ok')
620        return
621
622    def test_passport_edit_view(self):
623        # We get a default image after login
624        self.browser.open(self.login_path)
625        self.login()
626        self.browser.open(self.browser.url + '/passport.jpg')
627        self.assertEqual(self.browser.headers['status'], '200 Ok')
628        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
629        self.assertTrue('JFIF' in self.browser.contents)
630        self.assertEqual(
631            self.browser.headers['content-length'], str(PH_LEN))
632
633    def test_applicant_login(self):
634        self.applicant.suspended = True
635        self.login()
636        self.assertTrue(
637            'You entered invalid credentials.' in self.browser.contents)
638        self.applicant.suspended = False
639        self.browser.getControl("Login").click()
640        self.assertTrue(
641            'You logged in.' in self.browser.contents)
642
643    def test_maintenance_mode(self):
644        config = grok.getSite()['configuration']
645        self.login()
646        # Applicant  logged in.
647        self.assertTrue('You logged in' in self.browser.contents)
648        self.assertTrue("Joan None" in self.browser.contents)
649        # If maintenance mode is enabled, applicant is immediately logged out.
650        config.maintmode_enabled_by = u'any_user'
651        self.assertRaises(
652            Unauthorized, self.browser.open, 'http://localhost/app/faculties')
653        self.browser.open('http://localhost/app/login')
654        self.assertTrue('The portal is in maintenance mode' in self.browser.contents)
655        # Applicant really can't login if maintenance mode is enabled.
656        self.login()
657        # A second warning is raised.
658        self.assertTrue(
659            'The portal is in maintenance mode. You can\'t login!'
660            in self.browser.contents)
661        return
662
663    def test_applicant_access(self):
664        # Applicants can edit their record
665        self.browser.open(self.login_path)
666        self.login()
667        self.assertTrue(
668            'You logged in.' in self.browser.contents)
669        self.browser.open(self.edit_path)
670        self.assertTrue(self.browser.url != self.login_path)
671        self.assertEqual(self.browser.headers['Status'], '200 Ok')
672        self.fill_correct_values()
673        self.assertTrue(IUserAccount(self.applicant).checkPassword('apwd'))
674        self.browser.getControl("Save").click()
675        self.assertMatches('...Form has been saved...', self.browser.contents)
676        # Applicants don't see manage and search links ...
677        self.browser.open(self.root_path)
678        self.assertEqual(self.browser.headers['Status'], '200 Ok')
679        self.assertFalse('Search' in self.browser.contents)
680        self.assertFalse('Manage applicants section' in self.browser.contents)
681        # ... and can't access the manage page
682        self.assertRaises(
683            Unauthorized, self.browser.open, self.manage_path)
684        return
685
686    def test_message_for_created(self):
687        IWorkflowState(self.applicant).setState('created')
688        self.applicant.student_id = u'my id'
689        self.browser.open(self.login_path)
690        self.login()
691        self.assertTrue(
692            'You logged in.' in self.browser.contents)
693        self.assertTrue(
694            '<strong>Congratulations!</strong> You have been offered provisional'
695            ' admission into the %s/%s Academic Session of'
696            ' Sample University. Your student record has been created for you.'
697            % (session_1, session_1 + 1) in self.browser.contents)
698        self.assertTrue(
699            'Then enter your new student credentials: user name= my id,'
700            ' password = %s.' % self.applicant.application_number
701            in self.browser.contents)
702        return
703
704    def image_url(self, filename):
705        return self.edit_path.replace('edit', filename)
706
707    def test_after_login_default_browsable(self):
708        # After login we see the placeholder image in the edit view
709        self.login()
710        self.assertEqual(self.browser.url, self.view_path)
711        self.browser.open(self.edit_path)
712        # There is a correct <img> link included
713        self.assertTrue(
714              '<img src="passport.jpg" height="180px" />' in self.browser.contents)
715        # Browsing the link shows a real image
716        self.browser.open(self.image_url('passport.jpg'))
717        self.assertEqual(
718            self.browser.headers['content-type'], 'image/jpeg')
719        self.assertEqual(len(self.browser.contents), PH_LEN)
720
721    def test_after_submit_default_browsable(self):
722        # After submitting an applicant form the default image is
723        # still visible
724        self.login()
725        self.browser.open(self.edit_path)
726        self.browser.getControl("Save").click() # submit form
727        # There is a correct <img> link included
728        self.assertTrue(
729            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
730        # Browsing the link shows a real image
731        self.browser.open(self.image_url('passport.jpg'))
732        self.assertEqual(
733            self.browser.headers['content-type'], 'image/jpeg')
734        self.assertEqual(len(self.browser.contents), PH_LEN)
735
736    def test_uploaded_image_respects_file_size_restriction(self):
737        # When we upload an image that is too big ( > 10 KB) we will
738        # get an error message
739        self.login()
740        self.browser.open(self.edit_path)
741        # Create a pseudo image file and select it to be uploaded in form
742        photo_content = 'A' * 1024 * 51  # A string of 11 KB size
743        pseudo_image = StringIO(photo_content)
744        ctrl = self.browser.getControl(name='form.passport')
745        file_ctrl = ctrl.mech_control
746        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
747        self.browser.getControl("Save").click() # submit form
748        # There is a correct <img> link included
749        self.assertTrue(
750            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
751        # We get a warning message
752        self.assertTrue(
753            'Uploaded image is too big' in self.browser.contents)
754        # Browsing the image gives us the default image, not the
755        # uploaded one.
756        self.browser.open(self.image_url('passport.jpg'))
757        self.assertEqual(
758            self.browser.headers['content-type'], 'image/jpeg')
759        self.assertEqual(len(self.browser.contents), PH_LEN)
760        # There is really no file stored for the applicant
761        img = getUtility(IExtFileStore).getFile(
762            IFileStoreNameChooser(self.applicant).chooseName())
763        self.assertTrue(img is None)
764
765    def test_uploaded_image_browsable_w_errors(self):
766        # We can upload a different image and browse it after submit,
767        # even if there are still errors in the form
768        self.login()
769        self.browser.open(self.edit_path)
770        # Create a pseudo image file and select it to be uploaded in form
771        photo_content = 'I pretend to be a graphics file'
772        pseudo_image = StringIO(photo_content)
773        ctrl = self.browser.getControl(name='form.passport')
774        file_ctrl = ctrl.mech_control
775        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
776        self.browser.getControl("Save").click() # submit form
777        # There is a correct <img> link included
778        self.assertTrue(
779            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
780        # Browsing the link shows a real image
781        self.browser.open(self.image_url('passport.jpg'))
782        self.assertEqual(
783            self.browser.headers['content-type'], 'image/jpeg')
784        self.assertEqual(self.browser.contents, photo_content)
785
786    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
787        # After uploading a new passport pic the file is correctly
788        # stored in an imagestorage
789        self.login()
790        self.browser.open(self.edit_path)
791        # Create a pseudo image file and select it to be uploaded in form
792        pseudo_image = StringIO('I pretend to be a graphics file')
793        ctrl = self.browser.getControl(name='form.passport')
794        file_ctrl = ctrl.mech_control
795        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
796        self.browser.getControl("Save").click() # submit form
797        storage = getUtility(IExtFileStore)
798        file_id = IFileStoreNameChooser(self.applicant).chooseName()
799        pseudo_image.seek(0) # reset our file data source
800        self.assertEqual(
801            storage.getFile(file_id).read(), pseudo_image.read())
802        return
803
804    def test_uploaded_image_browsable_wo_errors(self):
805        # We can upload a different image and browse it after submit,
806        # if there are no errors in form
807        self.login()
808        self.browser.open(self.edit_path)
809        self.fill_correct_values() # fill other fields with correct values
810        # Create a pseudo image file and select it to be uploaded in form
811        pseudo_image = StringIO('I pretend to be a graphics file')
812        ctrl = self.browser.getControl(name='form.passport')
813        file_ctrl = ctrl.mech_control
814        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
815        self.browser.getControl("Save").click() # submit form
816        # There is a correct <img> link included
817        self.assertTrue(
818            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
819        # Browsing the link shows a real image
820        self.browser.open(self.image_url('passport.jpg'))
821        self.assertEqual(
822            self.browser.headers['content-type'], 'image/jpeg')
823        self.assertEqual(len(self.browser.contents), 31)
824
825    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
826        # After uploading a new passport pic the file is correctly
827        # stored in an imagestorage if form contains no errors
828        self.login()
829        self.browser.open(self.edit_path)
830        self.fill_correct_values() # fill other fields with correct values
831        # Create a pseudo image file and select it to be uploaded in form
832        pseudo_image = StringIO('I pretend to be a graphics file')
833        ctrl = self.browser.getControl(name='form.passport')
834        file_ctrl = ctrl.mech_control
835        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
836        self.browser.getControl("Save").click() # submit form
837        storage = getUtility(IExtFileStore)
838        file_id = IFileStoreNameChooser(self.applicant).chooseName()
839        # The stored image can be fetched
840        fd = storage.getFile(file_id)
841        file_len = len(fd.read())
842        self.assertEqual(file_len, 31)
843        # When an applicant is removed, also the image is gone.
844        del self.app['applicants'][container_name_1][self.applicant.application_number]
845        fd = storage.getFile(file_id)
846        self.assertTrue(fd is None)
847
848    def test_uploaded_images_equal(self):
849        # Make sure uploaded images do really differ if we eject a
850        # change notfication (and do not if we don't)
851        self.login()
852        self.browser.open(self.edit_path)
853        self.fill_correct_values() # fill other fields with correct values
854        self.browser.getControl("Save").click() # submit form
855        # Now go on as an officer
856        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
857        self.browser.open(self.manage_path)
858
859        # Create a pseudo image file and select it to be uploaded in form
860        pseudo_image = StringIO('I pretend to be a graphics file')
861        ctrl = self.browser.getControl(name='form.passport')
862        file_ctrl = ctrl.mech_control
863        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
864        file_id = IFileStoreNameChooser(self.applicant).chooseName()
865        setSite(self.app)
866        passport0 = getUtility(IExtFileStore).getFile(file_id)
867        self.browser.getControl("Save").click() # submit form with changed pic
868        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
869        self.browser.getControl("Save").click() # submit form w/o changes
870        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
871        self.assertTrue(passport0 is None)
872        self.assertTrue(passport0 != passport1)
873        self.assertTrue(passport1 == passport2)
874        return
875
876    def test_upload_image_by_manager_with_logging(self):
877        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
878        self.browser.open(self.manage_path)
879        # Create a pseudo image file and select it to be uploaded in form
880        photo_content = 'A' * 1024 * 5  # A string of 5 KB size
881        pseudo_image = StringIO(photo_content)
882        ctrl = self.browser.getControl(name='form.passport')
883        file_ctrl = ctrl.mech_control
884        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
885        self.browser.getControl("Save").click() # submit form
886        # Even though the form could not be saved ...
887        self.assertTrue(
888            'Required input is missing' in self.browser.contents)
889        # ... the file has been successfully uploaded
890        logfile = os.path.join(
891            self.app['datacenter'].storage, 'logs', 'applicants.log')
892        logcontent = open(logfile).read()
893        self.assertTrue(
894            'zope.mgr - applicants.browser.ApplicantManageFormPage - '
895            '%s - saved: passport'
896            % (self.applicant.applicant_id)
897            in logcontent)
898
899    def test_application_slip_with_non_jpg_image(self):
900        IWorkflowState(self.applicant).setState('submitted')
901        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
902        self.browser.open(self.manage_path)
903        # Create a pseudo image file and select it to be uploaded in form
904        photo_content = 'A' * 1024 * 5  # A string of 5 KB size
905        pseudo_image = StringIO(photo_content)
906        ctrl = self.browser.getControl(name='form.passport')
907        file_ctrl = ctrl.mech_control
908        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
909        self.browser.getControl("Save").click() # submit form
910        self.browser.open(self.manage_path)
911        self.browser.getLink("Download application slip").click()
912        self.assertEqual(self.browser.headers['Status'], '200 Ok')
913        self.assertMatches(
914            '...Your image file is corrupted. Please replace...',
915            self.browser.contents)
916
917    def test_pay_portal_application_fee(self):
918        self.login()
919        self.browser.open(self.edit_path)
920        self.fill_correct_values()
921        # We have to save the form otherwise the filled fields will be cleared
922        # after adding an online payment, because adding an online payment
923        # requires a filled form but does not save it
924        self.browser.getControl("Save").click()
925        # Payment section does not appear if application fee isn't set
926        self.assertFalse('Payment Tickets' in self.browser.contents)
927        self.assertFalse('Add online payment' in self.browser.contents)
928        self.applicantscontainer.application_fee = 200.0
929        self.browser.open(self.edit_path)
930        self.browser.getControl("Add online payment ticket").click()
931        # Session object missing
932        self.assertTrue(
933            'Session configuration object is not available'
934            in self.browser.contents)
935        configuration = SessionConfiguration()
936        configuration.academic_session = session_1
937        self.app['configuration'].addSessionConfiguration(configuration)
938        self.browser.open(self.edit_path)
939        self.browser.getControl("Add online payment ticket").click()
940        self.assertMatches('...Payment ticket created...',
941                           self.browser.contents)
942        self.assertMatches('...Activation Code...',
943                           self.browser.contents)
944        self.assertTrue(
945            '<span>200.0</span>' in self.browser.contents)
946        # Payment ticket can be removed if they haven't received a
947        # valid callback
948        self.browser.open(self.edit_path)
949        ctrl = self.browser.getControl(name='val_id')
950        value = ctrl.options[0]
951        ctrl.getControl(value=value).selected = True
952        self.browser.getControl("Remove selected", index=0).click()
953        self.assertMatches('...Successfully removed...', self.browser.contents)
954        # We will try the callback request view
955        self.browser.getControl("Add online payment ticket").click()
956        self.browser.open(self.edit_path)
957        ctrl = self.browser.getControl(name='val_id')
958        value = ctrl.options[0]
959        self.browser.getLink(value).click()
960        self.assertMatches('...Amount Authorized...',
961                           self.browser.contents)
962        payment_url = self.browser.url
963        payment_id = self.applicant.keys()[0]
964        payment = self.applicant[payment_id]
965        self.assertEqual(payment.p_item,'This is the %s container' % container_name_1)
966        self.assertEqual(payment.p_session, session_1)
967        self.assertEqual(payment.p_category,'application')
968        self.assertEqual(payment.amount_auth,200.0)
969        # Applicant is payer of the payment ticket.
970        self.assertEqual(
971            IPayer(payment).display_fullname, 'John Anthony Tester')
972        self.assertEqual(
973            IPayer(payment).id, self.applicant.applicant_id)
974        self.assertEqual(IPayer(payment).faculty, 'N/A')
975        self.assertEqual(IPayer(payment).department, 'N/A')
976        # The pdf payment slip can't yet be opened
977        #self.browser.open(payment_url + '/payment_receipt.pdf')
978        #self.assertMatches('...Ticket not yet paid...',
979        #                   self.browser.contents)
980        # Approve payment
981        # Applicants can't approve payments
982        self.assertRaises(
983            Unauthorized, self.browser.open, payment_url + '/approve')
984        # We approve the payment by bypassing the view
985        payment.approve()
986        # Applicant is is not yet in state 'paid' because it was only
987        # the payment which we set to paid
988        self.browser.open(self.view_path)
989        self.assertMatches('...started...',
990                           self.browser.contents)
991        self.assertTrue(self.applicant.state == 'started')
992        # Let's logout and approve the payment as manager
993        self.browser.getLink("Logout").click()
994        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
995        # First we reset the payment
996        payment.r_amount_approved = 0.0
997        payment.r_code = u''
998        payment.p_state = 'unpaid'
999        payment.r_desc = u''
1000        payment.payment_date = None
1001        self.browser.open(payment_url)
1002        self.browser.getLink("Approve payment").click()
1003        self.assertEqual(payment.p_state, 'paid')
1004        self.assertEqual(payment.r_amount_approved, 200.0)
1005        self.assertEqual(payment.r_code, 'AP')
1006        self.assertTrue(self.applicant.state == 'paid')
1007        # Approval is logged in students.log ...
1008        logfile = os.path.join(
1009            self.app['datacenter'].storage, 'logs', 'applicants.log')
1010        logcontent = open(logfile).read()
1011        self.assertTrue(
1012            'zope.mgr - applicants.browser.OnlinePaymentApprovePage - '
1013            '%s - approved' % self.applicant.applicant_id
1014            in logcontent)
1015        # ... and in payments.log
1016        logfile = os.path.join(
1017            self.app['datacenter'].storage, 'logs', 'payments.log')
1018        logcontent = open(logfile).read()
1019        self.assertTrue(
1020            '"zope.mgr",%s,%s,application,200.0,AP,,,,,,\n'
1021            % (self.applicant.applicant_id, payment.p_id)
1022            in logcontent)
1023        # Payment slips can't be downloaded ...
1024        payment_id = self.applicant.keys()[0]
1025        self.browser.open(self.view_path + '/' + payment_id)
1026        self.browser.getLink("Download payment slip").click()
1027        self.assertTrue(
1028            'Please submit the application form before trying to download payment slips.'
1029            in self.browser.contents)
1030        # ... unless form is submitted.
1031        self.browser.open(self.view_path + '/edit')
1032        image = open(SAMPLE_IMAGE, 'rb')
1033        ctrl = self.browser.getControl(name='form.passport')
1034        file_ctrl = ctrl.mech_control
1035        file_ctrl.add_file(image, filename='myphoto.jpg')
1036        self.browser.getControl(name="confirm_passport").value = True
1037        self.browser.getControl("Finally Submit").click()
1038        self.browser.open(self.view_path + '/' + payment_id)
1039        self.browser.getLink("Download payment slip").click()
1040        self.assertEqual(self.browser.headers['Content-Type'],
1041                 'application/pdf')
1042        return
1043
1044    def prepare_special_container(self):
1045        # Add special application container
1046        container_name = u'special%s' % session_1
1047        applicantscontainer = ApplicantsContainer()
1048        applicantscontainer.code = container_name
1049        applicantscontainer.prefix = 'special'
1050        applicantscontainer.year = session_1
1051        applicantscontainer.title = u'This is a special app container'
1052        applicantscontainer.application_category = 'no'
1053        applicantscontainer.mode = 'create'
1054        applicantscontainer.strict_deadline = True
1055        delta = timedelta(days=10)
1056        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
1057        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
1058        self.app['applicants'][container_name] = applicantscontainer
1059        # Add an applicant
1060        applicant = createObject('waeup.Applicant')
1061        # reg_number is the only field which has to be preset here
1062        # because managers are allowed to edit this required field
1063        applicant.reg_number = u'12345'
1064        self.special_applicant = applicant
1065        self.app['applicants'][container_name].addApplicant(applicant)
1066        IUserAccount(
1067            self.app['applicants'][container_name][
1068            applicant.application_number]).setPassword('apwd')
1069        # Add session configuration object
1070        self.configuration = SessionConfiguration()
1071        self.configuration.academic_session = session_1
1072        #self.configuration.transcript_fee = 200.0
1073        self.configuration.clearance_fee = 300.0
1074        self.app['configuration'].addSessionConfiguration(self.configuration)
1075
1076
1077    def test_pay_special_fee(self):
1078        self.prepare_special_container()
1079        # Login
1080        self.browser.open(self.login_path)
1081        self.browser.getControl(
1082            name="form.login").value = self.special_applicant.applicant_id
1083        self.browser.getControl(name="form.password").value = 'apwd'
1084        self.browser.getControl("Login").click()
1085        applicant_path = self.browser.url
1086        self.browser.getLink("Edit application record").click()
1087        self.browser.getControl(name="form.firstname").value = 'John'
1088        self.browser.getControl(name="form.middlename").value = 'Anthony'
1089        self.browser.getControl(name="form.lastname").value = 'Tester'
1090        self.browser.getControl(name="form.special_application").value = [
1091            'transcript']
1092        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
1093        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1094        self.configuration.transcript_fee = 0.0
1095        self.browser.getControl("Save").click()
1096        self.browser.getControl("Add online payment ticket").click()
1097        self.assertMatches('...Amount could not be determined...',
1098                           self.browser.contents)
1099        self.configuration.transcript_fee = 200.0
1100        self.browser.getLink("Edit application record").click()
1101        self.browser.getControl("Add online payment ticket").click()
1102        self.assertMatches('...Payment ticket created...',
1103                           self.browser.contents)
1104        self.assertTrue(
1105            '<span>Transcript Fee</span>' in self.browser.contents)
1106        self.assertTrue(
1107            'This is a special app container' in self.browser.contents)
1108        self.assertTrue(
1109            '<span>200.0</span>' in self.browser.contents)
1110        self.assertEqual(len(self.special_applicant.keys()), 1)
1111        # The applicant's workflow state is paid ...
1112        self.special_applicant.payments[0].approveApplicantPayment()
1113        self.assertEqual(self.special_applicant.state, 'paid')
1114        self.browser.open(applicant_path + '/edit')
1115        # ... but he can create further tickets.
1116        self.browser.getControl(name="form.special_application").value = [
1117            'clearance']
1118        self.browser.getControl("Save").click()
1119        self.browser.getControl("Add online payment ticket").click()
1120        self.assertMatches('...Payment ticket created...',
1121                           self.browser.contents)
1122        self.browser.open(applicant_path)
1123        self.assertTrue(
1124            '<td>Acceptance Fee</td>' in self.browser.contents)
1125        self.assertEqual(len(self.special_applicant.keys()), 2)
1126        # Second payment can also be approved wthout error message
1127        flashtype, msg, log = self.special_applicant.payments[1].approveApplicantPayment()
1128        self.assertEqual(flashtype, 'success')
1129        self.assertEqual(msg, 'Payment approved')
1130        # Payment slips can't be downloaded ...
1131        payment_id = self.special_applicant.keys()[0]
1132        self.browser.open(applicant_path + '/' + payment_id)
1133        self.browser.getLink("Download payment slip").click()
1134        self.assertTrue(
1135            'Please submit the application form before trying to download payment slips.'
1136            in self.browser.contents)
1137        # ... unless form is submitted.
1138        self.browser.open(applicant_path + '/edit')
1139        image = open(SAMPLE_IMAGE, 'rb')
1140        ctrl = self.browser.getControl(name='form.passport')
1141        file_ctrl = ctrl.mech_control
1142        file_ctrl.add_file(image, filename='myphoto.jpg')
1143        self.browser.getControl(name="confirm_passport").value = True
1144        self.browser.getControl("Finally Submit").click()
1145        self.browser.open(applicant_path + '/' + payment_id)
1146        self.browser.getLink("Download payment slip").click()
1147        self.assertEqual(self.browser.headers['Content-Type'],
1148                 'application/pdf')
1149        return
1150
1151    def test_final_submit(self):
1152        # Make sure that a correctly filled form with passport picture
1153        # can be submitted (only) after payment
1154        self.login()
1155        self.browser.getLink("Edit application record").click()
1156        self.assertFalse('Finally Submit' in self.browser.contents)
1157        IWorkflowInfo(self.applicant).fireTransition('pay')
1158        self.browser.open(self.edit_path)
1159        self.assertTrue('Finally Submit' in self.browser.contents)
1160        self.fill_correct_values() # fill other fields with correct values
1161        self.browser.getControl("Save").click()
1162        self.browser.getControl("Finally Submit").click()
1163        # We forgot to upload a passport picture
1164        self.assertTrue(
1165            'No passport picture uploaded' in self.browser.contents)
1166        # Use a real image file and select it to be uploaded in form
1167        image = open(SAMPLE_IMAGE, 'rb')
1168        ctrl = self.browser.getControl(name='form.passport')
1169        file_ctrl = ctrl.mech_control
1170        file_ctrl.add_file(image, filename='myphoto.jpg')
1171        self.browser.getControl("Finally Submit").click() # (finally) submit form
1172        # The picture has been uploaded but the form cannot be submitted
1173        # since the passport confirmation box was not ticked
1174        self.assertTrue(
1175            'Passport picture confirmation box not ticked'
1176            in self.browser.contents)
1177        self.browser.getControl(name="confirm_passport").value = True
1178        # If application period has expired and strict-deadline is set
1179        # applicants do notsee edit button and can't open
1180        # the edit form.
1181        self.applicantscontainer.enddate = datetime.now(pytz.utc)
1182        self.browser.open(self.view_path)
1183        self.assertFalse(
1184            'Edit application record' in self.browser.contents)
1185        self.browser.open(self.edit_path)
1186        self.assertTrue(
1187            'form is locked' in self.browser.contents)
1188        # We can either postpone the enddate ...
1189        self.applicantscontainer.enddate = datetime.now(
1190            pytz.utc) + timedelta(days=10)
1191        self.browser.open(self.edit_path)
1192        self.browser.getControl(name="confirm_passport").value = True
1193        self.browser.getControl("Finally Submit").click()
1194        self.assertTrue(
1195            'Application submitted' in self.browser.contents)
1196        # ... or allow submission after deadline.
1197        IWorkflowState(self.applicant).setState('paid')
1198        self.applicant.locked = False
1199        self.applicantscontainer.strict_deadline = False
1200        self.browser.open(self.edit_path)
1201        self.browser.getControl(name="confirm_passport").value = True
1202        self.browser.getControl("Finally Submit").click()
1203        self.assertTrue(
1204            'Application submitted' in self.browser.contents)
1205        return
1206
1207    def test_locking(self):
1208        # Make sure that locked forms can't be submitted
1209        self.login()
1210        self.browser.open(self.edit_path)
1211        self.fill_correct_values() # fill other fields with correct values
1212        # Create a pseudo image file and select it to be uploaded in form
1213        pseudo_image = StringIO('I pretend to be a graphics file')
1214        ctrl = self.browser.getControl(name='form.passport')
1215        file_ctrl = ctrl.mech_control
1216        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
1217        self.browser.getControl("Save").click()
1218        # Now we lock the form
1219        self.applicant.locked = True
1220        self.browser.open(self.edit_path)
1221        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1222        self.assertTrue(
1223            'The requested form is locked' in self.browser.contents)
1224        return
1225
1226    def test_certificate_removed(self):
1227        self.login()
1228        self.browser.open(self.edit_path)
1229        self.fill_correct_values()
1230        self.browser.getControl("Save").click()
1231        self.browser.open(self.view_path)
1232        self.assertTrue(
1233            'Unnamed Certificate' in self.browser.contents)
1234        self.browser.open(self.edit_path)
1235        self.assertTrue(
1236            '<option selected="selected" value="CERT1">' in self.browser.contents)
1237        # Now we remove the certificate
1238        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1239        # The certificate is still shown in display mode
1240        self.browser.open(self.view_path)
1241        self.assertTrue(
1242            'Unnamed Certificate' in self.browser.contents)
1243        # The certificate is still selectable in edit mode so that it won't
1244        # be automatically replaced by another (arbitrary) certificate
1245        self.browser.open(self.edit_path)
1246        self.assertTrue(
1247            '<option selected="selected" value="CERT1">' in self.browser.contents)
1248        # Consequently, the certificate is still shown after saving the form
1249        self.browser.getControl("Save").click()
1250        self.browser.open(self.view_path)
1251        self.assertTrue(
1252            'Unnamed Certificate' in self.browser.contents)
1253        # Even if we add a new certificate the previous (removed)
1254        # certificate is shown
1255        certificate = createObject('waeup.Certificate')
1256        certificate.code = 'CERT2'
1257        certificate.title = 'New Certificate'
1258        certificate.application_category = 'basic'
1259        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1260            certificate)
1261        self.browser.open(self.edit_path)
1262        self.assertTrue(
1263            '<option selected="selected" value="CERT1">'
1264            in self.browser.contents)
1265
1266class ApplicantRegisterTests(ApplicantsFullSetup):
1267    # Tests for applicant registration
1268
1269    layer = FunctionalLayer
1270
1271    def test_register_applicant_create(self):
1272        config = grok.getSite()['configuration']
1273        config.maintmode_enabled_by = u'any_user'
1274        self.assertEqual(len(self.app['applicants'][container_name_1]), 1)
1275        # An applicant can register himself.
1276        self.browser.open(self.container_path)
1277        self.browser.getLink("Register for application").click()
1278        self.assertTrue(
1279            'The portal is in maintenance mode' in self.browser.contents)
1280        config.maintmode_enabled_by = None
1281        self.browser.getLink("Register for application").click()
1282        # The edit form now opens and can be filled with suitable values
1283        self.browser.getControl(name="form.firstname").value = 'Anna'
1284        self.browser.getControl(name="form.lastname").value = 'Kurios'
1285        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1286        self.browser.getControl(name="form.phone.country").value = ['+234']
1287        self.browser.getControl(name="form.phone.area").value = '555'
1288        self.browser.getControl(name="form.phone.ext").value = '6666666'
1289        self.browser.getControl("Send login credentials").click()
1290        self.assertEqual(self.browser.url,
1291            self.container_path + '/registration_complete?email=xx%40yy.zz')
1292        # A new applicant has been created
1293        self.assertEqual(len(self.app['applicants'][container_name_1]), 2)
1294        # The new applicant can be found in the catalog via the email address
1295        cat = getUtility(ICatalog, name='applicants_catalog')
1296        results = list(
1297            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
1298        applicant = results[0]
1299        self.assertEqual(applicant.lastname,'Kurios')
1300        # The application_id has been copied to the reg_number
1301        self.assertEqual(applicant.applicant_id, applicant.reg_number)
1302        # The applicant can be found in the catalog via the reg_number
1303        results = list(
1304            cat.searchResults(
1305            reg_number=(applicant.reg_number, applicant.reg_number)))
1306        self.assertEqual(applicant,results[0])
1307        return
1308
1309    def test_register_applicant_take_unused_record(self):
1310        # Create an unused record
1311        uu_applicant = createObject('waeup.Applicant')
1312        self.app['applicants'][container_name_1].addApplicant(uu_applicant)
1313        self.assertEqual(uu_applicant.container_code, container_name_1 + '-')
1314        self.assertEqual(len(self.app['applicants'][container_name_1]), 2)
1315        self.browser.open(self.container_path)
1316        self.browser.getLink("Register for application").click()
1317        # Fill the edit form with suitable values
1318        self.browser.getControl(name="form.firstname").value = 'Anna'
1319        self.browser.getControl(name="form.lastname").value = 'Kurios'
1320        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1321        self.browser.getControl(name="form.phone.country").value = ['+234']
1322        self.browser.getControl(name="form.phone.area").value = '555'
1323        self.browser.getControl(name="form.phone.ext").value = '6666666'
1324        self.browser.getControl("Send login credentials").click()
1325        # No applicant has been created ...
1326        self.assertEqual(len(self.app['applicants'][container_name_1]), 2)
1327        # ... and the existing, formerly unused record has been used instead
1328        self.assertEqual(uu_applicant.lastname, 'Kurios')
1329        self.assertEqual(uu_applicant.container_code, container_name_1 + '+')
1330        return
1331
1332    def test_register_applicant_update(self):
1333        # We change the application mode and check if applicants
1334        # can find and update imported records instead of creating new records.
1335        # First we check what happens if record does not exist.
1336        self.applicantscontainer.mode = 'update'
1337        self.browser.open(self.container_path + '/register')
1338        self.browser.getControl(name="form.lastname").value = 'Better'
1339        self.browser.getControl(name="form.reg_number").value = 'anynumber'
1340        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1341        self.browser.getControl("Send login credentials").click()
1342        self.assertTrue('No application record found.'
1343            in self.browser.contents)
1344        # Even with the correct reg_number we can't register
1345        # because lastname attribute is not set.
1346        self.applicantscontainer.mode = 'update'
1347        self.browser.open(self.container_path + '/register')
1348        self.browser.getControl(name="form.lastname").value = 'Better'
1349        self.browser.getControl(name="form.reg_number").value = '1234'
1350        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1351        self.browser.getControl("Send login credentials").click()
1352        self.assertTrue('An error occurred.' in self.browser.contents)
1353        # Let's set this attribute manually
1354        # and try to register with a wrong name.
1355        self.applicant.lastname = u'Better'
1356        self.browser.open(self.container_path + '/register')
1357        self.browser.getControl(name="form.lastname").value = 'Worse'
1358        self.browser.getControl(name="form.reg_number").value = '1234'
1359        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1360        self.browser.getControl("Send login credentials").click()
1361        # Anonymous is not informed that lastname verification failed.
1362        # It seems that the record doesn't exist.
1363        self.assertTrue('No application record found.'
1364            in self.browser.contents)
1365        # Even with the correct lastname we can't register if a
1366        # password has been set and used.
1367        IWorkflowState(self.applicant).setState('started')
1368        self.browser.getControl(name="form.lastname").value = 'Better'
1369        self.browser.getControl(name="form.reg_number").value = '1234'
1370        self.browser.getControl("Send login credentials").click()
1371        self.assertTrue('Your password has already been set and used.'
1372            in self.browser.contents)
1373        #IUserAccount(
1374        #    self.app['applicants'][container_name_1][
1375        #    self.applicant.application_number]).context.password = None
1376        # Even without unsetting the password we can re-register if state
1377        # is 'initialized'
1378        IWorkflowState(self.applicant).setState('initialized')
1379        self.browser.open(self.container_path + '/register')
1380        # The lastname field, used for verification, is not case-sensitive.
1381        self.browser.getControl(name="form.lastname").value = 'bEtter'
1382        self.browser.getControl(name="form.reg_number").value = '1234'
1383        self.browser.getControl(name="form.email").value = 'new@yy.zz'
1384        self.browser.getControl("Send login credentials").click()
1385        # Yeah, we succeded ...
1386        self.assertTrue('Your registration was successful.'
1387            in self.browser.contents)
1388        # ... and  applicant can be found in the catalog via the email address
1389        cat = getUtility(ICatalog, name='applicants_catalog')
1390        results = list(
1391            cat.searchResults(
1392            email=('new@yy.zz', 'new@yy.zz')))
1393        self.assertEqual(self.applicant,results[0])
1394        return
1395
1396    def test_change_password_request(self):
1397        self.browser.open('http://localhost/app/changepw')
1398        self.browser.getControl(name="form.identifier").value = '1234'
1399        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1400        self.browser.getControl("Send login credentials").click()
1401        self.assertTrue('No record found' in self.browser.contents)
1402        self.applicant.email = 'aa@aa.ng'
1403        # Update the catalog
1404        notify(grok.ObjectModifiedEvent(self.applicant))
1405        self.browser.open('http://localhost/app/changepw')
1406        self.browser.getControl(name="form.identifier").value = '1234'
1407        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1408        self.browser.getControl("Send login credentials").click()
1409        self.assertTrue(
1410            'An email with your user name and password has been sent'
1411            in self.browser.contents)
1412
1413    def test_check_status(self):
1414        self.applicant.lastname = u'Lion '
1415        self.browser.open('http://localhost/app/applicants/checkstatus')
1416        self.browser.getControl(name="unique_id").value = 'nonsense'
1417        self.browser.getControl(name="lastname").value = 'Lion'
1418        self.browser.getControl("Submit").click()
1419        self.assertTrue('No application record found' in self.browser.contents)
1420        self.browser.getControl(
1421          name="unique_id").value = self.applicant.applicant_id
1422        self.browser.getControl(name="lastname").value = 'nonsense'
1423        self.browser.getControl("Submit").click()
1424        self.assertTrue('No application record found' in self.browser.contents)
1425        self.browser.getControl(
1426          name="unique_id").value = self.applicant.applicant_id
1427        self.browser.getControl(name="lastname").value = 'Lion'
1428        self.browser.getControl("Submit").click()
1429        self.assertTrue('Admission status of' in self.browser.contents)
1430        self.assertTrue(
1431          'You have not yet submitted your application' in self.browser.contents)
1432        IWorkflowState(self.applicant).setState('admitted')
1433        self.browser.open('http://localhost/app/applicants/checkstatus')
1434        self.browser.getControl(
1435          name="unique_id").value = self.applicant.applicant_id
1436        # Whitespaces are ignored.
1437        self.browser.getControl(name="lastname").value = 'Lion'
1438        self.browser.getControl("Submit").click()
1439        self.assertTrue('Congratulations!' in self.browser.contents)
1440        self.assertFalse('Study Course' in self.browser.contents)
1441        self.applicant.course_admitted = self.certificate
1442        self.browser.open('http://localhost/app/applicants/checkstatus')
1443        self.browser.getControl(
1444          name="unique_id").value = self.applicant.applicant_id
1445        self.browser.getControl(name="lastname").value = 'Lion'
1446        self.browser.getControl("Submit").click()
1447        self.assertTrue('Congratulations!' in self.browser.contents)
1448        self.assertTrue('Unnamed Certificate (CERT1)' in self.browser.contents)
1449        self.assertTrue(
1450          'Department of Unnamed Department (dep1)' in self.browser.contents)
1451        self.assertTrue(
1452          'Faculty of Unnamed Faculty (NA)' in self.browser.contents)
1453        # Also the reg_number can be used and page shows student id and password
1454        # if applicant is in state created.
1455        IWorkflowState(self.applicant).setState('created')
1456        self.applicant.student_id = u'my id'
1457        self.browser.open('http://localhost/app/applicants/checkstatus')
1458        self.browser.getControl(
1459          name="unique_id").value = self.applicant.reg_number
1460        self.browser.getControl(name="lastname").value = 'Lion'
1461        self.browser.getControl("Submit").click()
1462        self.assertTrue('Congratulations!' in self.browser.contents)
1463        self.assertTrue('Unnamed Certificate (CERT1)' in self.browser.contents)
1464        self.assertTrue('Department of Unnamed Department (dep1)'
1465            in self.browser.contents)
1466        self.assertTrue(
1467          'Faculty of Unnamed Faculty (NA)' in self.browser.contents)
1468        self.assertTrue('user name (= student id) is: <strong>my id</strong>'
1469            in self.browser.contents)
1470        self.assertTrue(
1471          'password is: <strong>%s</strong>' % self.applicant.application_number
1472          in self.browser.contents)
1473       
1474
1475class ApplicantsExportTests(ApplicantsFullSetup, FunctionalAsyncTestCase):
1476    # Tests for StudentsContainer class views and pages
1477
1478    layer = FunctionalLayer
1479
1480    def wait_for_export_jobs_completed(self):
1481        # helper function waiting until the current export job is completed
1482        manager = getUtility(IJobManager)
1483        job_ids = [i[0] for i in self.app['datacenter'].running_exports]
1484        jobs = [manager.get(job_id) for job_id in job_ids]
1485        for job in jobs:
1486            wait_for_result(job)
1487        return job_ids
1488
1489    def test_applicants_in_container_export(self):
1490        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1491        container_path = 'http://localhost/app/applicants/%s' % container_name_1
1492        self.browser.open(container_path)
1493        self.browser.getLink("Export application data").click()
1494        self.browser.getControl("Start new exports").click()
1495        job_ids = self.wait_for_export_jobs_completed()
1496        # Two exports were created
1497        self.assertEqual(len(self.app['datacenter'].running_exports), 2)
1498       # When the jobs are finished and we reload the page...
1499        self.browser.open(container_path + '/exports')
1500        # ... the both csv files can be downloaded ...
1501        self.browser.getLink("Download", index=0).click()
1502        self.assertEqual(self.browser.headers['content-type'],
1503            'text/csv; charset=UTF-8')
1504        self.assertTrue(
1505            'filename="WAeUP.Kofa_applicants_%s.csv' % job_ids[0] in
1506            self.browser.headers['content-disposition'])
1507        self.browser.open(container_path + '/exports')
1508        self.browser.getLink("Download", index=1).click()
1509        self.assertEqual(self.browser.headers['content-type'],
1510            'text/csv; charset=UTF-8')
1511        self.assertTrue(
1512            'filename="WAeUP.Kofa_applicantpayments_%s.csv' % job_ids[1] in
1513            self.browser.headers['content-disposition'])
1514        # ... and discarded
1515        self.browser.open(container_path + '/exports')
1516        self.browser.getControl("Discard", index=0).click()
1517        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
1518        self.browser.getControl("Discard").click()
1519        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
1520        # Creation, downloading and discarding are logged
1521        logfile = os.path.join(
1522            self.app['datacenter'].storage, 'logs', 'datacenter.log')
1523        logcontent = open(logfile).read()
1524        self.assertTrue(
1525            'zope.mgr - applicants.browser.ExportJobContainerJobStart - '
1526            'exported: applicants (%s), job_id=%s'
1527            % (container_name_1, job_ids[0]) in logcontent
1528            )
1529        self.assertTrue(
1530            'zope.mgr - applicants.browser.ExportJobContainerDownload '
1531            '- downloaded: WAeUP.Kofa_applicants_%s.csv, job_id=%s'
1532            % (job_ids[0], job_ids[0]) in logcontent
1533            )
1534        self.assertTrue(
1535            'zope.mgr - applicants.browser.ExportJobContainerOverview '
1536            '- discarded: job_id=%s' % job_ids[0] in logcontent
1537            )
1538        self.assertTrue(
1539            'zope.mgr - applicants.browser.ExportJobContainerJobStart - '
1540            'exported: applicantpayments (%s), job_id=%s'
1541            % (container_name_1, job_ids[1]) in logcontent
1542            )
1543        self.assertTrue(
1544            'zope.mgr - applicants.browser.ExportJobContainerDownload '
1545            '- downloaded: WAeUP.Kofa_applicantpayments_%s.csv, job_id=%s'
1546            % (job_ids[1], job_ids[1]) in logcontent
1547            )
1548        self.assertTrue(
1549            'zope.mgr - applicants.browser.ExportJobContainerOverview '
1550            '- discarded: job_id=%s' % job_ids[1] in logcontent
1551            )
1552
1553class ApplicantRefereeReportTests(ApplicantsFullSetup, FunctionalAsyncTestCase):
1554    # Tests for ApplicantRefereeReport class views and pages
1555
1556    layer = FunctionalLayer
1557
1558    def setUp(self):
1559        super(ApplicantRefereeReportTests, self).setUp()
1560        self.setup_logging()
1561        return
1562
1563    def tearDown(self):
1564        super(ApplicantRefereeReportTests, self).tearDown()
1565        self.teardown_logging()
1566        return
1567
1568    def setup_logging(self):
1569        # setup a log-handler that catches all fake mailer output
1570        self.stream = StringIO()
1571        handler = logging.StreamHandler(self.stream)
1572        logger = logging.getLogger('test.smtp')
1573        logger.addHandler(handler)
1574        logger.setLevel(logging.INFO)
1575        return
1576
1577    def get_fake_smtp_output(self):
1578        # get output generated by fake mailer
1579        self.stream.flush()
1580        self.stream.seek(0)
1581        return self.stream.read()
1582
1583    def teardown_logging(self):
1584        # remove the log handler for fake mailer output
1585        logger = logging.getLogger('test.smtp')
1586        handlers = [x for x in logger.handlers]
1587        for handler in handlers:
1588            logger.removeHandler(handler)
1589        return
1590
1591    def test_refereereport_mandate(self):
1592        mandate = RefereeReportMandate()
1593        mandate.params['name'] = u'John Referee'
1594        mandate.params['email'] = 'aa@aa.aa'
1595        mandate.params[
1596            'redirect_path'] = '/applicants/%s/%s/addrefereereport' % (
1597                container_name_1, self.applicant.application_number)
1598        self.app['mandates'].addMandate(mandate)
1599        # Let's open the add form page via the mandate view
1600        self.browser.open('http://localhost/app/mandate?mandate_id=%s'
1601            % mandate.mandate_id)
1602        # Form page opens and is prefilled
1603        self.assertEqual(
1604            self.browser.url,
1605            'http://localhost/app/applicants/%s/%s/addrefereereport?mandate_id=%s'
1606            % (container_name_1, self.applicant.application_number,
1607            mandate.mandate_id))
1608        self.assertTrue('value="John Referee"' in self.browser.contents)
1609        # Let's open the page directly with an invalid mandate
1610        self.browser.open(
1611            'http://localhost/app/applicants/%s/%s/addrefereereport?mandate_id=wrongmadate'
1612            % (container_name_1, self.applicant.application_number))
1613        self.assertTrue('<div class="alert alert-warning">No mandate.</div>'
1614            in self.browser.contents)
1615        self.assertEqual(self.browser.url, 'http://localhost/app')
1616        # Page is also blocked in maintenance mode
1617        grok.getSite()['configuration'].maintmode_enabled_by = u'anybody'
1618        self.browser.open(
1619            'http://localhost/app/applicants/%s/%s/addrefereereport?mandate_id=%s'
1620            % (container_name_1, self.applicant.application_number,
1621            mandate.mandate_id))
1622        self.assertTrue('<div class="alert alert-warning">The portal is '
1623                        'in maintenance mode'
1624            in self.browser.contents)
1625        self.assertEqual(self.browser.url, 'http://localhost/app')
1626
1627    def test_add_and_view_reports(self):
1628        mandate = RefereeReportMandate()
1629        mandate.params['name'] = u'John Referee'
1630        mandate.params['email'] = 'aa@aa.aa'
1631        mandate.params[
1632            'redirect_path'] = '/applicants/%s/%s/addrefereereport' % (
1633                container_name_1, self.applicant.application_number)
1634        self.app['mandates'].addMandate(mandate)
1635        self.assertEqual(len(self.app['mandates'].keys()), 1)
1636        # Let's open the add form page via the mandate view
1637        self.browser.open('http://localhost/app/mandate?mandate_id=%s'
1638            % mandate.mandate_id)
1639        self.assertTrue('Joan None' in self.browser.contents)
1640        # Report can't be saved without required fields
1641        self.browser.getControl(name="form.email").value = ''
1642        self.browser.getControl("Submit").click()
1643        self.assertTrue('Required input is missing' in self.browser.contents)
1644        self.browser.getControl(name="form.email").value = 'bb@bb.bb'
1645        self.browser.getControl("Submit").click()
1646        self.assertTrue('Referee report has been saved' in self.browser.contents)
1647        self.assertEqual(self.browser.url, 'http://localhost/app')
1648        # Report has been created
1649        self.assertEqual(len(self.applicant.refereereports), 1)
1650        report = self.applicant.refereereports[0]
1651        # Managers can view the report
1652        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1653        self.browser.open(self.manage_path)
1654        self.browser.getLink("%s" % report.r_id).click()
1655        self.assertEqual(
1656            self.browser.url, self.view_path  + '/%s' % report.r_id)
1657        self.assertTrue('John Referee' in self.browser.contents)
1658        # Report creation is logged
1659        logfile = os.path.join(
1660            self.app['datacenter'].storage, 'logs', 'applicants.log')
1661        logcontent = open(logfile).read()
1662        self.assertTrue(
1663            'zope.anybody - applicants.browser.RefereeReportAddFormPage - '
1664            '%s - added: %s\n' % (self.applicant.applicant_id, report.r_id)
1665            in logcontent
1666            )
1667        # Mandate is deleted
1668        self.assertEqual(len(self.app['mandates'].keys()), 0)
1669
1670    def test_final_submit_with_referees(self):
1671        # Add two referees
1672        referee1 = RefereeEntry()
1673        referee2 = RefereeEntry()
1674        referee1.name = u'Linda Tree'
1675        referee1.email = 'linda@forest.de'
1676        referee2.name = u'Otis Stone'
1677        referee2.email = 'otis@stones.de'
1678        self.applicant.referees = [referee1, referee2]
1679        self.assertFalse(referee1.email_sent)
1680        self.assertFalse(referee2.email_sent)
1681        self.login()
1682        IWorkflowInfo(self.applicant).fireTransition('pay')
1683        self.browser.open(self.edit_path)
1684        self.fill_correct_values() # fill other fields with correct values
1685        image = open(SAMPLE_IMAGE, 'rb')
1686        ctrl = self.browser.getControl(name='form.passport')
1687        file_ctrl = ctrl.mech_control
1688        file_ctrl.add_file(image, filename='myphoto.jpg')
1689        self.browser.getControl("Save").click()
1690        self.browser.getControl(name="confirm_passport").value = True
1691        self.browser.getControl("Finally Submit").click()
1692        mandate_id_0 = self.app['mandates'].keys()[0]
1693        mandate_id_1 = self.app['mandates'].keys()[1]
1694        if self.app['mandates'].values()[0].params['name'] == 'Linda Tree':
1695            mandate_id_0 = self.app['mandates'].keys()[0]
1696            mandate_id_1 = self.app['mandates'].keys()[1]
1697        else:
1698            mandate_id_0 = self.app['mandates'].keys()[1]
1699            mandate_id_1 = self.app['mandates'].keys()[0]
1700        self.assertMatches(
1701            'Sending email from no-reply@waeup.org to linda@forest.de:'
1702            '\nMessage:'
1703            '\nmsg: MIME-Version: 1.0\nmsg: Content-Type: text/plain; charset="us-ascii"'
1704            '\nmsg: Content-Transfer-Encoding: 7bit'
1705            '\nmsg: From: Administrator <no-reply@waeup.org>'
1706            '\nmsg: To: Linda Tree <linda@forest.de>'
1707            '\nmsg: Reply-To: Administrator <contact@waeup.org>'
1708            '\nmsg: Subject: Request for referee report from Sample University'
1709            '\nmsg: '
1710            '\nmsg: Dear Linda Tree,'
1711            '\nmsg: '
1712            '\nmsg: The candidate with Id app%s_372052 and name John Anthony Tester applied to'
1713            '\nmsg: the Sample University to study Unnamed Certificate for the %s/%s session.'
1714            '\nmsg: The candidate has listed you as referee. You are thus required to kindly use'
1715            '\nmsg: the link below to provide your referral remarks on or before'
1716            '\nmsg: 2016-08-12 08:32:41.619671+00:00.'
1717            '\nmsg: '
1718            '\nmsg: Report link: http://localhost/app/mandate?mandate_id=%s'
1719            '\nmsg: '
1720            '\nmsg: Thank You'
1721            '\nmsg: '
1722            '\nmsg: The Secretary'
1723            '\nmsg: Post Graduate School'
1724            '\nmsg: Sample University'
1725            '\nmsg: '
1726            '\nSending email from no-reply@waeup.org to otis@stones.de:'
1727            '\nMessage:'
1728            '\nmsg: MIME-Version: 1.0'
1729            '\nmsg: Content-Type: text/plain; charset="us-ascii"'
1730            '\nmsg: Content-Transfer-Encoding: 7bit'
1731            '\nmsg: From: Administrator <no-reply@waeup.org>'
1732            '\nmsg: To: Otis Stone <otis@stones.de>'
1733            '\nmsg: Reply-To: Administrator <contact@waeup.org>'
1734            '\nmsg: Subject: Request for referee report from Sample University'
1735            '\nmsg: '
1736            '\nmsg: Dear Otis Stone,'
1737            '\nmsg: '
1738            '\nmsg: The candidate with Id app%s_<6-DIGITS> and name John Anthony Tester applied to'
1739            '\nmsg: the Sample University to study Unnamed Certificate for the %s/%s session.'
1740            '\nmsg: The candidate has listed you as referee. You are thus required to kindly use'
1741            '\nmsg: the link below to provide your referral remarks on or before'
1742            '\nmsg: <YYYY-MM-DD hh:mm:ss>.<6-DIGITS>+00:00.'
1743            '\nmsg: '
1744            '\nmsg: Report link: http://localhost/app/mandate?mandate_id=%s'
1745            '\nmsg: '
1746            '\nmsg: Thank You'
1747            '\nmsg: '
1748            '\nmsg: The Secretary'
1749            '\nmsg: Post Graduate School'
1750            '\nmsg: Sample University'
1751            '\nmsg: '
1752            % (session_1, session_1, session_2, mandate_id_0,
1753               session_1, session_1, session_2, mandate_id_1,),
1754            self.get_fake_smtp_output()
1755            )
1756        self.assertTrue(
1757            'Application submitted' in self.browser.contents)
1758        self.assertTrue(
1759            'Form has been successfully submitted and 2 '
1760            'invitation emails were sent.' in self.browser.contents)
1761        logfile = os.path.join(
1762            self.app['datacenter'].storage, 'logs', 'applicants.log')
1763        logcontent = open(logfile).read()
1764        self.assertTrue(
1765            '%s - applicants.browser.ApplicantEditFormPage - %s - '
1766            'email sent: otis@stones.de' %
1767            (self.applicant.applicant_id, self.applicant.applicant_id)
1768            in logcontent)
1769        self.assertTrue(referee1.email_sent)
1770        self.assertTrue(referee2.email_sent)
1771        # If the form is being resubmitted, no more emails will be sent
1772        IWorkflowState(self.applicant).setState('paid')
1773        self.applicant.locked = False
1774        self.browser.open(self.edit_path)
1775        self.browser.getControl(name="confirm_passport").value = True
1776        self.browser.getControl("Finally Submit").click()
1777        self.assertTrue(
1778            'Form has been successfully submitted and 0 '
1779            'invitation emails were sent.' in self.browser.contents)
1780        return
Note: See TracBrowser for help on using the repository browser.