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

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

Take unused records instead of creating new records during self-registration.

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