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

Last change on this file since 10907 was 10895, checked in by Henrik Bettermann, 11 years ago

Flash message if student record is created.

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