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

Last change on this file since 11600 was 11600, checked in by Henrik Bettermann, 10 years ago

Change flash message.

  • Property svn:keywords set to Id
File size: 64.0 KB
Line 
1## $Id: test_browser.py 11600 2014-04-24 06:50:46Z 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 applicant 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 application 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 application section' in self.browser.contents)
219        # There is a manage link
220        link = self.browser.getLink('Manage application 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 application 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 submit the application form before '
510            '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 application 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_pay_portal_application_fee(self):
785        self.login()
786        self.browser.open(self.edit_path)
787        # Payment tickets can't be created before the form has been validated
788        self.browser.getControl("Add online payment ticket").click()
789        self.assertTrue('Required input is missing' in self.browser.contents)
790        self.fill_correct_values()
791        # We have to save the form otherwise the filled fields will be cleared
792        # after adding an online payment, because adding an online payment
793        # requires a filled form but does not save it
794        self.browser.getControl("Save").click()
795        self.browser.getControl("Add online payment ticket").click()
796        # Session object missing
797        self.assertTrue(
798            'Session configuration object is not available'
799            in self.browser.contents)
800        configuration = SessionConfiguration()
801        configuration.academic_session = session_1
802        configuration.application_fee = 200.0
803        self.app['configuration'].addSessionConfiguration(configuration)
804        self.browser.open(self.edit_path)
805        self.browser.getControl("Add online payment ticket").click()
806        self.assertMatches('...Payment ticket created...',
807                           self.browser.contents)
808        self.assertMatches('...Activation Code...',
809                           self.browser.contents)
810        # Payment ticket can be removed if they haven't received a
811        # valid callback
812        self.browser.open(self.edit_path)
813        ctrl = self.browser.getControl(name='val_id')
814        value = ctrl.options[0]
815        ctrl.getControl(value=value).selected = True
816        self.browser.getControl("Remove selected", index=0).click()
817        self.assertMatches('...Successfully removed...', self.browser.contents)
818        # We will try the callback request view
819        self.browser.getControl("Add online payment ticket").click()
820        self.browser.open(self.edit_path)
821        ctrl = self.browser.getControl(name='val_id')
822        value = ctrl.options[0]
823        self.browser.getLink(value).click()
824        self.assertMatches('...Amount Authorized...',
825                           self.browser.contents)
826        payment_url = self.browser.url
827        payment_id = self.applicant.keys()[0]
828        payment = self.applicant[payment_id]
829        self.assertEqual(payment.p_item,'This is the %s container' % container_name_1)
830        self.assertEqual(payment.p_session, session_1)
831        self.assertEqual(payment.p_category,'application')
832        self.assertEqual(payment.amount_auth,200.0)
833        # Applicant is payer of the payment ticket.
834        self.assertEqual(
835            IPayer(payment).display_fullname, 'John Anthony Tester')
836        self.assertEqual(
837            IPayer(payment).id, self.applicant.applicant_id)
838        self.assertEqual(IPayer(payment).faculty, 'N/A')
839        self.assertEqual(IPayer(payment).department, 'N/A')
840        # The pdf payment slip can't yet be opened
841        #self.browser.open(payment_url + '/payment_receipt.pdf')
842        #self.assertMatches('...Ticket not yet paid...',
843        #                   self.browser.contents)
844        # Approve payment
845        # Applicants can't approve payments
846        self.assertRaises(
847            Unauthorized, self.browser.open, payment_url + '/approve')
848        # We approve the payment by bypassing the view
849        payment.approve()
850        # The payment slip can be downloaded now
851        self.browser.open(payment_url)
852        self.browser.getLink("Download payment slip").click()
853        self.assertEqual(self.browser.headers['Status'], '200 Ok')
854        self.assertEqual(self.browser.headers['Content-Type'],
855                         'application/pdf')
856        # Applicant is is not yet in state 'paid' because it was only
857        # the payment which we set to paid
858        self.browser.open(self.view_path)
859        self.assertMatches('...started...',
860                           self.browser.contents)
861        self.assertTrue(self.applicant.state == 'started')
862        # Let's logout and approve the payment as manager
863        self.browser.getLink("Logout").click()
864        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
865        # First we reset the payment
866        payment.r_amount_approved = 0.0
867        payment.r_code = u''
868        payment.p_state = 'unpaid'
869        payment.r_desc = u''
870        payment.payment_date = None
871        self.browser.open(payment_url)
872        self.browser.getLink("Approve payment").click()
873        self.assertEqual(payment.p_state, 'paid')
874        self.assertEqual(payment.r_amount_approved, 200.0)
875        self.assertEqual(payment.r_code, 'AP')
876        self.assertTrue(self.applicant.state == 'paid')
877        # Approval is logged in students.log ...
878        logfile = os.path.join(
879            self.app['datacenter'].storage, 'logs', 'applicants.log')
880        logcontent = open(logfile).read()
881        self.assertTrue(
882            'zope.mgr - applicants.browser.OnlinePaymentApprovePage - '
883            '%s - payment approved' % self.applicant.applicant_id
884            in logcontent)
885        # ... and in payments.log
886        logfile = os.path.join(
887            self.app['datacenter'].storage, 'logs', 'payments.log')
888        logcontent = open(logfile).read()
889        self.assertTrue(
890            '"zope.mgr",%s,%s,application,200.0,AP,,,,,,\n'
891            % (self.applicant.applicant_id, payment.p_id)
892            in logcontent)
893        return
894
895    def test_pay_container_application_fee(self):
896        self.login()
897        self.browser.open(self.edit_path)
898        self.fill_correct_values()
899        self.browser.getControl("Save").click()
900        configuration = SessionConfiguration()
901        configuration.academic_session = session_1
902        self.applicantscontainer.application_fee = 120.0
903        configuration.application_fee = 9999.9
904        self.app['configuration'].addSessionConfiguration(configuration)
905        self.browser.open(self.edit_path)
906        self.browser.getControl("Add online payment ticket").click()
907        self.assertMatches('...Payment ticket created...',
908                           self.browser.contents)
909        self.assertMatches('...Payment ticket created...',
910                           self.browser.contents)
911        self.assertFalse(
912            '<span>9999.9</span>' in self.browser.contents)
913        self.assertTrue(
914            '<span>120.0</span>' in self.browser.contents)
915        self.assertTrue(
916            '<span>Application Fee</span>' in self.browser.contents)
917        return
918
919    def prepare_special_container(self):
920        # Add special application container
921        container_name = u'special%s' % session_1
922        applicantscontainer = ApplicantsContainer()
923        applicantscontainer.code = container_name
924        applicantscontainer.prefix = 'special'
925        applicantscontainer.year = session_1
926        applicantscontainer.title = u'This is a special app container'
927        applicantscontainer.application_category = 'no'
928        applicantscontainer.mode = 'create'
929        applicantscontainer.strict_deadline = True
930        delta = timedelta(days=10)
931        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
932        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
933        self.app['applicants'][container_name] = applicantscontainer
934        # Add an applicant
935        applicant = createObject('waeup.Applicant')
936        # reg_number is the only field which has to be preset here
937        # because managers are allowed to edit this required field
938        applicant.reg_number = u'12345'
939        self.special_applicant = applicant
940        self.app['applicants'][container_name].addApplicant(applicant)
941        IUserAccount(
942            self.app['applicants'][container_name][
943            applicant.application_number]).setPassword('apwd')
944        # Add session configuration object
945        configuration = SessionConfiguration()
946        configuration.academic_session = session_1
947        configuration.transcript_fee = 200.0
948        configuration.clearance_fee = 300.0
949        self.app['configuration'].addSessionConfiguration(configuration)
950
951
952    def test_pay_special_fee(self):
953        self.prepare_special_container()
954        # Login
955        self.browser.open(self.login_path)
956        self.browser.getControl(
957            name="form.login").value = self.special_applicant.applicant_id
958        self.browser.getControl(name="form.password").value = 'apwd'
959        self.browser.getControl("Login").click()
960        applicant_path = self.browser.url
961        self.browser.getLink("Edit application record").click()
962        self.browser.getControl(name="form.firstname").value = 'John'
963        self.browser.getControl(name="form.middlename").value = 'Anthony'
964        self.browser.getControl(name="form.lastname").value = 'Tester'
965        self.browser.getControl(name="form.special_application").value = [
966            'transcript']
967        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
968        #self.browser.getControl(name="form.sex").value = ['m']
969        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
970        self.browser.getControl("Save").click()
971        self.browser.getControl("Add online payment ticket").click()
972        self.assertMatches('...Payment ticket created...',
973                           self.browser.contents)
974        self.assertTrue(
975            '<span>Transcript Fee</span>' in self.browser.contents)
976        self.assertTrue(
977            'This is a special app container' in self.browser.contents)
978        self.assertTrue(
979            '<span>200.0</span>' in self.browser.contents)
980        self.assertEqual(len(self.special_applicant.keys()), 1)
981        # The applicant's workflow state is paid ...
982        self.special_applicant.values()[0].approveApplicantPayment()
983        self.assertEqual(self.special_applicant.state, 'paid')
984        self.browser.open(applicant_path + '/edit')
985        # ... but he can create further tickets.
986        self.browser.getControl(name="form.special_application").value = [
987            'clearance']
988        self.browser.getControl("Save").click()
989        self.browser.getControl("Add online payment ticket").click()
990        self.assertMatches('...Payment ticket created...',
991                           self.browser.contents)
992        self.browser.open(applicant_path)
993        self.assertTrue(
994            '<td>Acceptance Fee</td>' in self.browser.contents)
995        self.assertEqual(len(self.special_applicant.keys()), 2)
996        # Second payment can also be approved wthout error message
997        flashtype, msg, log = self.special_applicant.values()[1].approveApplicantPayment()
998        self.assertEqual(flashtype, 'success')
999        self.assertEqual(msg, 'Payment approved')
1000        # Payment slips can't be downloaded ...
1001        payment_id = self.special_applicant.keys()[0]
1002        self.browser.open(applicant_path + '/' + payment_id)
1003        self.browser.getLink("Download payment slip").click()
1004        self.assertTrue(
1005            'Please pay and submit before trying to download payment slips.'
1006            in self.browser.contents)
1007        # ... unless form is submitted.
1008        self.browser.open(applicant_path + '/edit')
1009        image = open(SAMPLE_IMAGE, 'rb')
1010        ctrl = self.browser.getControl(name='form.passport')
1011        file_ctrl = ctrl.mech_control
1012        file_ctrl.add_file(image, filename='myphoto.jpg')
1013        self.browser.getControl(name="confirm_passport").value = True
1014        self.browser.getControl("Final Submit").click()
1015        self.browser.open(applicant_path + '/' + payment_id)
1016        self.browser.getLink("Download payment slip").click()
1017        self.assertEqual(self.browser.headers['Content-Type'],
1018                 'application/pdf')
1019        return
1020
1021    def test_duplicate_choice(self):
1022        # Make sure that that 1st and 2nd choice are different
1023        self.login()
1024        self.browser.open(self.edit_path)
1025        self.fill_correct_values() # fill other fields with correct values
1026        self.browser.getControl(name="form.course2").value = ['CERT1']
1027        self.browser.getControl("Save").click()
1028        self.assertTrue(
1029            '1st and 2nd choice must be different' in self.browser.contents)
1030        self.browser.getControl(name="form.course2").value = []
1031        self.browser.getControl("Save").click()
1032        self.assertTrue('Form has been saved' in self.browser.contents)
1033        return
1034
1035    def test_final_submit(self):
1036        # Make sure that a correctly filled form with passport picture
1037        # can be submitted (only) after payment
1038        self.login()
1039        self.browser.getLink("Edit application record").click()
1040        self.assertFalse('Final Submit' in self.browser.contents)
1041        IWorkflowInfo(self.applicant).fireTransition('pay')
1042        self.browser.open(self.edit_path)
1043        self.assertTrue('Final Submit' in self.browser.contents)
1044        self.fill_correct_values() # fill other fields with correct values
1045        self.browser.getControl("Save").click()
1046        self.browser.getControl("Final Submit").click()
1047        # We forgot to upload a passport picture
1048        self.assertTrue(
1049            'No passport picture uploaded' in self.browser.contents)
1050        # Use a real image file and select it to be uploaded in form
1051        image = open(SAMPLE_IMAGE, 'rb')
1052        ctrl = self.browser.getControl(name='form.passport')
1053        file_ctrl = ctrl.mech_control
1054        file_ctrl.add_file(image, filename='myphoto.jpg')
1055        self.browser.getControl("Final Submit").click() # (finally) submit form
1056        # The picture has been uploaded but the form cannot be submitted
1057        # since the passport confirmation box was not ticked
1058        self.assertTrue(
1059            'Passport picture confirmation box not ticked'
1060            in self.browser.contents)
1061        self.browser.getControl(name="confirm_passport").value = True
1062        # If application period has expired and strict-deadline is set
1063        # applicants do notsee edit button and can't open
1064        # the edit form.
1065        self.applicantscontainer.enddate = datetime.now(pytz.utc)
1066        self.browser.open(self.view_path)
1067        self.assertFalse(
1068            'Edit application record' in self.browser.contents)
1069        self.browser.open(self.edit_path)
1070        self.assertTrue(
1071            'form is locked' in self.browser.contents)
1072        # We can either postpone the enddate ...
1073        self.applicantscontainer.enddate = datetime.now(
1074            pytz.utc) + timedelta(days=10)
1075        self.browser.open(self.edit_path)
1076        self.browser.getControl(name="confirm_passport").value = True
1077        self.browser.getControl("Final Submit").click()
1078        self.assertTrue(
1079            'Application submitted' in self.browser.contents)
1080        # ... or allow submission after deadline.
1081        IWorkflowState(self.applicant).setState('paid')
1082        self.applicant.locked = False
1083        self.applicantscontainer.strict_deadline = False
1084        self.browser.open(self.edit_path)
1085        self.browser.getControl(name="confirm_passport").value = True
1086        self.browser.getControl("Final Submit").click()
1087        self.assertTrue(
1088            'Application submitted' in self.browser.contents)
1089        self.browser.goBack(count=1)
1090        self.browser.getControl("Save").click()
1091        # The form is locked.
1092        self.assertTrue(self.applicant.locked)
1093        self.assertTrue(
1094            'The requested form is locked' in self.browser.contents)
1095        self.browser.goBack(count=1)
1096        self.browser.getControl("Final Submit").click()
1097        self.assertTrue(
1098            'The requested form is locked' in self.browser.contents)
1099        return
1100
1101    def test_locking(self):
1102        # Make sure that locked forms can't be submitted
1103        self.login()
1104        self.browser.open(self.edit_path)
1105        self.fill_correct_values() # fill other fields with correct values
1106        # Create a pseudo image file and select it to be uploaded in form
1107        pseudo_image = StringIO('I pretend to be a graphics file')
1108        ctrl = self.browser.getControl(name='form.passport')
1109        file_ctrl = ctrl.mech_control
1110        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
1111        self.browser.getControl("Save").click()
1112        # Now we lock the form
1113        self.applicant.locked = True
1114        self.browser.open(self.edit_path)
1115        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1116        self.assertTrue(
1117            'The requested form is locked' in self.browser.contents)
1118        return
1119
1120    def test_certificate_removed(self):
1121        self.login()
1122        self.browser.open(self.edit_path)
1123        self.fill_correct_values()
1124        self.browser.getControl("Save").click()
1125        self.browser.open(self.view_path)
1126        self.assertTrue(
1127            'Unnamed Certificate' in self.browser.contents)
1128        self.browser.open(self.edit_path)
1129        self.assertTrue(
1130            '<option selected="selected" value="CERT1">' in self.browser.contents)
1131        # Now we remove the certificate
1132        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1133        # The certificate is still shown in display mode
1134        self.browser.open(self.view_path)
1135        self.assertTrue(
1136            'Unnamed Certificate' in self.browser.contents)
1137        # The certificate is still selectable in edit mode so that it won't
1138        # be automatically replaced by another (arbitrary) certificate
1139        self.browser.open(self.edit_path)
1140        self.assertTrue(
1141            '<option selected="selected" value="CERT1">' in self.browser.contents)
1142        # Consequently, the certificate is still shown after saving the form
1143        self.browser.getControl("Save").click()
1144        self.browser.open(self.view_path)
1145        self.assertTrue(
1146            'Unnamed Certificate' in self.browser.contents)
1147        # Even if we add a new certificate the previous (removed)
1148        # certificate is shown
1149        certificate = createObject('waeup.Certificate')
1150        certificate.code = 'CERT2'
1151        certificate.title = 'New Certificate'
1152        certificate.application_category = 'basic'
1153        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1154            certificate)
1155        self.browser.open(self.edit_path)
1156        self.assertTrue(
1157            '<option selected="selected" value="CERT1">'
1158            in self.browser.contents)
1159
1160class ApplicantRegisterTests(ApplicantsFullSetup):
1161    # Tests for applicant registration
1162
1163    layer = FunctionalLayer
1164
1165    def test_register_applicant_create(self):
1166        # An applicant can register himself.
1167        self.browser.open(self.container_path)
1168        self.browser.getLink("Register for application").click()
1169        # Fill the edit form with suitable values
1170        self.browser.getControl(name="form.firstname").value = 'Anna'
1171        self.browser.getControl(name="form.lastname").value = 'Kurios'
1172        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1173        self.browser.getControl(name="form.phone.country").value = ['+234']
1174        self.browser.getControl(name="form.phone.area").value = '555'
1175        self.browser.getControl(name="form.phone.ext").value = '6666666'
1176        self.browser.getControl("Send login credentials").click()
1177        self.assertEqual(self.browser.url,
1178            self.container_path + '/registration_complete?email=xx%40yy.zz')
1179        # The new applicant can be found in the catalog via the email address
1180        cat = getUtility(ICatalog, name='applicants_catalog')
1181        results = list(
1182            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
1183        applicant = results[0]
1184        self.assertEqual(applicant.lastname,'Kurios')
1185        # The application_id has been copied to the reg_number
1186        self.assertEqual(applicant.applicant_id, applicant.reg_number)
1187        # The applicant can be found in the catalog via the reg_number
1188        results = list(
1189            cat.searchResults(
1190            reg_number=(applicant.reg_number, applicant.reg_number)))
1191        self.assertEqual(applicant,results[0])
1192        return
1193
1194    def test_register_applicant_update(self):
1195        # We change the application mode and check if applicants
1196        # can find and update imported records instead of creating new records.
1197        # First we check what happens if record does not exist.
1198        self.applicantscontainer.mode = 'update'
1199        self.browser.open(self.container_path + '/register')
1200        self.browser.getControl(name="form.firstname").value = 'John'
1201        self.browser.getControl(name="form.reg_number").value = 'anynumber'
1202        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1203        self.browser.getControl("Send login credentials").click()
1204        self.assertTrue('No application record found.'
1205            in self.browser.contents)
1206        # Even with the correct reg_number we can't register
1207        # because firstname attribute is not set.
1208        self.applicantscontainer.mode = 'update'
1209        self.browser.open(self.container_path + '/register')
1210        self.browser.getControl(name="form.firstname").value = 'John'
1211        self.browser.getControl(name="form.reg_number").value = '1234'
1212        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1213        self.browser.getControl("Send login credentials").click()
1214        self.assertTrue('An error occurred.' in self.browser.contents)
1215        # Let's set this attribute manually
1216        # and try to register with a wrong name.
1217        self.applicant.firstname = u'John'
1218        self.browser.open(self.container_path + '/register')
1219        self.browser.getControl(name="form.firstname").value = 'Johnny'
1220        self.browser.getControl(name="form.reg_number").value = '1234'
1221        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1222        self.browser.getControl("Send login credentials").click()
1223        # Anonymous is not informed that firstname verification failed.
1224        # It seems that the record doesn't exist.
1225        self.assertTrue('No application record found.'
1226            in self.browser.contents)
1227        # Even with the correct firstname we can't register if a
1228        # password has been set and used.
1229        IWorkflowState(self.applicant).setState('started')
1230        self.browser.getControl(name="form.firstname").value = 'John'
1231        self.browser.getControl(name="form.reg_number").value = '1234'
1232        self.browser.getControl("Send login credentials").click()
1233        self.assertTrue('Your password has already been set and used.'
1234            in self.browser.contents)
1235        #IUserAccount(
1236        #    self.app['applicants'][container_name_1][
1237        #    self.applicant.application_number]).context.password = None
1238        # Even without unsetting the password we can re-register if state
1239        # is 'initialized'
1240        IWorkflowState(self.applicant).setState('initialized')
1241        self.browser.open(self.container_path + '/register')
1242        # The firstname field, used for verification, is not case-sensitive.
1243        self.browser.getControl(name="form.firstname").value = 'jOhn'
1244        self.browser.getControl(name="form.reg_number").value = '1234'
1245        self.browser.getControl(name="form.email").value = 'new@yy.zz'
1246        self.browser.getControl("Send login credentials").click()
1247        # Yeah, we succeded ...
1248        self.assertTrue('Your registration was successful.'
1249            in self.browser.contents)
1250        # ... and  applicant can be found in the catalog via the email address
1251        cat = getUtility(ICatalog, name='applicants_catalog')
1252        results = list(
1253            cat.searchResults(
1254            email=('new@yy.zz', 'new@yy.zz')))
1255        self.assertEqual(self.applicant,results[0])
1256        return
1257
1258    def test_change_password_request(self):
1259        self.browser.open('http://localhost/app/changepw')
1260        self.browser.getControl(name="form.identifier").value = '1234'
1261        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1262        self.browser.getControl("Send login credentials").click()
1263        self.assertTrue('No record found' in self.browser.contents)
1264        self.applicant.email = 'aa@aa.ng'
1265        # Update the catalog
1266        notify(grok.ObjectModifiedEvent(self.applicant))
1267        self.browser.open('http://localhost/app/changepw')
1268        self.browser.getControl(name="form.identifier").value = '1234'
1269        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1270        self.browser.getControl("Send login credentials").click()
1271        self.assertTrue(
1272            'An email with your user name and password has been sent'
1273            in self.browser.contents)
1274
1275class ApplicantsExportTests(ApplicantsFullSetup, FunctionalAsyncTestCase):
1276    # Tests for StudentsContainer class views and pages
1277
1278    layer = FunctionalLayer
1279
1280    def wait_for_export_job_completed(self):
1281        # helper function waiting until the current export job is completed
1282        manager = getUtility(IJobManager)
1283        job_id = self.app['datacenter'].running_exports[0][0]
1284        job = manager.get(job_id)
1285        wait_for_result(job)
1286        return job_id
1287
1288    def test_applicants_in_container_export(self):
1289        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1290        container_path = 'http://localhost/app/applicants/%s' % container_name_1
1291        self.browser.open(container_path)
1292        self.browser.getLink("Export applicants").click()
1293        self.browser.getControl("Start new export").click()
1294
1295        # When the job is finished and we reload the page...
1296        job_id = self.wait_for_export_job_completed()
1297        self.browser.open(container_path + '/exports')
1298        # ... the csv file can be downloaded ...
1299        self.browser.getLink("Download").click()
1300        self.assertEqual(self.browser.headers['content-type'],
1301            'text/csv; charset=UTF-8')
1302        self.assertTrue(
1303            'filename="WAeUP.Kofa_applicants_%s.csv' % job_id in
1304            self.browser.headers['content-disposition'])
1305        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
1306        job_id = self.app['datacenter'].running_exports[0][0]
1307        # ... and discarded
1308        self.browser.open(container_path + '/exports')
1309        self.browser.getControl("Discard").click()
1310        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
1311        # Creation, downloading and discarding is logged
1312        logfile = os.path.join(
1313            self.app['datacenter'].storage, 'logs', 'datacenter.log')
1314        logcontent = open(logfile).read()
1315        self.assertTrue(
1316            'zope.mgr - applicants.browser.ExportJobContainerJobStart - '
1317            'exported: applicants (%s), job_id=%s'
1318            % (container_name_1, job_id) in logcontent
1319            )
1320        self.assertTrue(
1321            'zope.mgr - applicants.browser.ExportJobContainerDownload '
1322            '- downloaded: WAeUP.Kofa_applicants_%s.csv, job_id=%s'
1323            % (job_id, job_id) in logcontent
1324            )
1325        self.assertTrue(
1326            'zope.mgr - applicants.browser.ExportJobContainerOverview '
1327            '- discarded: job_id=%s' % job_id in logcontent
1328            )
Note: See TracBrowser for help on using the repository browser.