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

Last change on this file since 13123 was 13123, checked in by Henrik Bettermann, 9 years ago

Do not create special application payment tickets with zero amount.

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