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

Last change on this file since 17959 was 11161, checked in by uli, 11 years ago

Merge current trunk into 0.2 maintenance branch. Now really

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