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

Last change on this file since 17421 was 17261, checked in by Henrik Bettermann, 23 months ago

Specify the role in flash message after logging in.

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