source: main/waeup.kofa/branches/uli-upgrade-dependencies/src/waeup/kofa/applicants/tests/test_browser.py @ 13621

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

Fix test.

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