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

Last change on this file since 14310 was 14282, checked in by Henrik Bettermann, 8 years ago

Fix test.

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