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

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

Insert the right mandate id.

  • Property svn:keywords set to Id
File size: 85.4 KB
Line 
1## $Id: test_browser.py 14052 2016-08-05 12:31:15Z 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, 'paid': 0})
432        #self.assertEqual(self.applicantscontainer.statistics[1],
433        #    {u'fac1': 0})
434        IWorkflowState(self.applicant).setState('submitted')
435        notify(grok.ObjectModifiedEvent(self.applicant))
436        self.assertEqual(self.applicantscontainer.statistics[0],
437            {'not admitted': 0, 'started': 0, 'created': 0,
438            'admitted': 0, 'submitted': 1, 'initialized': 0, 'paid': 0})
439        #self.assertEqual(self.applicantscontainer.statistics[1],
440        #    {u'fac1': 1})
441        return
442
443    def test_add_delete_applicants(self):
444        # Check the global role map first
445        role_manager = IPrincipalRoleManager(grok.getSite())
446        principals = role_manager.getPrincipalsForRole('waeup.Applicant')
447        self.assertEqual(len(principals), 1)
448        self.assertEqual(principals[0][0], self.applicant.applicant_id)
449        # Managers can add and delete applicants
450        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
451        self.add_applicant_path = self.container_path + '/addapplicant'
452        self.container_manage_path = self.container_path + '/@@manage'
453        self.browser.open(self.container_manage_path)
454        self.browser.getLink("Add applicant").click()
455        self.assertEqual(self.browser.headers['Status'], '200 Ok')
456        self.assertEqual(self.browser.url, self.add_applicant_path)
457        self.browser.getControl(name="form.firstname").value = 'Alois'
458        self.browser.getControl(name="form.middlename").value = 'Kofi'
459        self.browser.getControl(name="form.lastname").value = 'Bettermann'
460        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
461        self.browser.getControl("Create application record").click()
462        self.assertTrue('Application initialized' in self.browser.contents)
463        # The global role map has been extended
464        role_manager = IPrincipalRoleManager(grok.getSite())
465        principals = role_manager.getPrincipalsForRole('waeup.Applicant')
466        self.assertEqual(len(principals), 2)
467        self.browser.open(self.container_manage_path)
468        self.assertEqual(self.browser.headers['Status'], '200 Ok')
469        ctrl = self.browser.getControl(name='val_id')
470        value = ctrl.options[0]
471        ctrl.getControl(value=value).selected = True
472        self.browser.getControl("Remove selected", index=0).click()
473        self.assertTrue('Successfully removed:' in self.browser.contents)
474        # The global role map has been reduced
475        role_manager = IPrincipalRoleManager(grok.getSite())
476        principals = role_manager.getPrincipalsForRole('waeup.Applicant')
477        self.assertEqual(len(principals), 1)
478        self.browser.open(self.add_applicant_path)
479        self.browser.getControl(name="form.firstname").value = 'Albert'
480        self.browser.getControl(name="form.lastname").value = 'Einstein'
481        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
482        self.browser.getControl("Create application record").click()
483        self.assertTrue('Application initialized' in self.browser.contents)
484        return
485
486    def test_prefill_purge_container(self):
487        # Managers can pre-fill containers in create mode
488        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
489        self.prefill_path = self.container_path + '/prefill'
490        self.container_manage_path = self.container_path + '/@@manage'
491        self.browser.open(self.container_manage_path)
492        self.browser.getLink("Pre-fill").click()
493        self.assertEqual(self.browser.headers['Status'], '200 Ok')
494        self.assertEqual(self.browser.url, self.prefill_path)
495        self.browser.getControl(name="number").value = ['10']
496        self.browser.getControl("Pre-fill").click()
497        self.assertTrue('10 application records created.' in self.browser.contents)
498        self.browser.open(self.container_manage_path)
499        self.assertTrue('This container contains 10 unused pre-filled records.'
500            in self.browser.contents)
501        self.assertEqual(self.applicantscontainer.counts[0], 11)
502        self.assertEqual(self.applicantscontainer.counts[1], 1)
503        # In update mode we can't pre-fill the container
504        self.applicantscontainer.mode = 'update'
505        self.browser.open(self.container_manage_path)
506        self.browser.getLink("Pre-fill").click()
507        self.assertTrue('Container must be in create mode to be pre-filled.'
508            in self.browser.contents)
509        self.browser.open(self.manage_root_path)
510        # Number of total records is 11
511        self.assertTrue('<td>11</td>' in self.browser.contents)
512        # The statistics have not changed
513        self.browser.open(self.container_path)
514        self.browser.getLink("Container statistics").click()
515        self.assertTrue('<td>1</td>' in self.browser.contents)
516        self.assertEqual(self.applicantscontainer.statistics[0],
517            {'not admitted': 0, 'started': 0, 'created': 0,
518            'admitted': 0, 'submitted': 0, 'initialized': 1, 'paid': 0})
519        # Container can be purged
520        IWorkflowState(self.applicant).setState('submitted')
521        self.browser.open(self.container_manage_path)
522        self.browser.getLink("Purge").click()
523        self.browser.getControl("Remove").click()
524        self.assertTrue('10 application records purged' in self.browser.contents)
525        self.assertEqual(self.applicantscontainer.counts[0], 1)
526        self.assertEqual(self.applicantscontainer.counts[1], 1)
527        IWorkflowState(self.applicant).setState('initialized')
528        self.browser.open(self.container_manage_path)
529        self.browser.getLink("Purge").click()
530        self.browser.getControl("Remove").click()
531        self.assertTrue('1 application records purged' in self.browser.contents)
532        self.assertEqual(self.applicantscontainer.counts[0], 0)
533        self.assertEqual(self.applicantscontainer.counts[1], 0)
534        return
535
536class ApplicantUITests(ApplicantsFullSetup):
537    # Tests for uploading/browsing the passport image of appplicants
538
539    layer = FunctionalLayer
540
541    def test_manage_and_view_applicant(self):
542        # Managers can manage applicants
543        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
544        self.slip_path = self.view_path + '/application_slip.pdf'
545        self.browser.open(self.manage_path)
546        self.assertEqual(self.browser.headers['Status'], '200 Ok')
547        self.fill_correct_values()
548        # Fire transition
549        self.browser.getControl(name="transition").value = ['start']
550        self.browser.getControl("Save").click()
551        # Be sure that the empty phone field does not show wrong error message
552        self.assertFalse('Required input is missing' in self.browser.contents)
553        self.assertMatches('...Form has been saved...', self.browser.contents)
554        self.assertMatches('...Application started by Manager...',
555                           self.browser.contents)
556        self.browser.open(self.view_path)
557        self.assertEqual(self.browser.headers['Status'], '200 Ok')
558        # Change course_admitted
559        self.browser.open(self.manage_path)
560        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
561        self.browser.getControl("Save").click()
562        self.assertMatches('...Form has been saved...', self.browser.contents)
563        # Change password
564        self.browser.getControl(name="password").value = 'secret'
565        self.browser.getControl(name="control_password").value = 'secre'
566        self.browser.getControl("Save").click()
567        self.assertMatches('...Passwords do not match...',
568                           self.browser.contents)
569        self.browser.getControl(name="password").value = 'secret'
570        self.browser.getControl(name="control_password").value = 'secret'
571        self.browser.getControl("Save").click()
572        self.assertMatches('...Form has been saved...', self.browser.contents)
573        # Pdf slip can't be opened and download button is not available
574        self.assertFalse('Download application slip' in self.browser.contents)
575        self.browser.open(self.slip_path)
576        self.assertTrue(
577            'Please pay and submit before trying to download the application slip.'
578            in self.browser.contents)
579        # If applicant is in correct state the pdf slip can be opened.
580        IWorkflowState(self.applicant).setState('submitted')
581        self.browser.open(self.manage_path)
582        self.browser.getLink("Download application slip").click()
583        self.assertEqual(self.browser.headers['Status'], '200 Ok')
584        self.assertEqual(self.browser.headers['Content-Type'],
585                         'application/pdf')
586        # Managers can view applicants even if certificate has been removed
587        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
588        self.browser.open(self.view_path)
589        self.assertEqual(self.browser.headers['Status'], '200 Ok')
590        self.browser.open(self.slip_path)
591        self.assertEqual(self.browser.headers['Status'], '200 Ok')
592        return
593
594    def test_passport_edit_view(self):
595        # We get a default image after login
596        self.browser.open(self.login_path)
597        self.login()
598        self.browser.open(self.browser.url + '/passport.jpg')
599        self.assertEqual(self.browser.headers['status'], '200 Ok')
600        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
601        self.assertTrue('JFIF' in self.browser.contents)
602        self.assertEqual(
603            self.browser.headers['content-length'], str(PH_LEN))
604
605    def test_applicant_login(self):
606        self.applicant.suspended = True
607        self.login()
608        self.assertTrue(
609            'You entered invalid credentials.' in self.browser.contents)
610        self.applicant.suspended = False
611        self.browser.getControl("Login").click()
612        self.assertTrue(
613            'You logged in.' in self.browser.contents)
614
615    def test_maintenance_mode(self):
616        config = grok.getSite()['configuration']
617        self.login()
618        # Applicant  logged in.
619        self.assertTrue('You logged in' in self.browser.contents)
620        self.assertTrue("Joan None" in self.browser.contents)
621        # If maintenance mode is enabled, applicant is immediately logged out.
622        config.maintmode_enabled_by = u'any_user'
623        self.assertRaises(
624            Unauthorized, self.browser.open, 'http://localhost/app/faculties')
625        self.browser.open('http://localhost/app/login')
626        self.assertTrue('The portal is in maintenance mode' in self.browser.contents)
627        # Applicant really can't login if maintenance mode is enabled.
628        self.login()
629        # A second warning is raised.
630        self.assertTrue(
631            'The portal is in maintenance mode. You can\'t login!'
632            in self.browser.contents)
633        return
634
635    def test_applicant_access(self):
636        # Applicants can edit their record
637        self.browser.open(self.login_path)
638        self.login()
639        self.assertTrue(
640            'You logged in.' in self.browser.contents)
641        self.browser.open(self.edit_path)
642        self.assertTrue(self.browser.url != self.login_path)
643        self.assertEqual(self.browser.headers['Status'], '200 Ok')
644        self.fill_correct_values()
645        self.assertTrue(IUserAccount(self.applicant).checkPassword('apwd'))
646        self.browser.getControl("Save").click()
647        self.assertMatches('...Form has been saved...', self.browser.contents)
648        # Applicants don't see manage and search links ...
649        self.browser.open(self.root_path)
650        self.assertEqual(self.browser.headers['Status'], '200 Ok')
651        self.assertFalse('Search' in self.browser.contents)
652        self.assertFalse('Manage applicants section' in self.browser.contents)
653        # ... and can't access the manage page
654        self.assertRaises(
655            Unauthorized, self.browser.open, self.manage_path)
656        return
657
658    def test_message_for_created(self):
659        IWorkflowState(self.applicant).setState('created')
660        self.applicant.student_id = u'my id'
661        self.browser.open(self.login_path)
662        self.login()
663        self.assertTrue(
664            'You logged in.' in self.browser.contents)
665        self.assertTrue(
666            '<strong>Congratulations!</strong> You have been offered provisional'
667            ' admission into the %s/%s Academic Session of'
668            ' Sample University. Your student record has been created for you.'
669            % (session_1, session_1 + 1) in self.browser.contents)
670        self.assertTrue(
671            'Then enter your new student credentials: user name= my id,'
672            ' password = %s.' % self.applicant.application_number
673            in self.browser.contents)
674        return
675
676    def image_url(self, filename):
677        return self.edit_path.replace('edit', filename)
678
679    def test_after_login_default_browsable(self):
680        # After login we see the placeholder image in the edit view
681        self.login()
682        self.assertEqual(self.browser.url, self.view_path)
683        self.browser.open(self.edit_path)
684        # There is a correct <img> link included
685        self.assertTrue(
686              '<img src="passport.jpg" height="180px" />' in self.browser.contents)
687        # Browsing the link shows a real image
688        self.browser.open(self.image_url('passport.jpg'))
689        self.assertEqual(
690            self.browser.headers['content-type'], 'image/jpeg')
691        self.assertEqual(len(self.browser.contents), PH_LEN)
692
693    def test_after_submit_default_browsable(self):
694        # After submitting an applicant form the default image is
695        # still visible
696        self.login()
697        self.browser.open(self.edit_path)
698        self.browser.getControl("Save").click() # submit form
699        # There is a correct <img> link included
700        self.assertTrue(
701            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
702        # Browsing the link shows a real image
703        self.browser.open(self.image_url('passport.jpg'))
704        self.assertEqual(
705            self.browser.headers['content-type'], 'image/jpeg')
706        self.assertEqual(len(self.browser.contents), PH_LEN)
707
708    def test_uploaded_image_respects_file_size_restriction(self):
709        # When we upload an image that is too big ( > 10 KB) we will
710        # get an error message
711        self.login()
712        self.browser.open(self.edit_path)
713        # Create a pseudo image file and select it to be uploaded in form
714        photo_content = 'A' * 1024 * 51  # A string of 11 KB size
715        pseudo_image = StringIO(photo_content)
716        ctrl = self.browser.getControl(name='form.passport')
717        file_ctrl = ctrl.mech_control
718        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
719        self.browser.getControl("Save").click() # submit form
720        # There is a correct <img> link included
721        self.assertTrue(
722            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
723        # We get a warning message
724        self.assertTrue(
725            'Uploaded image is too big' in self.browser.contents)
726        # Browsing the image gives us the default image, not the
727        # uploaded one.
728        self.browser.open(self.image_url('passport.jpg'))
729        self.assertEqual(
730            self.browser.headers['content-type'], 'image/jpeg')
731        self.assertEqual(len(self.browser.contents), PH_LEN)
732        # There is really no file stored for the applicant
733        img = getUtility(IExtFileStore).getFile(
734            IFileStoreNameChooser(self.applicant).chooseName())
735        self.assertTrue(img is None)
736
737    def test_uploaded_image_browsable_w_errors(self):
738        # We can upload a different image and browse it after submit,
739        # even if there are still errors in the form
740        self.login()
741        self.browser.open(self.edit_path)
742        # Create a pseudo image file and select it to be uploaded in form
743        photo_content = 'I pretend to be a graphics file'
744        pseudo_image = StringIO(photo_content)
745        ctrl = self.browser.getControl(name='form.passport')
746        file_ctrl = ctrl.mech_control
747        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
748        self.browser.getControl("Save").click() # submit form
749        # There is a correct <img> link included
750        self.assertTrue(
751            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
752        # Browsing the link shows a real image
753        self.browser.open(self.image_url('passport.jpg'))
754        self.assertEqual(
755            self.browser.headers['content-type'], 'image/jpeg')
756        self.assertEqual(self.browser.contents, photo_content)
757
758    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
759        # After uploading a new passport pic the file is correctly
760        # stored in an imagestorage
761        self.login()
762        self.browser.open(self.edit_path)
763        # Create a pseudo image file and select it to be uploaded in form
764        pseudo_image = StringIO('I pretend to be a graphics file')
765        ctrl = self.browser.getControl(name='form.passport')
766        file_ctrl = ctrl.mech_control
767        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
768        self.browser.getControl("Save").click() # submit form
769        storage = getUtility(IExtFileStore)
770        file_id = IFileStoreNameChooser(self.applicant).chooseName()
771        pseudo_image.seek(0) # reset our file data source
772        self.assertEqual(
773            storage.getFile(file_id).read(), pseudo_image.read())
774        return
775
776    def test_uploaded_image_browsable_wo_errors(self):
777        # We can upload a different image and browse it after submit,
778        # if there are no errors in form
779        self.login()
780        self.browser.open(self.edit_path)
781        self.fill_correct_values() # fill other fields with correct values
782        # Create a pseudo image file and select it to be uploaded in form
783        pseudo_image = StringIO('I pretend to be a graphics file')
784        ctrl = self.browser.getControl(name='form.passport')
785        file_ctrl = ctrl.mech_control
786        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
787        self.browser.getControl("Save").click() # submit form
788        # There is a correct <img> link included
789        self.assertTrue(
790            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
791        # Browsing the link shows a real image
792        self.browser.open(self.image_url('passport.jpg'))
793        self.assertEqual(
794            self.browser.headers['content-type'], 'image/jpeg')
795        self.assertEqual(len(self.browser.contents), 31)
796
797    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
798        # After uploading a new passport pic the file is correctly
799        # stored in an imagestorage if form contains no errors
800        self.login()
801        self.browser.open(self.edit_path)
802        self.fill_correct_values() # fill other fields with correct values
803        # Create a pseudo image file and select it to be uploaded in form
804        pseudo_image = StringIO('I pretend to be a graphics file')
805        ctrl = self.browser.getControl(name='form.passport')
806        file_ctrl = ctrl.mech_control
807        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
808        self.browser.getControl("Save").click() # submit form
809        storage = getUtility(IExtFileStore)
810        file_id = IFileStoreNameChooser(self.applicant).chooseName()
811        # The stored image can be fetched
812        fd = storage.getFile(file_id)
813        file_len = len(fd.read())
814        self.assertEqual(file_len, 31)
815        # When an applicant is removed, also the image is gone.
816        del self.app['applicants'][container_name_1][self.applicant.application_number]
817        fd = storage.getFile(file_id)
818        self.assertTrue(fd is None)
819
820    def test_uploaded_images_equal(self):
821        # Make sure uploaded images do really differ if we eject a
822        # change notfication (and do not if we don't)
823        self.login()
824        self.browser.open(self.edit_path)
825        self.fill_correct_values() # fill other fields with correct values
826        self.browser.getControl("Save").click() # submit form
827        # Now go on as an officer
828        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
829        self.browser.open(self.manage_path)
830
831        # Create a pseudo image file and select it to be uploaded in form
832        pseudo_image = StringIO('I pretend to be a graphics file')
833        ctrl = self.browser.getControl(name='form.passport')
834        file_ctrl = ctrl.mech_control
835        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
836        file_id = IFileStoreNameChooser(self.applicant).chooseName()
837        setSite(self.app)
838        passport0 = getUtility(IExtFileStore).getFile(file_id)
839        self.browser.getControl("Save").click() # submit form with changed pic
840        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
841        self.browser.getControl("Save").click() # submit form w/o changes
842        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
843        self.assertTrue(passport0 is None)
844        self.assertTrue(passport0 != passport1)
845        self.assertTrue(passport1 == passport2)
846        return
847
848    def test_upload_image_by_manager_with_logging(self):
849        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
850        self.browser.open(self.manage_path)
851        # Create a pseudo image file and select it to be uploaded in form
852        photo_content = 'A' * 1024 * 5  # A string of 5 KB size
853        pseudo_image = StringIO(photo_content)
854        ctrl = self.browser.getControl(name='form.passport')
855        file_ctrl = ctrl.mech_control
856        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
857        self.browser.getControl("Save").click() # submit form
858        # Even though the form could not be saved ...
859        self.assertTrue(
860            'Required input is missing' in self.browser.contents)
861        # ... the file has been successfully uploaded
862        logfile = os.path.join(
863            self.app['datacenter'].storage, 'logs', 'applicants.log')
864        logcontent = open(logfile).read()
865        self.assertTrue(
866            'zope.mgr - applicants.browser.ApplicantManageFormPage - '
867            '%s - saved: passport'
868            % (self.applicant.applicant_id)
869            in logcontent)
870
871    def test_application_slip_with_non_jpg_image(self):
872        IWorkflowState(self.applicant).setState('submitted')
873        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
874        self.browser.open(self.manage_path)
875        # Create a pseudo image file and select it to be uploaded in form
876        photo_content = 'A' * 1024 * 5  # A string of 5 KB size
877        pseudo_image = StringIO(photo_content)
878        ctrl = self.browser.getControl(name='form.passport')
879        file_ctrl = ctrl.mech_control
880        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
881        self.browser.getControl("Save").click() # submit form
882        self.browser.open(self.manage_path)
883        self.browser.getLink("Download application slip").click()
884        self.assertEqual(self.browser.headers['Status'], '200 Ok')
885        self.assertMatches(
886            '...Your image file is corrupted. Please replace...',
887            self.browser.contents)
888
889    def test_pay_portal_application_fee(self):
890        self.login()
891        self.browser.open(self.edit_path)
892        self.fill_correct_values()
893        # We have to save the form otherwise the filled fields will be cleared
894        # after adding an online payment, because adding an online payment
895        # requires a filled form but does not save it
896        self.browser.getControl("Save").click()
897        # Payment section does not appear if application fee isn't set
898        self.assertFalse('Payment Tickets' in self.browser.contents)
899        self.assertFalse('Add online payment' in self.browser.contents)
900        self.applicantscontainer.application_fee = 200.0
901        self.browser.open(self.edit_path)
902        self.browser.getControl("Add online payment ticket").click()
903        # Session object missing
904        self.assertTrue(
905            'Session configuration object is not available'
906            in self.browser.contents)
907        configuration = SessionConfiguration()
908        configuration.academic_session = session_1
909        self.app['configuration'].addSessionConfiguration(configuration)
910        self.browser.open(self.edit_path)
911        self.browser.getControl("Add online payment ticket").click()
912        self.assertMatches('...Payment ticket created...',
913                           self.browser.contents)
914        self.assertMatches('...Activation Code...',
915                           self.browser.contents)
916        self.assertTrue(
917            '<span>200.0</span>' in self.browser.contents)
918        # Payment ticket can be removed if they haven't received a
919        # valid callback
920        self.browser.open(self.edit_path)
921        ctrl = self.browser.getControl(name='val_id')
922        value = ctrl.options[0]
923        ctrl.getControl(value=value).selected = True
924        self.browser.getControl("Remove selected", index=0).click()
925        self.assertMatches('...Successfully removed...', self.browser.contents)
926        # We will try the callback request view
927        self.browser.getControl("Add online payment ticket").click()
928        self.browser.open(self.edit_path)
929        ctrl = self.browser.getControl(name='val_id')
930        value = ctrl.options[0]
931        self.browser.getLink(value).click()
932        self.assertMatches('...Amount Authorized...',
933                           self.browser.contents)
934        payment_url = self.browser.url
935        payment_id = self.applicant.keys()[0]
936        payment = self.applicant[payment_id]
937        self.assertEqual(payment.p_item,'This is the %s container' % container_name_1)
938        self.assertEqual(payment.p_session, session_1)
939        self.assertEqual(payment.p_category,'application')
940        self.assertEqual(payment.amount_auth,200.0)
941        # Applicant is payer of the payment ticket.
942        self.assertEqual(
943            IPayer(payment).display_fullname, 'John Anthony Tester')
944        self.assertEqual(
945            IPayer(payment).id, self.applicant.applicant_id)
946        self.assertEqual(IPayer(payment).faculty, 'N/A')
947        self.assertEqual(IPayer(payment).department, 'N/A')
948        # The pdf payment slip can't yet be opened
949        #self.browser.open(payment_url + '/payment_receipt.pdf')
950        #self.assertMatches('...Ticket not yet paid...',
951        #                   self.browser.contents)
952        # Approve payment
953        # Applicants can't approve payments
954        self.assertRaises(
955            Unauthorized, self.browser.open, payment_url + '/approve')
956        # We approve the payment by bypassing the view
957        payment.approve()
958        # Applicant is is not yet in state 'paid' because it was only
959        # the payment which we set to paid
960        self.browser.open(self.view_path)
961        self.assertMatches('...started...',
962                           self.browser.contents)
963        self.assertTrue(self.applicant.state == 'started')
964        # Let's logout and approve the payment as manager
965        self.browser.getLink("Logout").click()
966        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
967        # First we reset the payment
968        payment.r_amount_approved = 0.0
969        payment.r_code = u''
970        payment.p_state = 'unpaid'
971        payment.r_desc = u''
972        payment.payment_date = None
973        self.browser.open(payment_url)
974        self.browser.getLink("Approve payment").click()
975        self.assertEqual(payment.p_state, 'paid')
976        self.assertEqual(payment.r_amount_approved, 200.0)
977        self.assertEqual(payment.r_code, 'AP')
978        self.assertTrue(self.applicant.state == 'paid')
979        # Approval is logged in students.log ...
980        logfile = os.path.join(
981            self.app['datacenter'].storage, 'logs', 'applicants.log')
982        logcontent = open(logfile).read()
983        self.assertTrue(
984            'zope.mgr - applicants.browser.OnlinePaymentApprovePage - '
985            '%s - approved' % self.applicant.applicant_id
986            in logcontent)
987        # ... and in payments.log
988        logfile = os.path.join(
989            self.app['datacenter'].storage, 'logs', 'payments.log')
990        logcontent = open(logfile).read()
991        self.assertTrue(
992            '"zope.mgr",%s,%s,application,200.0,AP,,,,,,\n'
993            % (self.applicant.applicant_id, payment.p_id)
994            in logcontent)
995        # Payment slips can't be downloaded ...
996        payment_id = self.applicant.keys()[0]
997        self.browser.open(self.view_path + '/' + payment_id)
998        self.browser.getLink("Download payment slip").click()
999        self.assertTrue(
1000            'Please submit the application form before trying to download payment slips.'
1001            in self.browser.contents)
1002        # ... unless form is submitted.
1003        self.browser.open(self.view_path + '/edit')
1004        image = open(SAMPLE_IMAGE, 'rb')
1005        ctrl = self.browser.getControl(name='form.passport')
1006        file_ctrl = ctrl.mech_control
1007        file_ctrl.add_file(image, filename='myphoto.jpg')
1008        self.browser.getControl(name="confirm_passport").value = True
1009        self.browser.getControl("Finally Submit").click()
1010        self.browser.open(self.view_path + '/' + payment_id)
1011        self.browser.getLink("Download payment slip").click()
1012        self.assertEqual(self.browser.headers['Content-Type'],
1013                 'application/pdf')
1014        return
1015
1016    def prepare_special_container(self):
1017        # Add special application container
1018        container_name = u'special%s' % session_1
1019        applicantscontainer = ApplicantsContainer()
1020        applicantscontainer.code = container_name
1021        applicantscontainer.prefix = 'special'
1022        applicantscontainer.year = session_1
1023        applicantscontainer.title = u'This is a special app container'
1024        applicantscontainer.application_category = 'no'
1025        applicantscontainer.mode = 'create'
1026        applicantscontainer.strict_deadline = True
1027        delta = timedelta(days=10)
1028        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
1029        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
1030        self.app['applicants'][container_name] = applicantscontainer
1031        # Add an applicant
1032        applicant = createObject('waeup.Applicant')
1033        # reg_number is the only field which has to be preset here
1034        # because managers are allowed to edit this required field
1035        applicant.reg_number = u'12345'
1036        self.special_applicant = applicant
1037        self.app['applicants'][container_name].addApplicant(applicant)
1038        IUserAccount(
1039            self.app['applicants'][container_name][
1040            applicant.application_number]).setPassword('apwd')
1041        # Add session configuration object
1042        self.configuration = SessionConfiguration()
1043        self.configuration.academic_session = session_1
1044        #self.configuration.transcript_fee = 200.0
1045        self.configuration.clearance_fee = 300.0
1046        self.app['configuration'].addSessionConfiguration(self.configuration)
1047
1048
1049    def test_pay_special_fee(self):
1050        self.prepare_special_container()
1051        # Login
1052        self.browser.open(self.login_path)
1053        self.browser.getControl(
1054            name="form.login").value = self.special_applicant.applicant_id
1055        self.browser.getControl(name="form.password").value = 'apwd'
1056        self.browser.getControl("Login").click()
1057        applicant_path = self.browser.url
1058        self.browser.getLink("Edit application record").click()
1059        self.browser.getControl(name="form.firstname").value = 'John'
1060        self.browser.getControl(name="form.middlename").value = 'Anthony'
1061        self.browser.getControl(name="form.lastname").value = 'Tester'
1062        self.browser.getControl(name="form.special_application").value = [
1063            'transcript']
1064        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
1065        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1066        self.configuration.transcript_fee = 0.0
1067        self.browser.getControl("Save").click()
1068        self.browser.getControl("Add online payment ticket").click()
1069        self.assertMatches('...Amount could not be determined...',
1070                           self.browser.contents)
1071        self.configuration.transcript_fee = 200.0
1072        self.browser.getLink("Edit application record").click()
1073        self.browser.getControl("Add online payment ticket").click()
1074        self.assertMatches('...Payment ticket created...',
1075                           self.browser.contents)
1076        self.assertTrue(
1077            '<span>Transcript Fee</span>' in self.browser.contents)
1078        self.assertTrue(
1079            'This is a special app container' in self.browser.contents)
1080        self.assertTrue(
1081            '<span>200.0</span>' in self.browser.contents)
1082        self.assertEqual(len(self.special_applicant.keys()), 1)
1083        # The applicant's workflow state is paid ...
1084        self.special_applicant.payments[0].approveApplicantPayment()
1085        self.assertEqual(self.special_applicant.state, 'paid')
1086        self.browser.open(applicant_path + '/edit')
1087        # ... but he can create further tickets.
1088        self.browser.getControl(name="form.special_application").value = [
1089            'clearance']
1090        self.browser.getControl("Save").click()
1091        self.browser.getControl("Add online payment ticket").click()
1092        self.assertMatches('...Payment ticket created...',
1093                           self.browser.contents)
1094        self.browser.open(applicant_path)
1095        self.assertTrue(
1096            '<td>Acceptance Fee</td>' in self.browser.contents)
1097        self.assertEqual(len(self.special_applicant.keys()), 2)
1098        # Second payment can also be approved wthout error message
1099        flashtype, msg, log = self.special_applicant.payments[1].approveApplicantPayment()
1100        self.assertEqual(flashtype, 'success')
1101        self.assertEqual(msg, 'Payment approved')
1102        # Payment slips can't be downloaded ...
1103        payment_id = self.special_applicant.keys()[0]
1104        self.browser.open(applicant_path + '/' + payment_id)
1105        self.browser.getLink("Download payment slip").click()
1106        self.assertTrue(
1107            'Please submit the application form before trying to download payment slips.'
1108            in self.browser.contents)
1109        # ... unless form is submitted.
1110        self.browser.open(applicant_path + '/edit')
1111        image = open(SAMPLE_IMAGE, 'rb')
1112        ctrl = self.browser.getControl(name='form.passport')
1113        file_ctrl = ctrl.mech_control
1114        file_ctrl.add_file(image, filename='myphoto.jpg')
1115        self.browser.getControl(name="confirm_passport").value = True
1116        self.browser.getControl("Finally Submit").click()
1117        self.browser.open(applicant_path + '/' + payment_id)
1118        self.browser.getLink("Download payment slip").click()
1119        self.assertEqual(self.browser.headers['Content-Type'],
1120                 'application/pdf')
1121        return
1122
1123    def test_final_submit(self):
1124        # Make sure that a correctly filled form with passport picture
1125        # can be submitted (only) after payment
1126        self.login()
1127        self.browser.getLink("Edit application record").click()
1128        self.assertFalse('Finally Submit' in self.browser.contents)
1129        IWorkflowInfo(self.applicant).fireTransition('pay')
1130        self.browser.open(self.edit_path)
1131        self.assertTrue('Finally Submit' in self.browser.contents)
1132        self.fill_correct_values() # fill other fields with correct values
1133        self.browser.getControl("Save").click()
1134        self.browser.getControl("Finally Submit").click()
1135        # We forgot to upload a passport picture
1136        self.assertTrue(
1137            'No passport picture uploaded' in self.browser.contents)
1138        # Use a real image file and select it to be uploaded in form
1139        image = open(SAMPLE_IMAGE, 'rb')
1140        ctrl = self.browser.getControl(name='form.passport')
1141        file_ctrl = ctrl.mech_control
1142        file_ctrl.add_file(image, filename='myphoto.jpg')
1143        self.browser.getControl("Finally Submit").click() # (finally) submit form
1144        # The picture has been uploaded but the form cannot be submitted
1145        # since the passport confirmation box was not ticked
1146        self.assertTrue(
1147            'Passport picture confirmation box not ticked'
1148            in self.browser.contents)
1149        self.browser.getControl(name="confirm_passport").value = True
1150        # If application period has expired and strict-deadline is set
1151        # applicants do notsee edit button and can't open
1152        # the edit form.
1153        self.applicantscontainer.enddate = datetime.now(pytz.utc)
1154        self.browser.open(self.view_path)
1155        self.assertFalse(
1156            'Edit application record' in self.browser.contents)
1157        self.browser.open(self.edit_path)
1158        self.assertTrue(
1159            'form is locked' in self.browser.contents)
1160        # We can either postpone the enddate ...
1161        self.applicantscontainer.enddate = datetime.now(
1162            pytz.utc) + timedelta(days=10)
1163        self.browser.open(self.edit_path)
1164        self.browser.getControl(name="confirm_passport").value = True
1165        self.browser.getControl("Finally Submit").click()
1166        self.assertTrue(
1167            'Application submitted' in self.browser.contents)
1168        # ... or allow submission after deadline.
1169        IWorkflowState(self.applicant).setState('paid')
1170        self.applicant.locked = False
1171        self.applicantscontainer.strict_deadline = False
1172        self.browser.open(self.edit_path)
1173        self.browser.getControl(name="confirm_passport").value = True
1174        self.browser.getControl("Finally Submit").click()
1175        self.assertTrue(
1176            'Application submitted' in self.browser.contents)
1177        return
1178
1179    def test_locking(self):
1180        # Make sure that locked forms can't be submitted
1181        self.login()
1182        self.browser.open(self.edit_path)
1183        self.fill_correct_values() # fill other fields with correct values
1184        # Create a pseudo image file and select it to be uploaded in form
1185        pseudo_image = StringIO('I pretend to be a graphics file')
1186        ctrl = self.browser.getControl(name='form.passport')
1187        file_ctrl = ctrl.mech_control
1188        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
1189        self.browser.getControl("Save").click()
1190        # Now we lock the form
1191        self.applicant.locked = True
1192        self.browser.open(self.edit_path)
1193        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1194        self.assertTrue(
1195            'The requested form is locked' in self.browser.contents)
1196        return
1197
1198    def test_certificate_removed(self):
1199        self.login()
1200        self.browser.open(self.edit_path)
1201        self.fill_correct_values()
1202        self.browser.getControl("Save").click()
1203        self.browser.open(self.view_path)
1204        self.assertTrue(
1205            'Unnamed Certificate' in self.browser.contents)
1206        self.browser.open(self.edit_path)
1207        self.assertTrue(
1208            '<option selected="selected" value="CERT1">' in self.browser.contents)
1209        # Now we remove the certificate
1210        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1211        # The certificate is still shown in display mode
1212        self.browser.open(self.view_path)
1213        self.assertTrue(
1214            'Unnamed Certificate' in self.browser.contents)
1215        # The certificate is still selectable in edit mode so that it won't
1216        # be automatically replaced by another (arbitrary) certificate
1217        self.browser.open(self.edit_path)
1218        self.assertTrue(
1219            '<option selected="selected" value="CERT1">' in self.browser.contents)
1220        # Consequently, the certificate is still shown after saving the form
1221        self.browser.getControl("Save").click()
1222        self.browser.open(self.view_path)
1223        self.assertTrue(
1224            'Unnamed Certificate' in self.browser.contents)
1225        # Even if we add a new certificate the previous (removed)
1226        # certificate is shown
1227        certificate = createObject('waeup.Certificate')
1228        certificate.code = 'CERT2'
1229        certificate.title = 'New Certificate'
1230        certificate.application_category = 'basic'
1231        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1232            certificate)
1233        self.browser.open(self.edit_path)
1234        self.assertTrue(
1235            '<option selected="selected" value="CERT1">'
1236            in self.browser.contents)
1237
1238class ApplicantRegisterTests(ApplicantsFullSetup):
1239    # Tests for applicant registration
1240
1241    layer = FunctionalLayer
1242
1243    def test_register_applicant_create(self):
1244        config = grok.getSite()['configuration']
1245        config.maintmode_enabled_by = u'any_user'
1246        self.assertEqual(len(self.app['applicants'][container_name_1]), 1)
1247        # An applicant can register himself.
1248        self.browser.open(self.container_path)
1249        self.browser.getLink("Register for application").click()
1250        self.assertTrue(
1251            'The portal is in maintenance mode' in self.browser.contents)
1252        config.maintmode_enabled_by = None
1253        self.browser.getLink("Register for application").click()
1254        # The edit form now opens and can be filled with suitable values
1255        self.browser.getControl(name="form.firstname").value = 'Anna'
1256        self.browser.getControl(name="form.lastname").value = 'Kurios'
1257        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1258        self.browser.getControl(name="form.phone.country").value = ['+234']
1259        self.browser.getControl(name="form.phone.area").value = '555'
1260        self.browser.getControl(name="form.phone.ext").value = '6666666'
1261        self.browser.getControl("Send login credentials").click()
1262        self.assertEqual(self.browser.url,
1263            self.container_path + '/registration_complete?email=xx%40yy.zz')
1264        # A new applicant has been created
1265        self.assertEqual(len(self.app['applicants'][container_name_1]), 2)
1266        # The new applicant can be found in the catalog via the email address
1267        cat = getUtility(ICatalog, name='applicants_catalog')
1268        results = list(
1269            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
1270        applicant = results[0]
1271        self.assertEqual(applicant.lastname,'Kurios')
1272        # The application_id has been copied to the reg_number
1273        self.assertEqual(applicant.applicant_id, applicant.reg_number)
1274        # The applicant can be found in the catalog via the reg_number
1275        results = list(
1276            cat.searchResults(
1277            reg_number=(applicant.reg_number, applicant.reg_number)))
1278        self.assertEqual(applicant,results[0])
1279        return
1280
1281    def test_register_applicant_take_unused_record(self):
1282        # Create an unused record
1283        uu_applicant = createObject('waeup.Applicant')
1284        self.app['applicants'][container_name_1].addApplicant(uu_applicant)
1285        self.assertEqual(uu_applicant.container_code, container_name_1 + '-')
1286        self.assertEqual(len(self.app['applicants'][container_name_1]), 2)
1287        self.browser.open(self.container_path)
1288        self.browser.getLink("Register for application").click()
1289        # Fill the edit form with suitable values
1290        self.browser.getControl(name="form.firstname").value = 'Anna'
1291        self.browser.getControl(name="form.lastname").value = 'Kurios'
1292        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1293        self.browser.getControl(name="form.phone.country").value = ['+234']
1294        self.browser.getControl(name="form.phone.area").value = '555'
1295        self.browser.getControl(name="form.phone.ext").value = '6666666'
1296        self.browser.getControl("Send login credentials").click()
1297        # No applicant has been created ...
1298        self.assertEqual(len(self.app['applicants'][container_name_1]), 2)
1299        # ... and the existing, formerly unused record has been used instead
1300        self.assertEqual(uu_applicant.lastname, 'Kurios')
1301        self.assertEqual(uu_applicant.container_code, container_name_1 + '+')
1302        return
1303
1304    def test_register_applicant_update(self):
1305        # We change the application mode and check if applicants
1306        # can find and update imported records instead of creating new records.
1307        # First we check what happens if record does not exist.
1308        self.applicantscontainer.mode = 'update'
1309        self.browser.open(self.container_path + '/register')
1310        self.browser.getControl(name="form.lastname").value = 'Better'
1311        self.browser.getControl(name="form.reg_number").value = 'anynumber'
1312        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1313        self.browser.getControl("Send login credentials").click()
1314        self.assertTrue('No application record found.'
1315            in self.browser.contents)
1316        # Even with the correct reg_number we can't register
1317        # because lastname attribute is not set.
1318        self.applicantscontainer.mode = 'update'
1319        self.browser.open(self.container_path + '/register')
1320        self.browser.getControl(name="form.lastname").value = 'Better'
1321        self.browser.getControl(name="form.reg_number").value = '1234'
1322        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1323        self.browser.getControl("Send login credentials").click()
1324        self.assertTrue('An error occurred.' in self.browser.contents)
1325        # Let's set this attribute manually
1326        # and try to register with a wrong name.
1327        self.applicant.lastname = u'Better'
1328        self.browser.open(self.container_path + '/register')
1329        self.browser.getControl(name="form.lastname").value = 'Worse'
1330        self.browser.getControl(name="form.reg_number").value = '1234'
1331        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1332        self.browser.getControl("Send login credentials").click()
1333        # Anonymous is not informed that lastname verification failed.
1334        # It seems that the record doesn't exist.
1335        self.assertTrue('No application record found.'
1336            in self.browser.contents)
1337        # Even with the correct lastname we can't register if a
1338        # password has been set and used.
1339        IWorkflowState(self.applicant).setState('started')
1340        self.browser.getControl(name="form.lastname").value = 'Better'
1341        self.browser.getControl(name="form.reg_number").value = '1234'
1342        self.browser.getControl("Send login credentials").click()
1343        self.assertTrue('Your password has already been set and used.'
1344            in self.browser.contents)
1345        #IUserAccount(
1346        #    self.app['applicants'][container_name_1][
1347        #    self.applicant.application_number]).context.password = None
1348        # Even without unsetting the password we can re-register if state
1349        # is 'initialized'
1350        IWorkflowState(self.applicant).setState('initialized')
1351        self.browser.open(self.container_path + '/register')
1352        # The lastname field, used for verification, is not case-sensitive.
1353        self.browser.getControl(name="form.lastname").value = 'bEtter'
1354        self.browser.getControl(name="form.reg_number").value = '1234'
1355        self.browser.getControl(name="form.email").value = 'new@yy.zz'
1356        self.browser.getControl("Send login credentials").click()
1357        # Yeah, we succeded ...
1358        self.assertTrue('Your registration was successful.'
1359            in self.browser.contents)
1360        # ... and  applicant can be found in the catalog via the email address
1361        cat = getUtility(ICatalog, name='applicants_catalog')
1362        results = list(
1363            cat.searchResults(
1364            email=('new@yy.zz', 'new@yy.zz')))
1365        self.assertEqual(self.applicant,results[0])
1366        return
1367
1368    def test_change_password_request(self):
1369        self.browser.open('http://localhost/app/changepw')
1370        self.browser.getControl(name="form.identifier").value = '1234'
1371        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1372        self.browser.getControl("Send login credentials").click()
1373        self.assertTrue('No record found' in self.browser.contents)
1374        self.applicant.email = 'aa@aa.ng'
1375        # Update the catalog
1376        notify(grok.ObjectModifiedEvent(self.applicant))
1377        self.browser.open('http://localhost/app/changepw')
1378        self.browser.getControl(name="form.identifier").value = '1234'
1379        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1380        self.browser.getControl("Send login credentials").click()
1381        self.assertTrue(
1382            'An email with your user name and password has been sent'
1383            in self.browser.contents)
1384
1385    def test_check_status(self):
1386        self.applicant.lastname = u'Lion '
1387        self.browser.open('http://localhost/app/applicants/checkstatus')
1388        self.browser.getControl(name="unique_id").value = 'nonsense'
1389        self.browser.getControl(name="lastname").value = 'Lion'
1390        self.browser.getControl("Submit").click()
1391        self.assertTrue('No application record found' in self.browser.contents)
1392        self.browser.getControl(
1393          name="unique_id").value = self.applicant.applicant_id
1394        self.browser.getControl(name="lastname").value = 'nonsense'
1395        self.browser.getControl("Submit").click()
1396        self.assertTrue('No application record found' in self.browser.contents)
1397        self.browser.getControl(
1398          name="unique_id").value = self.applicant.applicant_id
1399        self.browser.getControl(name="lastname").value = 'Lion'
1400        self.browser.getControl("Submit").click()
1401        self.assertTrue('Admission status of' in self.browser.contents)
1402        self.assertTrue(
1403          'You have not yet submitted your application' in self.browser.contents)
1404        IWorkflowState(self.applicant).setState('admitted')
1405        self.browser.open('http://localhost/app/applicants/checkstatus')
1406        self.browser.getControl(
1407          name="unique_id").value = self.applicant.applicant_id
1408        # Whitespaces are ignored.
1409        self.browser.getControl(name="lastname").value = 'Lion'
1410        self.browser.getControl("Submit").click()
1411        self.assertTrue('Congratulations!' in self.browser.contents)
1412        self.assertFalse('Study Course' in self.browser.contents)
1413        self.applicant.course_admitted = self.certificate
1414        self.browser.open('http://localhost/app/applicants/checkstatus')
1415        self.browser.getControl(
1416          name="unique_id").value = self.applicant.applicant_id
1417        self.browser.getControl(name="lastname").value = 'Lion'
1418        self.browser.getControl("Submit").click()
1419        self.assertTrue('Congratulations!' in self.browser.contents)
1420        self.assertTrue('Unnamed Certificate (CERT1)' in self.browser.contents)
1421        self.assertTrue(
1422          'Department of Unnamed Department (dep1)' in self.browser.contents)
1423        self.assertTrue(
1424          'Faculty of Unnamed Faculty (NA)' in self.browser.contents)
1425        # Also the reg_number can be used and page shows student id and password
1426        # if applicant is in state created.
1427        IWorkflowState(self.applicant).setState('created')
1428        self.applicant.student_id = u'my id'
1429        self.browser.open('http://localhost/app/applicants/checkstatus')
1430        self.browser.getControl(
1431          name="unique_id").value = self.applicant.reg_number
1432        self.browser.getControl(name="lastname").value = 'Lion'
1433        self.browser.getControl("Submit").click()
1434        self.assertTrue('Congratulations!' in self.browser.contents)
1435        self.assertTrue('Unnamed Certificate (CERT1)' in self.browser.contents)
1436        self.assertTrue('Department of Unnamed Department (dep1)'
1437            in self.browser.contents)
1438        self.assertTrue(
1439          'Faculty of Unnamed Faculty (NA)' in self.browser.contents)
1440        self.assertTrue('user name (= student id) is: <strong>my id</strong>'
1441            in self.browser.contents)
1442        self.assertTrue(
1443          'password is: <strong>%s</strong>' % self.applicant.application_number
1444          in self.browser.contents)
1445       
1446
1447class ApplicantsExportTests(ApplicantsFullSetup, FunctionalAsyncTestCase):
1448    # Tests for StudentsContainer class views and pages
1449
1450    layer = FunctionalLayer
1451
1452    def wait_for_export_jobs_completed(self):
1453        # helper function waiting until the current export job is completed
1454        manager = getUtility(IJobManager)
1455        job_ids = [i[0] for i in self.app['datacenter'].running_exports]
1456        jobs = [manager.get(job_id) for job_id in job_ids]
1457        for job in jobs:
1458            wait_for_result(job)
1459        return job_ids
1460
1461    def test_applicants_in_container_export(self):
1462        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1463        container_path = 'http://localhost/app/applicants/%s' % container_name_1
1464        self.browser.open(container_path)
1465        self.browser.getLink("Export application data").click()
1466        self.browser.getControl("Start new exports").click()
1467        job_ids = self.wait_for_export_jobs_completed()
1468        # Two exports were created
1469        self.assertEqual(len(self.app['datacenter'].running_exports), 2)
1470       # When the jobs are finished and we reload the page...
1471        self.browser.open(container_path + '/exports')
1472        # ... the both csv files can be downloaded ...
1473        self.browser.getLink("Download", index=0).click()
1474        self.assertEqual(self.browser.headers['content-type'],
1475            'text/csv; charset=UTF-8')
1476        self.assertTrue(
1477            'filename="WAeUP.Kofa_applicants_%s.csv' % job_ids[0] in
1478            self.browser.headers['content-disposition'])
1479        self.browser.open(container_path + '/exports')
1480        self.browser.getLink("Download", index=1).click()
1481        self.assertEqual(self.browser.headers['content-type'],
1482            'text/csv; charset=UTF-8')
1483        self.assertTrue(
1484            'filename="WAeUP.Kofa_applicantpayments_%s.csv' % job_ids[1] in
1485            self.browser.headers['content-disposition'])
1486        # ... and discarded
1487        self.browser.open(container_path + '/exports')
1488        self.browser.getControl("Discard", index=0).click()
1489        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
1490        self.browser.getControl("Discard").click()
1491        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
1492        # Creation, downloading and discarding are logged
1493        logfile = os.path.join(
1494            self.app['datacenter'].storage, 'logs', 'datacenter.log')
1495        logcontent = open(logfile).read()
1496        self.assertTrue(
1497            'zope.mgr - applicants.browser.ExportJobContainerJobStart - '
1498            'exported: applicants (%s), job_id=%s'
1499            % (container_name_1, job_ids[0]) in logcontent
1500            )
1501        self.assertTrue(
1502            'zope.mgr - applicants.browser.ExportJobContainerDownload '
1503            '- downloaded: WAeUP.Kofa_applicants_%s.csv, job_id=%s'
1504            % (job_ids[0], job_ids[0]) in logcontent
1505            )
1506        self.assertTrue(
1507            'zope.mgr - applicants.browser.ExportJobContainerOverview '
1508            '- discarded: job_id=%s' % job_ids[0] in logcontent
1509            )
1510        self.assertTrue(
1511            'zope.mgr - applicants.browser.ExportJobContainerJobStart - '
1512            'exported: applicantpayments (%s), job_id=%s'
1513            % (container_name_1, job_ids[1]) in logcontent
1514            )
1515        self.assertTrue(
1516            'zope.mgr - applicants.browser.ExportJobContainerDownload '
1517            '- downloaded: WAeUP.Kofa_applicantpayments_%s.csv, job_id=%s'
1518            % (job_ids[1], job_ids[1]) in logcontent
1519            )
1520        self.assertTrue(
1521            'zope.mgr - applicants.browser.ExportJobContainerOverview '
1522            '- discarded: job_id=%s' % job_ids[1] in logcontent
1523            )
1524
1525class ApplicantRefereeReportTests(ApplicantsFullSetup, FunctionalAsyncTestCase):
1526    # Tests for ApplicantRefereeReport class views and pages
1527
1528    layer = FunctionalLayer
1529
1530    def setUp(self):
1531        super(ApplicantRefereeReportTests, self).setUp()
1532        self.setup_logging()
1533        return
1534
1535    def tearDown(self):
1536        super(ApplicantRefereeReportTests, self).tearDown()
1537        self.teardown_logging()
1538        return
1539
1540    def setup_logging(self):
1541        # setup a log-handler that catches all fake mailer output
1542        self.stream = StringIO()
1543        handler = logging.StreamHandler(self.stream)
1544        logger = logging.getLogger('test.smtp')
1545        logger.addHandler(handler)
1546        logger.setLevel(logging.INFO)
1547        return
1548
1549    def get_fake_smtp_output(self):
1550        # get output generated by fake mailer
1551        self.stream.flush()
1552        self.stream.seek(0)
1553        return self.stream.read()
1554
1555    def teardown_logging(self):
1556        # remove the log handler for fake mailer output
1557        logger = logging.getLogger('test.smtp')
1558        handlers = [x for x in logger.handlers]
1559        for handler in handlers:
1560            logger.removeHandler(handler)
1561        return
1562
1563    def test_refereereport_mandate(self):
1564        mandate = RefereeReportMandate()
1565        mandate.params['name'] = u'John Referee'
1566        mandate.params['email'] = 'aa@aa.aa'
1567        mandate.params[
1568            'redirect_path'] = '/applicants/%s/%s/addrefereereport' % (
1569                container_name_1, self.applicant.application_number)
1570        self.app['mandates'].addMandate(mandate)
1571        # Let's open the add form page via the mandate view
1572        self.browser.open('http://localhost/app/mandate?mandate_id=%s'
1573            % mandate.mandate_id)
1574        # Form page opens and is prefilled
1575        self.assertEqual(
1576            self.browser.url,
1577            'http://localhost/app/applicants/%s/%s/addrefereereport?mandate_id=%s'
1578            % (container_name_1, self.applicant.application_number,
1579            mandate.mandate_id))
1580        self.assertTrue('value="John Referee"' in self.browser.contents)
1581        # Let's open the page directly with an invalid mandate
1582        self.browser.open(
1583            'http://localhost/app/applicants/%s/%s/addrefereereport?mandate_id=wrongmadate'
1584            % (container_name_1, self.applicant.application_number))
1585        self.assertTrue('<div class="alert alert-warning">No mandate.</div>'
1586            in self.browser.contents)
1587        self.assertEqual(self.browser.url, 'http://localhost/app')
1588        # Page is also blocked in maintenance mode
1589        grok.getSite()['configuration'].maintmode_enabled_by = u'anybody'
1590        self.browser.open(
1591            'http://localhost/app/applicants/%s/%s/addrefereereport?mandate_id=%s'
1592            % (container_name_1, self.applicant.application_number,
1593            mandate.mandate_id))
1594        self.assertTrue('<div class="alert alert-warning">The portal is '
1595                        'in maintenance mode'
1596            in self.browser.contents)
1597        self.assertEqual(self.browser.url, 'http://localhost/app')
1598
1599    def test_add_and_view_reports(self):
1600        mandate = RefereeReportMandate()
1601        mandate.params['name'] = u'John Referee'
1602        mandate.params['email'] = 'aa@aa.aa'
1603        mandate.params[
1604            'redirect_path'] = '/applicants/%s/%s/addrefereereport' % (
1605                container_name_1, self.applicant.application_number)
1606        self.app['mandates'].addMandate(mandate)
1607        self.assertEqual(len(self.app['mandates'].keys()), 1)
1608        # Let's open the add form page via the mandate view
1609        self.browser.open('http://localhost/app/mandate?mandate_id=%s'
1610            % mandate.mandate_id)
1611        self.assertTrue('Joan None' in self.browser.contents)
1612        # Report can't be saved without required fields
1613        self.browser.getControl(name="form.email").value = ''
1614        self.browser.getControl("Submit").click()
1615        self.assertTrue('Required input is missing' in self.browser.contents)
1616        self.browser.getControl(name="form.email").value = 'bb@bb.bb'
1617        self.browser.getControl("Submit").click()
1618        self.assertTrue('Referee report has been saved' in self.browser.contents)
1619        self.assertEqual(self.browser.url, 'http://localhost/app')
1620        # Report has been created
1621        self.assertEqual(len(self.applicant.refereereports), 1)
1622        report = self.applicant.refereereports[0]
1623        # Managers can view the report
1624        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1625        self.browser.open(self.manage_path)
1626        self.browser.getLink("%s" % report.r_id).click()
1627        self.assertEqual(
1628            self.browser.url, self.view_path  + '/%s' % report.r_id)
1629        self.assertTrue('John Referee' in self.browser.contents)
1630        # Report creation is logged
1631        logfile = os.path.join(
1632            self.app['datacenter'].storage, 'logs', 'applicants.log')
1633        logcontent = open(logfile).read()
1634        self.assertTrue(
1635            'zope.anybody - applicants.browser.RefereeReportAddFormPage - '
1636            '%s - added: %s\n' % (self.applicant.applicant_id, report.r_id)
1637            in logcontent
1638            )
1639        # Mandate is deleted
1640        self.assertEqual(len(self.app['mandates'].keys()), 0)
1641
1642    def test_final_submit_with_referees(self):
1643        # Add two referees
1644        referee1 = RefereeEntry()
1645        referee2 = RefereeEntry()
1646        referee1.name = u'Linda Tree'
1647        referee1.email = 'linda@forest.de'
1648        referee2.name = u'Otis Stone'
1649        referee2.email = 'otis@stones.de'
1650        self.applicant.referees = [referee1, referee2]
1651        self.assertFalse(referee1.email_sent)
1652        self.assertFalse(referee2.email_sent)
1653        self.login()
1654        IWorkflowInfo(self.applicant).fireTransition('pay')
1655        self.browser.open(self.edit_path)
1656        self.fill_correct_values() # fill other fields with correct values
1657        image = open(SAMPLE_IMAGE, 'rb')
1658        ctrl = self.browser.getControl(name='form.passport')
1659        file_ctrl = ctrl.mech_control
1660        file_ctrl.add_file(image, filename='myphoto.jpg')
1661        self.browser.getControl("Save").click()
1662        self.browser.getControl(name="confirm_passport").value = True
1663        self.browser.getControl("Finally Submit").click()
1664        mandate_id_0 = self.app['mandates'].keys()[0]
1665        mandate_id_1 = self.app['mandates'].keys()[1]
1666        if self.app['mandates'].values()[0].params['name'] == 'Linda Tree':
1667            mandate_id_0 = self.app['mandates'].keys()[0]
1668            mandate_id_1 = self.app['mandates'].keys()[1]
1669        else:
1670            mandate_id_0 = self.app['mandates'].keys()[1]
1671            mandate_id_1 = self.app['mandates'].keys()[0]
1672        self.assertMatches(
1673            'Sending email from no-reply@waeup.org to linda@forest.de:'
1674            '\nMessage:'
1675            '\nmsg: MIME-Version: 1.0\nmsg: Content-Type: text/plain; charset="us-ascii"'
1676            '\nmsg: Content-Transfer-Encoding: 7bit'
1677            '\nmsg: From: Administrator <no-reply@waeup.org>'
1678            '\nmsg: To: Linda Tree <linda@forest.de>'
1679            '\nmsg: Reply-To: Administrator <contact@waeup.org>'
1680            '\nmsg: Subject: Request for referee report from Sample University'
1681            '\nmsg: '
1682            '\nmsg: Dear Linda Tree,'
1683            '\nmsg: '
1684            '\nmsg: The candidate with Id app2014_372052 and name John Anthony Tester applied to'
1685            '\nmsg: the Sample University to study Unnamed Certificate for the 2014/2015 session.'
1686            '\nmsg: The candidate has listed you as referee. You are thus required to kindly use'
1687            '\nmsg: the link below to provide your referral remarks on or before'
1688            '\nmsg: 2016-08-12 08:32:41.619671+00:00.'
1689            '\nmsg: '
1690            '\nmsg: Report link: http://localhost/app/mandate?mandate_id=%s'
1691            '\nmsg: '
1692            '\nmsg: Thank You'
1693            '\nmsg: '
1694            '\nmsg: The Secretary'
1695            '\nmsg: Post Graduate School'
1696            '\nmsg: Sample University'
1697            '\nmsg: '
1698            '\nSending email from no-reply@waeup.org to otis@stones.de:'
1699            '\nMessage:'
1700            '\nmsg: MIME-Version: 1.0'
1701            '\nmsg: Content-Type: text/plain; charset="us-ascii"'
1702            '\nmsg: Content-Transfer-Encoding: 7bit'
1703            '\nmsg: From: Administrator <no-reply@waeup.org>'
1704            '\nmsg: To: Otis Stone <otis@stones.de>'
1705            '\nmsg: Reply-To: Administrator <contact@waeup.org>'
1706            '\nmsg: Subject: Request for referee report from Sample University'
1707            '\nmsg: '
1708            '\nmsg: Dear Otis Stone,'
1709            '\nmsg: '
1710            '\nmsg: The candidate with Id app2014_<6-DIGITS> and name John Anthony Tester applied to'
1711            '\nmsg: the Sample University to study Unnamed Certificate for the 2014/2015 session.'
1712            '\nmsg: The candidate has listed you as referee. You are thus required to kindly use'
1713            '\nmsg: the link below to provide your referral remarks on or before'
1714            '\nmsg: <YYYY-MM-DD hh:mm:ss>.<6-DIGITS>+00:00.'
1715            '\nmsg: '
1716            '\nmsg: Report link: http://localhost/app/mandate?mandate_id=%s'
1717            '\nmsg: '
1718            '\nmsg: Thank You'
1719            '\nmsg: '
1720            '\nmsg: The Secretary'
1721            '\nmsg: Post Graduate School'
1722            '\nmsg: Sample University'
1723            '\nmsg: ' % (mandate_id_0, mandate_id_1),
1724            self.get_fake_smtp_output()
1725            )
1726        self.assertTrue(
1727            'Application submitted' in self.browser.contents)
1728        self.assertTrue(
1729            'Form has been successfully submitted and 2 '
1730            'invitation emails were sent.' in self.browser.contents)
1731        logfile = os.path.join(
1732            self.app['datacenter'].storage, 'logs', 'applicants.log')
1733        logcontent = open(logfile).read()
1734        self.assertTrue(
1735            '%s - applicants.browser.ApplicantEditFormPage - %s - '
1736            'email sent: otis@stones.de' %
1737            (self.applicant.applicant_id, self.applicant.applicant_id)
1738            in logcontent)
1739        self.assertTrue(referee1.email_sent)
1740        self.assertTrue(referee2.email_sent)
1741        # If the form is being resubmitted, no more emails will be sent
1742        IWorkflowState(self.applicant).setState('paid')
1743        self.applicant.locked = False
1744        self.browser.open(self.edit_path)
1745        self.browser.getControl(name="confirm_passport").value = True
1746        self.browser.getControl("Finally Submit").click()
1747        self.assertTrue(
1748            'Form has been successfully submitted and 0 '
1749            'invitation emails were sent.' in self.browser.contents)
1750        return
Note: See TracBrowser for help on using the repository browser.