source: main/waeup.kofa/branches/0.2/src/waeup/kofa/applicants/tests/test_browser.py @ 10888

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

Make tests turn-of-the-year-resistant.

  • Property svn:keywords set to Id
File size: 61.8 KB
Line 
1## $Id: test_browser.py 10874 2014-01-07 07:32:50Z 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 image_url(self, filename):
569        return self.edit_path.replace('edit', filename)
570
571    def test_after_login_default_browsable(self):
572        # After login we see the placeholder image in the edit view
573        self.login()
574        self.assertEqual(self.browser.url, self.view_path)
575        self.browser.open(self.edit_path)
576        # There is a correct <img> link included
577        self.assertTrue(
578              '<img src="passport.jpg" height="180px" />' in self.browser.contents)
579        # Browsing the link shows a real image
580        self.browser.open(self.image_url('passport.jpg'))
581        self.assertEqual(
582            self.browser.headers['content-type'], 'image/jpeg')
583        self.assertEqual(len(self.browser.contents), PH_LEN)
584
585    def test_after_submit_default_browsable(self):
586        # After submitting an applicant form the default image is
587        # still visible
588        self.login()
589        self.browser.open(self.edit_path)
590        self.browser.getControl("Save").click() # submit form
591        # There is a correct <img> link included
592        self.assertTrue(
593            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
594        # Browsing the link shows a real image
595        self.browser.open(self.image_url('passport.jpg'))
596        self.assertEqual(
597            self.browser.headers['content-type'], 'image/jpeg')
598        self.assertEqual(len(self.browser.contents), PH_LEN)
599
600    def test_uploaded_image_respects_file_size_restriction(self):
601        # When we upload an image that is too big ( > 10 KB) we will
602        # get an error message
603        self.login()
604        self.browser.open(self.edit_path)
605        # Create a pseudo image file and select it to be uploaded in form
606        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
607        pseudo_image = StringIO(photo_content)
608        ctrl = self.browser.getControl(name='form.passport')
609        file_ctrl = ctrl.mech_control
610        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
611        self.browser.getControl("Save").click() # submit form
612        # There is a correct <img> link included
613        self.assertTrue(
614            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
615        # We get a warning message
616        self.assertTrue(
617            'Uploaded image is too big' in self.browser.contents)
618        # Browsing the image gives us the default image, not the
619        # uploaded one.
620        self.browser.open(self.image_url('passport.jpg'))
621        self.assertEqual(
622            self.browser.headers['content-type'], 'image/jpeg')
623        self.assertEqual(len(self.browser.contents), PH_LEN)
624        # There is really no file stored for the applicant
625        img = getUtility(IExtFileStore).getFile(
626            IFileStoreNameChooser(self.applicant).chooseName())
627        self.assertTrue(img is None)
628
629    def test_uploaded_image_browsable_w_errors(self):
630        # We can upload a different image and browse it after submit,
631        # even if there are still errors in the form
632        self.login()
633        self.browser.open(self.edit_path)
634        # Create a pseudo image file and select it to be uploaded in form
635        photo_content = 'I pretend to be a graphics file'
636        pseudo_image = StringIO(photo_content)
637        ctrl = self.browser.getControl(name='form.passport')
638        file_ctrl = ctrl.mech_control
639        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
640        self.browser.getControl("Save").click() # submit form
641        # There is a correct <img> link included
642        self.assertTrue(
643            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
644        # Browsing the link shows a real image
645        self.browser.open(self.image_url('passport.jpg'))
646        self.assertEqual(
647            self.browser.headers['content-type'], 'image/jpeg')
648        self.assertEqual(self.browser.contents, photo_content)
649
650    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
651        # After uploading a new passport pic the file is correctly
652        # stored in an imagestorage
653        self.login()
654        self.browser.open(self.edit_path)
655        # Create a pseudo image file and select it to be uploaded in form
656        pseudo_image = StringIO('I pretend to be a graphics file')
657        ctrl = self.browser.getControl(name='form.passport')
658        file_ctrl = ctrl.mech_control
659        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
660        self.browser.getControl("Save").click() # submit form
661        storage = getUtility(IExtFileStore)
662        file_id = IFileStoreNameChooser(self.applicant).chooseName()
663        pseudo_image.seek(0) # reset our file data source
664        self.assertEqual(
665            storage.getFile(file_id).read(), pseudo_image.read())
666        return
667
668    def test_uploaded_image_browsable_wo_errors(self):
669        # We can upload a different image and browse it after submit,
670        # if there are no errors in form
671        self.login()
672        self.browser.open(self.edit_path)
673        self.fill_correct_values() # fill other fields with correct values
674        # Create a pseudo image file and select it to be uploaded in form
675        pseudo_image = StringIO('I pretend to be a graphics file')
676        ctrl = self.browser.getControl(name='form.passport')
677        file_ctrl = ctrl.mech_control
678        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
679        self.browser.getControl("Save").click() # submit form
680        # There is a correct <img> link included
681        self.assertTrue(
682            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
683        # Browsing the link shows a real image
684        self.browser.open(self.image_url('passport.jpg'))
685        self.assertEqual(
686            self.browser.headers['content-type'], 'image/jpeg')
687        self.assertEqual(len(self.browser.contents), 31)
688
689    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
690        # After uploading a new passport pic the file is correctly
691        # stored in an imagestorage if form contains no errors
692        self.login()
693        self.browser.open(self.edit_path)
694        self.fill_correct_values() # fill other fields with correct values
695        # Create a pseudo image file and select it to be uploaded in form
696        pseudo_image = StringIO('I pretend to be a graphics file')
697        ctrl = self.browser.getControl(name='form.passport')
698        file_ctrl = ctrl.mech_control
699        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
700        self.browser.getControl("Save").click() # submit form
701        storage = getUtility(IExtFileStore)
702        file_id = IFileStoreNameChooser(self.applicant).chooseName()
703        # The stored image can be fetched
704        fd = storage.getFile(file_id)
705        file_len = len(fd.read())
706        self.assertEqual(file_len, 31)
707        # When an applicant is removed, also the image is gone.
708        del self.app['applicants'][container_name_1][self.applicant.application_number]
709        fd = storage.getFile(file_id)
710        self.assertTrue(fd is None)
711
712    def test_uploaded_images_equal(self):
713        # Make sure uploaded images do really differ if we eject a
714        # change notfication (and do not if we don't)
715        self.login()
716        self.browser.open(self.edit_path)
717        self.fill_correct_values() # fill other fields with correct values
718        self.browser.getControl("Save").click() # submit form
719        # Now go on as an officer
720        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
721        self.browser.open(self.manage_path)
722
723        # Create a pseudo image file and select it to be uploaded in form
724        pseudo_image = StringIO('I pretend to be a graphics file')
725        ctrl = self.browser.getControl(name='form.passport')
726        file_ctrl = ctrl.mech_control
727        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
728        file_id = IFileStoreNameChooser(self.applicant).chooseName()
729        setSite(self.app)
730        passport0 = getUtility(IExtFileStore).getFile(file_id)
731        self.browser.getControl("Save").click() # submit form with changed pic
732        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
733        self.browser.getControl("Save").click() # submit form w/o changes
734        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
735        self.assertTrue(passport0 is None)
736        self.assertTrue(passport0 != passport1)
737        self.assertTrue(passport1 == passport2)
738        return
739
740    def test_upload_image_by_manager_with_logging(self):
741        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
742        self.browser.open(self.manage_path)
743        # Create a pseudo image file and select it to be uploaded in form
744        photo_content = 'A' * 1024 * 5  # A string of 5 KB size
745        pseudo_image = StringIO(photo_content)
746        ctrl = self.browser.getControl(name='form.passport')
747        file_ctrl = ctrl.mech_control
748        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
749        self.browser.getControl("Save").click() # submit form
750        # Even though the form could not be saved ...
751        self.assertTrue(
752            'Required input is missing' in self.browser.contents)
753        # ... the file has been successfully uploaded
754        logfile = os.path.join(
755            self.app['datacenter'].storage, 'logs', 'applicants.log')
756        logcontent = open(logfile).read()
757        self.assertTrue(
758            'zope.mgr - applicants.browser.ApplicantManageFormPage - '
759            '%s - saved: passport'
760            % (self.applicant.applicant_id)
761            in logcontent)
762
763    def test_pay_portal_application_fee(self):
764        self.login()
765        self.browser.open(self.edit_path)
766        # Payment tickets can't be created before the form has been validated
767        self.browser.getControl("Add online payment ticket").click()
768        self.assertTrue('Required input is missing' in self.browser.contents)
769        self.fill_correct_values()
770        # We have to save the form otherwise the filled fields will be cleared
771        # after adding an online payment, because adding an online payment
772        # requires a filled form but does not save it
773        self.browser.getControl("Save").click()
774        self.browser.getControl("Add online payment ticket").click()
775        # Session object missing
776        self.assertTrue(
777            'Session configuration object is not available'
778            in self.browser.contents)
779        configuration = SessionConfiguration()
780        configuration.academic_session = session_1
781        configuration.application_fee = 200.0
782        self.app['configuration'].addSessionConfiguration(configuration)
783        self.browser.open(self.edit_path)
784        self.browser.getControl("Add online payment ticket").click()
785        self.assertMatches('...Payment ticket created...',
786                           self.browser.contents)
787        self.assertMatches('...Activation Code...',
788                           self.browser.contents)
789        # Payment ticket can be removed if they haven't received a
790        # valid callback
791        self.browser.open(self.edit_path)
792        ctrl = self.browser.getControl(name='val_id')
793        value = ctrl.options[0]
794        ctrl.getControl(value=value).selected = True
795        self.browser.getControl("Remove selected", index=0).click()
796        self.assertMatches('...Successfully removed...', self.browser.contents)
797        # We will try the callback request view
798        self.browser.getControl("Add online payment ticket").click()
799        self.browser.open(self.edit_path)
800        ctrl = self.browser.getControl(name='val_id')
801        value = ctrl.options[0]
802        self.browser.getLink(value).click()
803        self.assertMatches('...Amount Authorized...',
804                           self.browser.contents)
805        payment_url = self.browser.url
806        payment_id = self.applicant.keys()[0]
807        payment = self.applicant[payment_id]
808        self.assertEqual(payment.p_item,'This is the %s container' % container_name_1)
809        self.assertEqual(payment.p_session, session_1)
810        self.assertEqual(payment.p_category,'application')
811        self.assertEqual(payment.amount_auth,200.0)
812        # Applicant is payer of the payment ticket.
813        self.assertEqual(
814            IPayer(payment).display_fullname, 'John Anthony Tester')
815        self.assertEqual(
816            IPayer(payment).id, self.applicant.applicant_id)
817        self.assertEqual(IPayer(payment).faculty, 'N/A')
818        self.assertEqual(IPayer(payment).department, 'N/A')
819        # The pdf payment slip can't yet be opened
820        #self.browser.open(payment_url + '/payment_receipt.pdf')
821        #self.assertMatches('...Ticket not yet paid...',
822        #                   self.browser.contents)
823        # Approve payment
824        # Applicants can't approve payments
825        self.assertRaises(
826            Unauthorized, self.browser.open, payment_url + '/approve')
827        # We approve the payment by bypassing the view
828        payment.approve()
829        # The payment slip can be downloaded now
830        self.browser.open(payment_url)
831        self.browser.getLink("Download payment slip").click()
832        self.assertEqual(self.browser.headers['Status'], '200 Ok')
833        self.assertEqual(self.browser.headers['Content-Type'],
834                         'application/pdf')
835        # Applicant is is not yet in state 'paid' because it was only
836        # the payment which we set to paid
837        self.browser.open(self.view_path)
838        self.assertMatches('...started...',
839                           self.browser.contents)
840        self.assertTrue(self.applicant.state == 'started')
841        # Let's logout and approve the payment as manager
842        self.browser.getLink("Logout").click()
843        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
844        # First we reset the payment
845        payment.r_amount_approved = 0.0
846        payment.r_code = u''
847        payment.p_state = 'unpaid'
848        payment.r_desc = u''
849        payment.payment_date = None
850        self.browser.open(payment_url)
851        self.browser.getLink("Approve payment").click()
852        self.assertEqual(payment.p_state, 'paid')
853        self.assertEqual(payment.r_amount_approved, 200.0)
854        self.assertEqual(payment.r_code, 'AP')
855        self.assertTrue(self.applicant.state == 'paid')
856        # Approval is logged in students.log ...
857        logfile = os.path.join(
858            self.app['datacenter'].storage, 'logs', 'applicants.log')
859        logcontent = open(logfile).read()
860        self.assertTrue(
861            'zope.mgr - applicants.browser.OnlinePaymentApprovePage - '
862            '%s - payment approved' % self.applicant.applicant_id
863            in logcontent)
864        # ... and in payments.log
865        logfile = os.path.join(
866            self.app['datacenter'].storage, 'logs', 'payments.log')
867        logcontent = open(logfile).read()
868        self.assertTrue(
869            '"zope.mgr",%s,%s,application,200.0,AP,,,,,,\n'
870            % (self.applicant.applicant_id, payment.p_id)
871            in logcontent)
872        return
873
874    def test_pay_container_application_fee(self):
875        self.login()
876        self.browser.open(self.edit_path)
877        self.fill_correct_values()
878        self.browser.getControl("Save").click()
879        configuration = SessionConfiguration()
880        configuration.academic_session = session_1
881        self.applicantscontainer.application_fee = 120.0
882        configuration.application_fee = 9999.9
883        self.app['configuration'].addSessionConfiguration(configuration)
884        self.browser.open(self.edit_path)
885        self.browser.getControl("Add online payment ticket").click()
886        self.assertMatches('...Payment ticket created...',
887                           self.browser.contents)
888        self.assertMatches('...Payment ticket created...',
889                           self.browser.contents)
890        self.assertFalse(
891            '<span>9999.9</span>' in self.browser.contents)
892        self.assertTrue(
893            '<span>120.0</span>' in self.browser.contents)
894        self.assertTrue(
895            '<span>Application Fee</span>' in self.browser.contents)
896        return
897
898    def prepare_special_container(self):
899        # Add special application container
900        container_name = u'special%s' % session_1
901        applicantscontainer = ApplicantsContainer()
902        applicantscontainer.code = container_name
903        applicantscontainer.prefix = 'special'
904        applicantscontainer.year = session_1
905        applicantscontainer.title = u'This is a special app container'
906        applicantscontainer.application_category = 'no'
907        applicantscontainer.mode = 'create'
908        applicantscontainer.strict_deadline = True
909        delta = timedelta(days=10)
910        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
911        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
912        self.app['applicants'][container_name] = applicantscontainer
913        # Add an applicant
914        applicant = createObject('waeup.Applicant')
915        # reg_number is the only field which has to be preset here
916        # because managers are allowed to edit this required field
917        applicant.reg_number = u'12345'
918        self.special_applicant = applicant
919        self.app['applicants'][container_name].addApplicant(applicant)
920        IUserAccount(
921            self.app['applicants'][container_name][
922            applicant.application_number]).setPassword('apwd')
923        # Add session configuration object
924        configuration = SessionConfiguration()
925        configuration.academic_session = session_1
926        configuration.transcript_fee = 200.0
927        self.app['configuration'].addSessionConfiguration(configuration)
928
929
930    def test_pay_special_fee(self):
931        self.prepare_special_container()
932        # Login
933        self.browser.open(self.login_path)
934        self.browser.getControl(
935            name="form.login").value = self.special_applicant.applicant_id
936        self.browser.getControl(name="form.password").value = 'apwd'
937        self.browser.getControl("Login").click()
938        applicant_path = self.browser.url
939        self.browser.getLink("Edit application record").click()
940        self.browser.getControl(name="form.firstname").value = 'John'
941        self.browser.getControl(name="form.middlename").value = 'Anthony'
942        self.browser.getControl(name="form.lastname").value = 'Tester'
943        self.browser.getControl(name="form.special_application").value = [
944            'transcript']
945        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
946        #self.browser.getControl(name="form.sex").value = ['m']
947        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
948        self.browser.getControl("Save").click()
949        self.browser.getControl("Add online payment ticket").click()
950        self.assertMatches('...Payment ticket created...',
951                           self.browser.contents)
952        self.assertTrue(
953            '<span>Transcript Fee</span>' in self.browser.contents)
954        self.assertTrue(
955            'This is a special app container' in self.browser.contents)
956        self.assertTrue(
957            '<span>200.0</span>' in self.browser.contents)
958        self.browser.getLink("Logout").click()
959        # Login as manager
960        #self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
961        #self.browser.open(applicant_path + '/manage')
962        # When unselecting special_application, the payment ticket
963        # can still be created.
964        #self.browser.getControl(name="form.special_application").value = [
965        #    '']
966        #self.browser.getControl("Save").click()
967        #self.browser.getControl("Add online payment ticket").click()
968        #self.assertMatches('...Payment ticket created...',
969        #                   self.browser.contents)
970        #self.assertMatches('...<span>--</span>...',
971        #                   self.browser.contents)
972        return
973
974    def test_duplicate_choice(self):
975        # Make sure that that 1st and 2nd choice are different
976        self.login()
977        self.browser.open(self.edit_path)
978        self.fill_correct_values() # fill other fields with correct values
979        self.browser.getControl(name="form.course2").value = ['CERT1']
980        self.browser.getControl("Save").click()
981        self.assertTrue(
982            '1st and 2nd choice must be different' in self.browser.contents)
983        self.browser.getControl(name="form.course2").value = []
984        self.browser.getControl("Save").click()
985        self.assertTrue('Form has been saved' in self.browser.contents)
986        return
987
988    def test_final_submit(self):
989        # Make sure that a correctly filled form with passport picture
990        # can be submitted (only) after payment
991        self.login()
992        self.browser.getLink("Edit application record").click()
993        self.assertFalse('Final Submit' in self.browser.contents)
994        IWorkflowInfo(self.applicant).fireTransition('pay')
995        self.browser.open(self.edit_path)
996        self.assertTrue('Final Submit' in self.browser.contents)
997        self.fill_correct_values() # fill other fields with correct values
998        self.browser.getControl("Save").click()
999        self.browser.getControl("Final Submit").click()
1000        # We forgot to upload a passport picture
1001        self.assertTrue(
1002            'No passport picture uploaded' in self.browser.contents)
1003        # Create a pseudo image file and select it to be uploaded in form
1004        pseudo_image = StringIO('I pretend to be a graphics file')
1005        ctrl = self.browser.getControl(name='form.passport')
1006        file_ctrl = ctrl.mech_control
1007        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
1008        self.browser.getControl("Final Submit").click() # (finally) submit form
1009        # The picture has been uploaded but the form cannot be submitted
1010        # since the passport confirmation box was not ticked
1011        self.assertTrue(
1012            'Passport picture confirmation box not ticked'
1013            in self.browser.contents)
1014        self.browser.getControl(name="confirm_passport").value = True
1015        # If application period has expired and strict-deadline is set
1016        # applicants do notsee edit button and can't open
1017        # the edit form.
1018        self.applicantscontainer.enddate = datetime.now(pytz.utc)
1019        self.browser.open(self.view_path)
1020        self.assertFalse(
1021            'Edit application record' in self.browser.contents)
1022        self.browser.open(self.edit_path)
1023        self.assertTrue(
1024            'form is locked' in self.browser.contents)
1025        # We can either postpone the enddate ...
1026        self.applicantscontainer.enddate = datetime.now(
1027            pytz.utc) + timedelta(days=10)
1028        self.browser.open(self.edit_path)
1029        self.browser.getControl(name="confirm_passport").value = True
1030        self.browser.getControl("Final Submit").click()
1031        self.assertTrue(
1032            'Application submitted' in self.browser.contents)
1033        # ... or allow submission after deadline.
1034        IWorkflowState(self.applicant).setState('paid')
1035        self.applicant.locked = False
1036        self.applicantscontainer.strict_deadline = False
1037        self.browser.open(self.edit_path)
1038        self.browser.getControl(name="confirm_passport").value = True
1039        self.browser.getControl("Final Submit").click()
1040        self.assertTrue(
1041            'Application submitted' in self.browser.contents)
1042        self.browser.goBack(count=1)
1043        self.browser.getControl("Save").click()
1044        # The form is locked.
1045        self.assertTrue(self.applicant.locked)
1046        self.assertTrue(
1047            'The requested form is locked' in self.browser.contents)
1048        self.browser.goBack(count=1)
1049        self.browser.getControl("Final Submit").click()
1050        self.assertTrue(
1051            'The requested form is locked' in self.browser.contents)
1052        return
1053
1054    def test_locking(self):
1055        # Make sure that locked forms can't be submitted
1056        self.login()
1057        self.browser.open(self.edit_path)
1058        self.fill_correct_values() # fill other fields with correct values
1059        # Create a pseudo image file and select it to be uploaded in form
1060        pseudo_image = StringIO('I pretend to be a graphics file')
1061        ctrl = self.browser.getControl(name='form.passport')
1062        file_ctrl = ctrl.mech_control
1063        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
1064        self.browser.getControl("Save").click()
1065        # Now we lock the form
1066        self.applicant.locked = True
1067        self.browser.open(self.edit_path)
1068        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1069        self.assertTrue(
1070            'The requested form is locked' in self.browser.contents)
1071        return
1072
1073    def test_certificate_removed(self):
1074        self.login()
1075        self.browser.open(self.edit_path)
1076        self.fill_correct_values()
1077        self.browser.getControl("Save").click()
1078        self.browser.open(self.view_path)
1079        self.assertTrue(
1080            'Unnamed Certificate' in self.browser.contents)
1081        self.browser.open(self.edit_path)
1082        self.assertTrue(
1083            '<option selected="selected" value="CERT1">' in self.browser.contents)
1084        # Now we remove the certificate
1085        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1086        # The certificate is still shown in display mode
1087        self.browser.open(self.view_path)
1088        self.assertTrue(
1089            'Unnamed Certificate' in self.browser.contents)
1090        # The certificate is still selectable in edit mode so that it won't
1091        # be automatically replaced by another (arbitrary) certificate
1092        self.browser.open(self.edit_path)
1093        self.assertTrue(
1094            '<option selected="selected" value="CERT1">' in self.browser.contents)
1095        # Consequently, the certificate is still shown after saving the form
1096        self.browser.getControl("Save").click()
1097        self.browser.open(self.view_path)
1098        self.assertTrue(
1099            'Unnamed Certificate' in self.browser.contents)
1100        # Even if we add a new certificate the previous (removed)
1101        # certificate is shown
1102        certificate = createObject('waeup.Certificate')
1103        certificate.code = 'CERT2'
1104        certificate.title = 'New Certificate'
1105        certificate.application_category = 'basic'
1106        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1107            certificate)
1108        self.browser.open(self.edit_path)
1109        self.assertTrue(
1110            '<option selected="selected" value="CERT1">'
1111            in self.browser.contents)
1112
1113class ApplicantRegisterTests(ApplicantsFullSetup):
1114    # Tests for applicant registration
1115
1116    layer = FunctionalLayer
1117
1118    def test_register_applicant_create(self):
1119        # An applicant can register himself.
1120        self.browser.open(self.container_path)
1121        self.browser.getLink("Register for application").click()
1122        # Fill the edit form with suitable values
1123        self.browser.getControl(name="form.firstname").value = 'Anna'
1124        self.browser.getControl(name="form.lastname").value = 'Kurios'
1125        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1126        self.browser.getControl(name="form.phone.country").value = ['+234']
1127        self.browser.getControl(name="form.phone.area").value = '555'
1128        self.browser.getControl(name="form.phone.ext").value = '6666666'
1129        self.browser.getControl("Send login credentials").click()
1130        self.assertEqual(self.browser.url,
1131            self.container_path + '/registration_complete?email=xx%40yy.zz')
1132        # The new applicant can be found in the catalog via the email address
1133        cat = getUtility(ICatalog, name='applicants_catalog')
1134        results = list(
1135            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
1136        applicant = results[0]
1137        self.assertEqual(applicant.lastname,'Kurios')
1138        # The application_id has been copied to the reg_number
1139        self.assertEqual(applicant.applicant_id, applicant.reg_number)
1140        # The applicant can be found in the catalog via the reg_number
1141        results = list(
1142            cat.searchResults(
1143            reg_number=(applicant.reg_number, applicant.reg_number)))
1144        self.assertEqual(applicant,results[0])
1145        return
1146
1147    def test_register_applicant_update(self):
1148        # We change the application mode and check if applicants
1149        # can find and update imported records instead of creating new records.
1150        # First we check what happens if record does not exist.
1151        self.applicantscontainer.mode = 'update'
1152        self.browser.open(self.container_path + '/register')
1153        self.browser.getControl(name="form.firstname").value = 'John'
1154        self.browser.getControl(name="form.reg_number").value = 'anynumber'
1155        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1156        self.browser.getControl("Send login credentials").click()
1157        self.assertTrue('No application record found.'
1158            in self.browser.contents)
1159        # Even with the correct reg_number we can't register
1160        # because firstname attribute is not set.
1161        self.applicantscontainer.mode = 'update'
1162        self.browser.open(self.container_path + '/register')
1163        self.browser.getControl(name="form.firstname").value = 'John'
1164        self.browser.getControl(name="form.reg_number").value = '1234'
1165        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1166        self.browser.getControl("Send login credentials").click()
1167        self.assertTrue('An error occurred.' in self.browser.contents)
1168        # Let's set this attribute manually
1169        # and try to register with a wrong name.
1170        self.applicant.firstname = u'John'
1171        self.browser.open(self.container_path + '/register')
1172        self.browser.getControl(name="form.firstname").value = 'Johnny'
1173        self.browser.getControl(name="form.reg_number").value = '1234'
1174        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1175        self.browser.getControl("Send login credentials").click()
1176        # Anonymous is not informed that firstname verification failed.
1177        # It seems that the record doesn't exist.
1178        self.assertTrue('No application record found.'
1179            in self.browser.contents)
1180        # Even with the correct firstname we can't register if a
1181        # password has been set and used.
1182        IWorkflowState(self.applicant).setState('started')
1183        self.browser.getControl(name="form.firstname").value = 'John'
1184        self.browser.getControl(name="form.reg_number").value = '1234'
1185        self.browser.getControl("Send login credentials").click()
1186        self.assertTrue('Your password has already been set and used.'
1187            in self.browser.contents)
1188        #IUserAccount(
1189        #    self.app['applicants'][container_name_1][
1190        #    self.applicant.application_number]).context.password = None
1191        # Even without unsetting the password we can re-register if state
1192        # is 'initialized'
1193        IWorkflowState(self.applicant).setState('initialized')
1194        self.browser.open(self.container_path + '/register')
1195        # The firstname field, used for verification, is not case-sensitive.
1196        self.browser.getControl(name="form.firstname").value = 'jOhn'
1197        self.browser.getControl(name="form.reg_number").value = '1234'
1198        self.browser.getControl(name="form.email").value = 'new@yy.zz'
1199        self.browser.getControl("Send login credentials").click()
1200        # Yeah, we succeded ...
1201        self.assertTrue('Your registration was successful.'
1202            in self.browser.contents)
1203        # ... and  applicant can be found in the catalog via the email address
1204        cat = getUtility(ICatalog, name='applicants_catalog')
1205        results = list(
1206            cat.searchResults(
1207            email=('new@yy.zz', 'new@yy.zz')))
1208        self.assertEqual(self.applicant,results[0])
1209        return
1210
1211    def test_change_password_request(self):
1212        self.browser.open('http://localhost/app/changepw')
1213        self.browser.getControl(name="form.identifier").value = '1234'
1214        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1215        self.browser.getControl("Send login credentials").click()
1216        self.assertTrue('No record found' in self.browser.contents)
1217        self.applicant.email = 'aa@aa.ng'
1218        # Update the catalog
1219        notify(grok.ObjectModifiedEvent(self.applicant))
1220        self.browser.open('http://localhost/app/changepw')
1221        self.browser.getControl(name="form.identifier").value = '1234'
1222        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1223        self.browser.getControl("Send login credentials").click()
1224        self.assertTrue(
1225            'An email with your user name and password has been sent'
1226            in self.browser.contents)
1227
1228class ApplicantsExportTests(ApplicantsFullSetup, FunctionalAsyncTestCase):
1229    # Tests for StudentsContainer class views and pages
1230
1231    layer = FunctionalLayer
1232
1233    def wait_for_export_job_completed(self):
1234        # helper function waiting until the current export job is completed
1235        manager = getUtility(IJobManager)
1236        job_id = self.app['datacenter'].running_exports[0][0]
1237        job = manager.get(job_id)
1238        wait_for_result(job)
1239        return job_id
1240
1241    def test_applicants_in_container_export(self):
1242        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1243        container_path = 'http://localhost/app/applicants/%s' % container_name_1
1244        self.browser.open(container_path)
1245        self.browser.getLink("Export applicants").click()
1246        self.browser.getControl("Start new export").click()
1247
1248        # When the job is finished and we reload the page...
1249        job_id = self.wait_for_export_job_completed()
1250        self.browser.open(container_path + '/exports')
1251        # ... the csv file can be downloaded ...
1252        self.browser.getLink("Download").click()
1253        self.assertEqual(self.browser.headers['content-type'],
1254            'text/csv; charset=UTF-8')
1255        self.assertTrue(
1256            'filename="WAeUP.Kofa_applicants_%s.csv' % job_id in
1257            self.browser.headers['content-disposition'])
1258        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
1259        job_id = self.app['datacenter'].running_exports[0][0]
1260        # ... and discarded
1261        self.browser.open(container_path + '/exports')
1262        self.browser.getControl("Discard").click()
1263        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
1264        # Creation, downloading and discarding is logged
1265        logfile = os.path.join(
1266            self.app['datacenter'].storage, 'logs', 'datacenter.log')
1267        logcontent = open(logfile).read()
1268        self.assertTrue(
1269            'zope.mgr - applicants.browser.ExportJobContainerJobStart - '
1270            'exported: applicants (%s), job_id=%s'
1271            % (container_name_1, job_id) in logcontent
1272            )
1273        self.assertTrue(
1274            'zope.mgr - applicants.browser.ExportJobContainerDownload '
1275            '- downloaded: WAeUP.Kofa_applicants_%s.csv, job_id=%s'
1276            % (job_id, job_id) in logcontent
1277            )
1278        self.assertTrue(
1279            'zope.mgr - applicants.browser.ExportJobContainerOverview '
1280            '- discarded: job_id=%s' % job_id in logcontent
1281            )
Note: See TracBrowser for help on using the repository browser.