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

Last change on this file since 9535 was 9531, checked in by Henrik Bettermann, 12 years ago

Log changes when saving ApplicantsContainerManageFormPage?.

  • Property svn:keywords set to Id
File size: 50.6 KB
Line 
1## $Id: test_browser.py 9531 2012-11-04 21:51:57Z 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 zope.event import notify
31from zope.catalog.interfaces import ICatalog
32from zope.component import createObject, getUtility
33from zope.component.hooks import setSite, clearSite
34from zope.security.interfaces import Unauthorized
35from zope.testbrowser.testing import Browser
36from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
37from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
38from waeup.kofa.app import University
39from waeup.kofa.payments.interfaces import IPaymentWebservice
40from waeup.kofa.configuration import SessionConfiguration
41from waeup.kofa.applicants.container import ApplicantsContainer
42from waeup.kofa.applicants.applicant import Applicant
43from waeup.kofa.interfaces import (
44    IExtFileStore, IFileStoreNameChooser, IUserAccount)
45from waeup.kofa.university.faculty import Faculty
46from waeup.kofa.university.department import Department
47
48PH_LEN = 2059  # Length of placeholder file
49
50session_1 = datetime.now().year - 2
51container_name_1 = u'app%s' % session_1
52session_2 = datetime.now().year - 1
53container_name_2 = u'app%s' % session_2
54
55class ApplicantsFullSetup(FunctionalTestCase):
56    # A test case that only contains a setup and teardown
57    #
58    # Complete setup for applicants handlings is rather complex and
59    # requires lots of things created before we can start. This is a
60    # setup that does all this, creates a university, creates PINs,
61    # etc.  so that we do not have to bother with that in different
62    # test cases.
63
64    layer = FunctionalLayer
65
66    def setUp(self):
67        super(ApplicantsFullSetup, self).setUp()
68
69        # Setup a sample site for each test
70        app = University()
71        self.dc_root = tempfile.mkdtemp()
72        app['datacenter'].setStoragePath(self.dc_root)
73
74        # Prepopulate the ZODB...
75        self.getRootFolder()['app'] = app
76        # we add the site immediately after creation to the
77        # ZODB. Catalogs and other local utilities are not setup
78        # before that step.
79        self.app = self.getRootFolder()['app']
80        # Set site here. Some of the following setup code might need
81        # to access grok.getSite() and should get our new app then
82        setSite(app)
83
84        self.login_path = 'http://localhost/app/login'
85        self.root_path = 'http://localhost/app/applicants'
86        self.search_path = 'http://localhost/app/applicants/search'
87        self.manage_root_path = self.root_path + '/@@manage'
88        self.add_container_path = self.root_path + '/@@add'
89        self.container_path = 'http://localhost/app/applicants/%s' % container_name_1
90        self.manage_container_path = self.container_path + '/@@manage'
91
92        # Add an applicants container
93        applicantscontainer = ApplicantsContainer()
94        applicantscontainer.code = container_name_1
95        applicantscontainer.prefix = 'app'
96        applicantscontainer.year = session_1
97        applicantscontainer.title = u'This is the %s container' % container_name_1
98        applicantscontainer.application_category = 'basic'
99        applicantscontainer.mode = 'create'
100        applicantscontainer.strict_deadline = True
101        delta = timedelta(days=10)
102        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
103        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
104        self.app['applicants'][container_name_1] = applicantscontainer
105        self.applicantscontainer = self.app['applicants'][container_name_1]
106
107        # Populate university
108        certificate = createObject('waeup.Certificate')
109        certificate.code = 'CERT1'
110        certificate.application_category = 'basic'
111        certificate.start_level = 100
112        certificate.end_level = 500
113        certificate.study_mode = u'ug_ft'
114        self.certificate = certificate
115        self.app['faculties']['fac1'] = Faculty()
116        # The code has explicitely to be set, otherwise we don't
117        # find created students in their department
118        self.app['faculties']['fac1']['dep1'] = Department(code='dep1')
119        self.department = self.app['faculties']['fac1']['dep1']
120        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
121            certificate)
122
123        # Put the prepopulated site into test ZODB and prepare test
124        # browser
125        self.browser = Browser()
126        self.browser.handleErrors = False
127
128        # Create 5 access codes with prefix'FOO' and cost 9.99 each
129        pin_container = self.app['accesscodes']
130        pin_container.createBatch(
131            datetime.now(), 'some_userid', 'APP', 9.99, 5)
132        pins = pin_container[pin_container.keys()[0]].values()
133        self.pins = [x.representation for x in pins]
134        self.existing_pin = self.pins[0]
135        parts = self.existing_pin.split('-')[1:]
136        self.existing_series, self.existing_number = parts
137
138        # Add an applicant
139        self.applicant = createObject('waeup.Applicant')
140        # reg_number is the only field which has to be preset here
141        # because managers are allowed to edit this required field
142        self.applicant.reg_number = u'1234'
143        self.applicant.course1 = certificate
144        app['applicants'][container_name_1].addApplicant(self.applicant)
145        IUserAccount(
146            self.app['applicants'][container_name_1][
147            self.applicant.application_number]).setPassword('apwd')
148        self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % (
149            container_name_1, self.applicant.application_number, 'manage')
150        self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % (
151            container_name_1, self.applicant.application_number, 'edit')
152        self.view_path = 'http://localhost/app/applicants/%s/%s' % (
153            container_name_1, self.applicant.application_number)
154
155    def login(self):
156        # Perform an applicant login. This creates an applicant record.
157        #
158        # This helper also sets `self.applicant`, which is the
159        # applicant object created.
160        self.browser.open(self.login_path)
161        self.browser.getControl(
162            name="form.login").value = self.applicant.applicant_id
163        self.browser.getControl(name="form.password").value = 'apwd'
164        self.browser.getControl("Login").click()
165
166    def fill_correct_values(self):
167        # Fill the edit form with suitable values
168        self.browser.getControl(name="form.firstname").value = 'John'
169        self.browser.getControl(name="form.lastname").value = 'Tester'
170        self.browser.getControl(name="form.course1").value = ['CERT1']
171        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
172        self.browser.getControl(name="form.sex").value = ['m']
173        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
174
175    def tearDown(self):
176        super(ApplicantsFullSetup, self).tearDown()
177        clearSite()
178        shutil.rmtree(self.dc_root)
179
180class ApplicantsRootUITests(ApplicantsFullSetup):
181    # Tests for ApplicantsRoot class
182
183    layer = FunctionalLayer
184
185    def test_anonymous_access(self):
186        # Anonymous users can access applicants root
187        self.browser.open(self.root_path)
188        self.assertEqual(self.browser.headers['Status'], '200 Ok')
189        self.assertFalse(
190            'Manage ' in self.browser.contents)
191        return
192
193    def test_anonymous_no_actions(self):
194        # Make sure anonymous users cannot access actions
195        self.browser.open(self.root_path)
196        self.assertRaises(
197            LookupError, self.browser.getControl, "Add local role")
198        # Manage screen neither linked nor accessible for anonymous
199        self.assertRaises(
200            LinkNotFoundError,
201            self.browser.getLink, 'Manage application section')
202        self.assertRaises(
203            Unauthorized, self.browser.open, self.manage_root_path)
204        # Add container screen not accessible for anonymous
205        self.assertRaises(
206            Unauthorized, self.browser.open, self.add_container_path)
207        return
208
209    def test_manage_access(self):
210        # Managers can access the manage pages of applicants root
211        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
212        self.browser.open(self.root_path)
213        self.assertTrue('Manage application section' in self.browser.contents)
214        # There is a manage link
215        link = self.browser.getLink('Manage application section')
216        link.click()
217        self.assertEqual(self.browser.headers['Status'], '200 Ok')
218        self.assertEqual(self.browser.url, self.manage_root_path)
219        return
220
221    def test_search(self):
222        # Managers can access the manage pages of applicants root
223        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
224        self.browser.open(self.manage_path)
225        self.fill_correct_values()
226        self.browser.getControl("Save").click()
227        self.browser.open(self.root_path)
228        self.assertTrue('Manage application section' in self.browser.contents)
229        # There is a search link
230        link = self.browser.getLink('Search applicants')
231        link.click()
232        self.assertEqual(self.browser.headers['Status'], '200 Ok')
233        # We can find an applicant ...
234        # ... via his name
235        self.browser.getControl(name="searchtype").value = ['fullname']
236        self.browser.getControl(name="searchterm").value = 'John'
237        self.browser.getControl("Search").click()
238        self.assertTrue('John Tester' in self.browser.contents)
239        self.browser.getControl(name="searchtype").value = ['fullname']
240        self.browser.getControl(name="searchterm").value = 'Tester'
241        self.browser.getControl("Search").click()
242        self.assertTrue('John Tester' in self.browser.contents)
243        self.browser.open(self.search_path)
244        # ... and via his reg_number ...
245        self.browser.getControl(name="searchtype").value = ['reg_number']
246        self.browser.getControl(name="searchterm").value = '2345'
247        self.browser.getControl("Search").click()
248        self.assertFalse('John Tester' in self.browser.contents)
249        self.browser.getControl(name="searchtype").value = ['reg_number']
250        self.browser.getControl(name="searchterm").value = '1234'
251        self.browser.getControl("Search").click()
252        self.assertTrue('John Tester' in self.browser.contents)
253        # ... and not via his application_number ...
254        self.browser.getControl(name="searchtype").value = ['applicant_id']
255        self.browser.getControl(
256            name="searchterm").value = self.applicant.application_number
257        self.browser.getControl("Search").click()
258        self.assertFalse('John Tester' in self.browser.contents)
259        # ... but ia his applicant_id ...
260        self.browser.getControl(name="searchtype").value = ['applicant_id']
261        self.browser.getControl(
262            name="searchterm").value = self.applicant.applicant_id
263        self.browser.getControl("Search").click()
264        self.assertTrue('John Tester' in self.browser.contents)
265        # ... and via his email
266        self.browser.getControl(name="searchtype").value = ['email']
267        self.browser.getControl(name="searchterm").value = 'xx@yy.zz'
268        self.browser.getControl("Search").click()
269        self.assertTrue('John Tester' in self.browser.contents)
270        return
271
272    def test_manage_actions_access(self):
273        # Managers can access the action on manage screen
274        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
275        self.browser.open(self.manage_root_path)
276        self.browser.getControl("Add local role").click()
277        self.assertTrue('No user selected' in self.browser.contents)
278        return
279
280    # We have no local roles yet
281    #def test_local_roles_add_delete(self):
282    #    # Managers can assign and delete local roles of applicants root
283    #    myusers = self.app['users']
284    #    myusers.addUser('bob', 'bobssecret')
285    #    self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
286    #    self.browser.open(self.manage_root_path)
287    #    self.browser.getControl(name="user").value = ['bob']
288    #    self.browser.getControl(name="local_role").value = [
289    #        'waeup.local.ApplicationsOfficer']
290    #    self.browser.getControl("Add local role").click()
291    #    self.assertTrue('<td>bob</td>' in self.browser.contents)
292    #    # Remove the role assigned
293    #    ctrl = self.browser.getControl(name='role_id')
294    #    ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
295    #    self.browser.getControl("Remove selected local roles").click()
296    #    self.assertTrue('Successfully removed:' in self.browser.contents)
297    #    self.assertFalse('<td>bob</td>' in self.browser.contents)
298    #    return
299
300    def test_add_delete_container(self):
301        # Managers can add and delete applicants containers
302        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
303        self.browser.open(self.manage_root_path)
304        self.browser.getControl("Add applicants container").click()
305        self.assertEqual(self.browser.headers['Status'], '200 Ok')
306        self.assertEqual(self.browser.url, self.add_container_path)
307        self.browser.getControl(name="form.prefix").value = ['app']
308        self.browser.getControl("Add applicants container").click()
309        self.assertTrue(
310            'There were errors' in self.browser.contents)
311        self.browser.getControl(name="form.prefix").value = ['app']
312        self.browser.getControl(name="form.year").value = [str(session_2)]
313        self.browser.getControl(name="form.mode").value = ['create']
314        self.browser.getControl(
315            name="form.application_category").value = ['basic']
316        self.browser.getControl("Add applicants container").click()
317        self.assertTrue('Added:' in self.browser.contents)
318        self.browser.getLink("app2010").click()
319        self.assertTrue('Manage applicants container'
320            in self.browser.contents)
321        self.browser.open(self.add_container_path)
322        self.browser.getControl("Cancel").click()
323        self.assertEqual(self.browser.url, self.manage_root_path)
324        self.browser.open(self.add_container_path)
325        self.browser.getControl(name="form.prefix").value = ['app']
326        self.browser.getControl(name="form.year").value = [str(session_2)]
327        self.browser.getControl(name="form.mode").value = ['create']
328        self.browser.getControl(
329            name="form.application_category").value = ['basic']
330        self.browser.getControl("Add applicants container").click()
331        self.assertTrue('exists already in the database'
332                        in self.browser.contents)
333        self.browser.open(self.manage_root_path)
334        ctrl = self.browser.getControl(name='val_id')
335        ctrl.getControl(value=container_name_2).selected = True
336        self.browser.getControl("Remove selected", index=0).click()
337        self.assertTrue('Successfully removed:' in self.browser.contents)
338        self.browser.open(self.add_container_path)
339        self.browser.getControl(name="form.prefix").value = ['app']
340        self.browser.getControl(name="form.year").value = [str(session_2)]
341        self.browser.getControl(name="form.mode").value = ['create']
342        #self.browser.getControl(name="form.ac_prefix").value = ['APP']
343        self.browser.getControl(
344            name="form.application_category").value = ['basic']
345        self.browser.getControl("Add applicants container").click()
346        del self.app['applicants'][container_name_2]
347        ctrl = self.browser.getControl(name='val_id')
348        ctrl.getControl(value=container_name_2).selected = True
349        self.browser.getControl("Remove selected", index=0).click()
350        self.assertMatches('...Could not delete...', self.browser.contents)
351        return
352
353class ApplicantsContainerUITests(ApplicantsFullSetup):
354    # Tests for ApplicantsContainer class views and pages
355
356    layer = FunctionalLayer
357
358    def test_anonymous_access(self):
359        # Anonymous users can access applicants containers
360        self.browser.open(self.container_path)
361        self.assertEqual(self.browser.headers['Status'], '200 Ok')
362        self.assertFalse(
363            'Manage ' in self.browser.contents)
364        return
365
366    def test_manage_access(self):
367        # Managers can access the manage pages of applicants
368        # containers and can perform actions
369        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
370        self.browser.open(self.manage_container_path)
371        self.assertEqual(self.browser.headers['Status'], '200 Ok')
372        self.assertEqual(self.browser.url, self.manage_container_path)
373        self.browser.getControl(name="form.application_fee").value = '200'
374        self.browser.getControl("Save").click()
375        self.assertTrue('Form has been saved' in self.browser.contents)
376        logfile = os.path.join(
377            self.app['datacenter'].storage, 'logs', 'applicants.log')
378        logcontent = open(logfile).read()
379        self.assertTrue(
380            'zope.mgr - applicants.browser.ApplicantsContainerManageFormPage - '
381            'app2010 - saved: application_fee\n' in logcontent)
382        self.browser.getControl("Remove selected", index=0).click()
383        self.assertTrue('No applicant selected' in self.browser.contents)
384        self.browser.getControl("Add local role").click()
385        self.assertTrue('No user selected' in self.browser.contents)
386        self.browser.getControl("Cancel", index=0).click()
387        self.assertEqual(self.browser.url, self.container_path)
388        return
389
390    def test_statistics(self):
391        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
392        self.browser.open(self.container_path)
393        self.browser.getLink("Container statistics").click()
394        self.assertTrue('<td>initialized</td>' in self.browser.contents)
395        self.assertTrue('<td>1</td>' in self.browser.contents)
396        self.assertEqual(self.applicantscontainer.statistics[0],
397            {'not admitted': 0, 'started': 0, 'created': 0,
398            'admitted': 0, 'submitted': 0, 'initialized': 1, 'paid': 0})
399        #self.assertEqual(self.applicantscontainer.statistics[1],
400        #    {u'fac1': 0})
401        IWorkflowState(self.applicant).setState('submitted')
402        notify(grok.ObjectModifiedEvent(self.applicant))
403        self.assertEqual(self.applicantscontainer.statistics[0],
404            {'not admitted': 0, 'started': 0, 'created': 0,
405            'admitted': 0, 'submitted': 1, 'initialized': 0, 'paid': 0})
406        #self.assertEqual(self.applicantscontainer.statistics[1],
407        #    {u'fac1': 1})
408        return
409
410    def test_add_delete_applicants(self):
411        # Managers can add and delete applicants
412        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
413        self.add_applicant_path = self.container_path + '/addapplicant'
414        self.container_manage_path = self.container_path + '/@@manage'
415        self.browser.open(self.container_manage_path)
416        self.browser.getLink("Add applicant").click()
417        self.assertEqual(self.browser.headers['Status'], '200 Ok')
418        self.assertEqual(self.browser.url, self.add_applicant_path)
419        self.browser.getControl(name="form.firstname").value = 'Alois'
420        self.browser.getControl(name="form.middlename").value = 'Kofi'
421        self.browser.getControl(name="form.lastname").value = 'Bettermann'
422        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
423        self.browser.getControl("Create application record").click()
424        self.assertTrue('Application initialized' in self.browser.contents)
425        self.browser.open(self.container_manage_path)
426        self.assertEqual(self.browser.headers['Status'], '200 Ok')
427        ctrl = self.browser.getControl(name='val_id')
428        value = ctrl.options[0]
429        ctrl.getControl(value=value).selected = True
430        self.browser.getControl("Remove selected", index=0).click()
431        self.assertTrue('Successfully removed:' in self.browser.contents)
432        self.browser.open(self.add_applicant_path)
433        self.browser.getControl(name="form.firstname").value = 'Albert'
434        self.browser.getControl(name="form.lastname").value = 'Einstein'
435        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
436        self.browser.getControl("Create application record").click()
437        self.assertTrue('Application initialized' in self.browser.contents)
438        return
439
440class ApplicantUITests(ApplicantsFullSetup):
441    # Tests for uploading/browsing the passport image of appplicants
442
443    layer = FunctionalLayer
444
445    def test_manage_and_view_applicant(self):
446        # Managers can manage applicants
447        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
448        self.slip_path = self.view_path + '/application_slip.pdf'
449        self.browser.open(self.manage_path)
450        self.assertEqual(self.browser.headers['Status'], '200 Ok')
451        self.fill_correct_values()
452        # Fire transition
453        self.browser.getControl(name="transition").value = ['start']
454        self.browser.getControl("Save").click()
455        # Be sure that the empty phone field does not show wrong error message
456        self.assertFalse('Required input is missing' in self.browser.contents)
457        self.assertMatches('...Form has been saved...', self.browser.contents)
458        self.assertMatches('...Application started by Manager...',
459                           self.browser.contents)
460        self.browser.open(self.view_path)
461        self.assertEqual(self.browser.headers['Status'], '200 Ok')
462        # Change course_admitted
463        self.browser.open(self.manage_path)
464        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
465        self.browser.getControl("Save").click()
466        self.assertMatches('...Form has been saved...', self.browser.contents)
467        # Change password
468        self.browser.getControl(name="password").value = 'secret'
469        self.browser.getControl(name="control_password").value = 'secre'
470        self.browser.getControl("Save").click()
471        self.assertMatches('...Passwords do not match...',
472                           self.browser.contents)
473        self.browser.getControl(name="password").value = 'secret'
474        self.browser.getControl(name="control_password").value = 'secret'
475        self.browser.getControl("Save").click()
476        self.assertMatches('...Form has been saved...', self.browser.contents)
477        # Pdf slip can't be opened and download button is not available
478        self.assertFalse('Download application slip' in self.browser.contents)
479        self.browser.open(self.slip_path)
480        self.assertTrue(
481            'Please pay and submit before trying to download the application slip.'
482            in self.browser.contents)
483        # If applicant is in correct state the pdf slip can be opened.
484        IWorkflowState(self.applicant).setState('submitted')
485        self.browser.open(self.manage_path)
486        self.browser.getLink("Download application slip").click()
487        self.assertEqual(self.browser.headers['Status'], '200 Ok')
488        self.assertEqual(self.browser.headers['Content-Type'],
489                         'application/pdf')
490        # Managers can view applicants even if certificate has been removed
491        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
492        self.browser.open(self.view_path)
493        self.assertEqual(self.browser.headers['Status'], '200 Ok')
494        self.browser.open(self.slip_path)
495        self.assertEqual(self.browser.headers['Status'], '200 Ok')
496        return
497
498    def test_passport_edit_view(self):
499        # We get a default image after login
500        self.browser.open(self.login_path)
501        self.login()
502        self.browser.open(self.browser.url + '/passport.jpg')
503        self.assertEqual(self.browser.headers['status'], '200 Ok')
504        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
505        self.assertTrue('JFIF' in self.browser.contents)
506        self.assertEqual(
507            self.browser.headers['content-length'], str(PH_LEN))
508
509    def test_applicant_login(self):
510        self.applicant.suspended = True
511        self.login()
512        self.assertTrue(
513            'You entered invalid credentials.' in self.browser.contents)
514        self.applicant.suspended = False
515        self.browser.getControl("Login").click()
516        self.assertTrue(
517            'You logged in.' in self.browser.contents)
518
519    def test_applicant_access(self):
520        # Applicants can edit their record
521        self.browser.open(self.login_path)
522        self.login()
523        self.assertTrue(
524            'You logged in.' in self.browser.contents)
525        self.browser.open(self.edit_path)
526        self.assertTrue(self.browser.url != self.login_path)
527        self.assertEqual(self.browser.headers['Status'], '200 Ok')
528        self.fill_correct_values()
529        self.assertTrue(IUserAccount(self.applicant).checkPassword('apwd'))
530        self.browser.getControl("Save").click()
531        self.assertMatches('...Form has been saved...', self.browser.contents)
532        # Applicants don't see manage and search links ...
533        self.browser.open(self.root_path)
534        self.assertEqual(self.browser.headers['Status'], '200 Ok')
535        self.assertFalse('Search' in self.browser.contents)
536        self.assertFalse('Manage application section' in self.browser.contents)
537        # ... and can't access the manage page
538        self.assertRaises(
539            Unauthorized, self.browser.open, self.manage_path)
540        return
541
542    def image_url(self, filename):
543        return self.edit_path.replace('edit', filename)
544
545    def test_after_login_default_browsable(self):
546        # After login we see the placeholder image in the edit view
547        self.login()
548        self.assertEqual(self.browser.url, self.view_path)
549        self.browser.open(self.edit_path)
550        # There is a correct <img> link included
551        self.assertTrue(
552              '<img src="passport.jpg" height="180px" />' in self.browser.contents)
553        # Browsing the link shows a real image
554        self.browser.open(self.image_url('passport.jpg'))
555        self.assertEqual(
556            self.browser.headers['content-type'], 'image/jpeg')
557        self.assertEqual(len(self.browser.contents), PH_LEN)
558
559    def test_after_submit_default_browsable(self):
560        # After submitting an applicant form the default image is
561        # still visible
562        self.login()
563        self.browser.open(self.edit_path)
564        self.browser.getControl("Save").click() # submit form
565        # There is a correct <img> link included
566        self.assertTrue(
567            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
568        # Browsing the link shows a real image
569        self.browser.open(self.image_url('passport.jpg'))
570        self.assertEqual(
571            self.browser.headers['content-type'], 'image/jpeg')
572        self.assertEqual(len(self.browser.contents), PH_LEN)
573
574    def test_uploaded_image_respects_file_size_restriction(self):
575        # When we upload an image that is too big ( > 10 KB) we will
576        # get an error message
577        self.login()
578        self.browser.open(self.edit_path)
579        # Create a pseudo image file and select it to be uploaded in form
580        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
581        pseudo_image = StringIO(photo_content)
582        ctrl = self.browser.getControl(name='form.passport')
583        file_ctrl = ctrl.mech_control
584        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
585        self.browser.getControl("Save").click() # submit form
586        # There is a correct <img> link included
587        self.assertTrue(
588            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
589        # We get a warning message
590        self.assertTrue(
591            'Uploaded image is too big' in self.browser.contents)
592        # Browsing the image gives us the default image, not the
593        # uploaded one.
594        self.browser.open(self.image_url('passport.jpg'))
595        self.assertEqual(
596            self.browser.headers['content-type'], 'image/jpeg')
597        self.assertEqual(len(self.browser.contents), PH_LEN)
598        # There is really no file stored for the applicant
599        img = getUtility(IExtFileStore).getFile(
600            IFileStoreNameChooser(self.applicant).chooseName())
601        self.assertTrue(img is None)
602
603    def test_uploaded_image_browsable_w_errors(self):
604        # We can upload a different image and browse it after submit,
605        # even if there are still errors in the form
606        self.login()
607        self.browser.open(self.edit_path)
608        # Create a pseudo image file and select it to be uploaded in form
609        photo_content = 'I pretend to be a graphics file'
610        pseudo_image = StringIO(photo_content)
611        ctrl = self.browser.getControl(name='form.passport')
612        file_ctrl = ctrl.mech_control
613        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
614        self.browser.getControl("Save").click() # submit form
615        # There is a correct <img> link included
616        self.assertTrue(
617            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
618        # Browsing the link shows a real image
619        self.browser.open(self.image_url('passport.jpg'))
620        self.assertEqual(
621            self.browser.headers['content-type'], 'image/jpeg')
622        self.assertEqual(self.browser.contents, photo_content)
623
624    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
625        # After uploading a new passport pic the file is correctly
626        # stored in an imagestorage
627        self.login()
628        self.browser.open(self.edit_path)
629        # Create a pseudo image file and select it to be uploaded in form
630        pseudo_image = StringIO('I pretend to be a graphics file')
631        ctrl = self.browser.getControl(name='form.passport')
632        file_ctrl = ctrl.mech_control
633        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
634        self.browser.getControl("Save").click() # submit form
635        storage = getUtility(IExtFileStore)
636        file_id = IFileStoreNameChooser(self.applicant).chooseName()
637        pseudo_image.seek(0) # reset our file data source
638        self.assertEqual(
639            storage.getFile(file_id).read(), pseudo_image.read())
640        return
641
642    def test_uploaded_image_browsable_wo_errors(self):
643        # We can upload a different image and browse it after submit,
644        # if there are no errors in form
645        self.login()
646        self.browser.open(self.edit_path)
647        self.fill_correct_values() # fill other fields with correct values
648        # Create a pseudo image file and select it to be uploaded in form
649        pseudo_image = StringIO('I pretend to be a graphics file')
650        ctrl = self.browser.getControl(name='form.passport')
651        file_ctrl = ctrl.mech_control
652        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
653        self.browser.getControl("Save").click() # submit form
654        # There is a correct <img> link included
655        self.assertTrue(
656            '<img src="passport.jpg" height="180px" />' in self.browser.contents)
657        # Browsing the link shows a real image
658        self.browser.open(self.image_url('passport.jpg'))
659        self.assertEqual(
660            self.browser.headers['content-type'], 'image/jpeg')
661        self.assertEqual(len(self.browser.contents), 31)
662
663    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
664        # After uploading a new passport pic the file is correctly
665        # stored in an imagestorage if form contains no errors
666        self.login()
667        self.browser.open(self.edit_path)
668        self.fill_correct_values() # fill other fields with correct values
669        # Create a pseudo image file and select it to be uploaded in form
670        pseudo_image = StringIO('I pretend to be a graphics file')
671        ctrl = self.browser.getControl(name='form.passport')
672        file_ctrl = ctrl.mech_control
673        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
674        self.browser.getControl("Save").click() # submit form
675        storage = getUtility(IExtFileStore)
676        file_id = IFileStoreNameChooser(self.applicant).chooseName()
677        # The stored image can be fetched
678        fd = storage.getFile(file_id)
679        file_len = len(fd.read())
680        self.assertEqual(file_len, 31)
681
682    def test_uploaded_images_equal(self):
683        # Make sure uploaded images do really differ if we eject a
684        # change notfication (and do not if we don't)
685        self.login()
686        self.browser.open(self.edit_path)
687        self.fill_correct_values() # fill other fields with correct values
688        self.browser.getControl("Save").click() # submit form
689        # Now go on as an officer
690        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
691        self.browser.open(self.manage_path)
692
693        # Create a pseudo image file and select it to be uploaded in form
694        pseudo_image = StringIO('I pretend to be a graphics file')
695        ctrl = self.browser.getControl(name='form.passport')
696        file_ctrl = ctrl.mech_control
697        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
698        file_id = IFileStoreNameChooser(self.applicant).chooseName()
699        setSite(self.app)
700        passport0 = getUtility(IExtFileStore).getFile(file_id)
701        self.browser.getControl("Save").click() # submit form with changed pic
702        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
703        self.browser.getControl("Save").click() # submit form w/o changes
704        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
705        self.assertTrue(passport0 is None)
706        self.assertTrue(passport0 != passport1)
707        self.assertTrue(passport1 == passport2)
708        return
709
710    def test_pay_application_fee(self):
711        self.login()
712        self.browser.open(self.edit_path)
713        # Payment tickets can't be created before the form has been validated
714        self.browser.getControl("Add online payment ticket").click()
715        self.assertTrue('Required input is missing' in self.browser.contents)
716        self.fill_correct_values()
717        # We have to save the form otherwise the filled fields will be cleared
718        # after adding an online payment, because adding an online payment
719        # requires a filled form but does not save it
720        self.browser.getControl("Save").click()
721        self.browser.getControl("Add online payment ticket").click()
722        # Session object missing
723        self.assertTrue(
724            'Session configuration object is not available'
725            in self.browser.contents)
726        configuration = SessionConfiguration()
727        configuration.academic_session = session_1
728        configuration.application_fee = 200.0
729        self.app['configuration'].addSessionConfiguration(configuration)
730        self.browser.open(self.edit_path)
731        self.browser.getControl("Add online payment ticket").click()
732        self.assertMatches('...Payment ticket created...',
733                           self.browser.contents)
734        self.assertMatches('...Activation Code...',
735                           self.browser.contents)
736        # Payment ticket can be removed if they haven't received a
737        # valid callback
738        self.browser.open(self.edit_path)
739        ctrl = self.browser.getControl(name='val_id')
740        value = ctrl.options[0]
741        ctrl.getControl(value=value).selected = True
742        self.browser.getControl("Remove selected", index=0).click()
743        self.assertMatches('...Successfully removed...', self.browser.contents)
744        # We will try the callback request view
745        self.browser.getControl("Add online payment ticket").click()
746        self.browser.open(self.edit_path)
747        ctrl = self.browser.getControl(name='val_id')
748        value = ctrl.options[0]
749        self.browser.getLink(value).click()
750        self.assertMatches('...Amount Authorized...',
751                           self.browser.contents)
752        payment_url = self.browser.url
753        payment_id = self.applicant.keys()[0]
754        payment = self.applicant[payment_id]
755        self.assertEqual(payment.p_item,'This is the %s container' % container_name_1)
756        self.assertEqual(payment.p_session, session_1)
757        self.assertEqual(payment.p_category,'application')
758        self.assertEqual(payment.amount_auth,200.0)
759        # Applicant is payee of the payment ticket.
760        self.assertEqual(
761            IPaymentWebservice(payment).display_fullname, 'John Tester')
762        self.assertEqual(
763            IPaymentWebservice(payment).id, self.applicant.applicant_id)
764        self.assertEqual(IPaymentWebservice(payment).faculty, '')
765        self.assertEqual(IPaymentWebservice(payment).department, '')
766        # The pdf payment slip can't yet be opened
767        #self.browser.open(payment_url + '/payment_receipt.pdf')
768        #self.assertMatches('...Ticket not yet paid...',
769        #                   self.browser.contents)
770        # Approve payment
771        # Applicants can't approve payments
772        self.assertRaises(
773            Unauthorized, self.browser.open, payment_url + '/approve')
774        # We approve the payment by bypassing the view
775        payment.approve()
776        # The payment slip can be downloaded now
777        self.browser.open(payment_url)
778        self.browser.getLink("Download payment slip").click()
779        self.assertEqual(self.browser.headers['Status'], '200 Ok')
780        self.assertEqual(self.browser.headers['Content-Type'],
781                         'application/pdf')
782        # Applicant is is not yet in state 'paid' because it was only
783        # the payment which we set to paid
784        self.browser.open(self.view_path)
785        self.assertMatches('...started...',
786                           self.browser.contents)
787        self.assertTrue(self.applicant.state == 'started')
788        # Let's logout and approve the payment as manager
789        self.browser.getLink("Logout").click()
790        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
791        # First we reset the payment
792        payment.r_amount_approved = 0.0
793        payment.r_code = u''
794        payment.p_state = 'unpaid'
795        payment.r_desc = u''
796        payment.payment_date = None
797        self.browser.open(payment_url)
798        self.browser.getLink("Approve payment").click()
799        self.assertEqual(payment.p_state, 'paid')
800        self.assertEqual(payment.r_amount_approved, 200.0)
801        self.assertEqual(payment.r_code, 'AP')
802        self.assertTrue(self.applicant.state == 'paid')
803        return
804
805    def test_final_submit(self):
806        # Make sure that a correctly filled form with passport picture
807        # can be submitted (only) after payment
808        self.login()
809        self.browser.getLink("Edit application record").click()
810        self.assertFalse('Final Submit' in self.browser.contents)
811        IWorkflowInfo(self.applicant).fireTransition('pay')
812        self.browser.open(self.edit_path)
813        self.assertTrue('Final Submit' in self.browser.contents)
814        self.fill_correct_values() # fill other fields with correct values
815        self.browser.getControl("Save").click()
816        self.browser.getControl("Final Submit").click()
817        # We forgot to upload a passport picture
818        self.assertTrue(
819            'No passport picture uploaded' in self.browser.contents)
820        # Create a pseudo image file and select it to be uploaded in form
821        pseudo_image = StringIO('I pretend to be a graphics file')
822        ctrl = self.browser.getControl(name='form.passport')
823        file_ctrl = ctrl.mech_control
824        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
825        self.browser.getControl("Final Submit").click() # (finally) submit form
826        # The picture has been uploaded but the form cannot be submitted
827        # since the passport confirmation box was not ticked
828        self.assertTrue(
829            'Passport picture confirmation box not ticked'
830            in self.browser.contents)
831        self.browser.getControl(name="confirm_passport").value = True
832        # If application period has expired and strict-deadline is set
833        # applicants do notsee edit button and can't open
834        # the edit form.
835        self.applicantscontainer.enddate = datetime.now(pytz.utc)
836        self.browser.open(self.view_path)
837        self.assertFalse(
838            'Edit application record' in self.browser.contents)
839        self.browser.open(self.edit_path)
840        self.assertTrue(
841            'form is locked' in self.browser.contents)
842        # We can either postpone the enddate ...
843        self.applicantscontainer.enddate = datetime.now(
844            pytz.utc) + timedelta(days=10)
845        self.browser.open(self.edit_path)
846        self.browser.getControl(name="confirm_passport").value = True
847        self.browser.getControl("Final Submit").click()
848        self.assertTrue(
849            'Application submitted' in self.browser.contents)
850        # ... or allow submission after deadline.
851        IWorkflowState(self.applicant).setState('paid')
852        self.applicant.locked = False
853        self.applicantscontainer.strict_deadline = False
854        self.browser.open(self.edit_path)
855        self.browser.getControl(name="confirm_passport").value = True
856        self.browser.getControl("Final Submit").click()
857        self.assertTrue(
858            'Application submitted' in self.browser.contents)
859        self.browser.goBack(count=1)
860        self.browser.getControl("Save").click()
861        # The form is locked.
862        self.assertTrue(self.applicant.locked)
863        self.assertTrue(
864            'The requested form is locked' in self.browser.contents)
865        self.browser.goBack(count=1)
866        self.browser.getControl("Final Submit").click()
867        self.assertTrue(
868            'The requested form is locked' in self.browser.contents)
869        return
870
871    def test_locking(self):
872        # Make sure that locked forms can't be submitted
873        self.login()
874        self.browser.open(self.edit_path)
875        self.fill_correct_values() # fill other fields with correct values
876        # Create a pseudo image file and select it to be uploaded in form
877        pseudo_image = StringIO('I pretend to be a graphics file')
878        ctrl = self.browser.getControl(name='form.passport')
879        file_ctrl = ctrl.mech_control
880        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
881        self.browser.getControl("Save").click()
882        # Now we lock the form
883        self.applicant.locked = True
884        self.browser.open(self.edit_path)
885        self.assertEqual(self.browser.headers['Status'], '200 Ok')
886        self.assertTrue(
887            'The requested form is locked' in self.browser.contents)
888        return
889
890    def test_certificate_removed(self):
891        self.login()
892        self.browser.open(self.edit_path)
893        self.fill_correct_values()
894        self.browser.getControl("Save").click()
895        self.browser.open(self.view_path)
896        self.assertTrue(
897            'Unnamed Certificate' in self.browser.contents)
898        self.browser.open(self.edit_path)
899        self.assertTrue(
900            '<option selected="selected" value="CERT1">' in self.browser.contents)
901        # Now we remove the certificate
902        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
903        # The certificate is still shown in display mode
904        self.browser.open(self.view_path)
905        self.assertTrue(
906            'Unnamed Certificate' in self.browser.contents)
907        # The certificate is still selectable in edit mode so that it won't
908        # be automatically replaced by another (arbitrary) certificate
909        self.browser.open(self.edit_path)
910        self.assertTrue(
911            '<option selected="selected" value="CERT1">' in self.browser.contents)
912        # Consequently, the certificate is still shown after saving the form
913        self.browser.getControl("Save").click()
914        self.browser.open(self.view_path)
915        self.assertTrue(
916            'Unnamed Certificate' in self.browser.contents)
917        # Even if we add a new certificate the previous (removed)
918        # certificate is shown
919        certificate = createObject('waeup.Certificate')
920        certificate.code = 'CERT2'
921        certificate.title = 'New Certificate'
922        certificate.application_category = 'basic'
923        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
924            certificate)
925        self.browser.open(self.edit_path)
926        self.assertTrue(
927            '<option selected="selected" value="CERT1">'
928            in self.browser.contents)
929
930class ApplicantRegisterTests(ApplicantsFullSetup):
931    # Tests for applicant registration
932
933    layer = FunctionalLayer
934
935    def test_register_applicant_create(self):
936        # An applicant can register himself.
937        self.browser.open(self.container_path)
938        self.browser.getLink("Register for application").click()
939        # Fill the edit form with suitable values
940        self.browser.getControl(name="form.firstname").value = 'Anna'
941        self.browser.getControl(name="form.lastname").value = 'Kurios'
942        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
943        self.browser.getControl(name="form.phone.country").value = ['+234']
944        self.browser.getControl(name="form.phone.area").value = '555'
945        self.browser.getControl(name="form.phone.ext").value = '6666666'
946        self.browser.getControl("Send login credentials").click()
947        self.assertEqual(self.browser.url,
948            self.container_path + '/registration_complete?email=xx%40yy.zz')
949        # The new applicant can be found in the catalog via the email address
950        cat = getUtility(ICatalog, name='applicants_catalog')
951        results = list(
952            cat.searchResults(email=('xx@yy.zz', 'xx@yy.zz')))
953        applicant = results[0]
954        self.assertEqual(applicant.lastname,'Kurios')
955        # The application_id has been copied to the reg_number
956        self.assertEqual(applicant.applicant_id, applicant.reg_number)
957        # The applicant can be found in the catalog via the reg_number
958        results = list(
959            cat.searchResults(
960            reg_number=(applicant.reg_number, applicant.reg_number)))
961        self.assertEqual(applicant,results[0])
962        return
963
964    def test_register_applicant_update(self):
965        # We change the application mode and check if applicants
966        # can find and update imported records instead of creating new records.
967        # First we check what happens if record does not exist.
968        self.applicantscontainer.mode = 'update'
969        self.browser.open(self.container_path + '/register')
970        self.browser.getControl(name="form.firstname").value = 'John'
971        self.browser.getControl(name="form.reg_number").value = 'anynumber'
972        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
973        self.browser.getControl("Send login credentials").click()
974        self.assertTrue('No application record found.'
975            in self.browser.contents)
976        # Even with the correct reg_number we can't register
977        # because firstname attribute is not set.
978        self.applicantscontainer.mode = 'update'
979        self.browser.open(self.container_path + '/register')
980        self.browser.getControl(name="form.firstname").value = 'John'
981        self.browser.getControl(name="form.reg_number").value = '1234'
982        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
983        self.browser.getControl("Send login credentials").click()
984        self.assertTrue('An error occurred.' in self.browser.contents)
985        # Let's set this attribute manually
986        # and try to register with a wrong name.
987        self.applicant.firstname = u'John'
988        self.browser.open(self.container_path + '/register')
989        self.browser.getControl(name="form.firstname").value = 'Johnny'
990        self.browser.getControl(name="form.reg_number").value = '1234'
991        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
992        self.browser.getControl("Send login credentials").click()
993        # Anonymous is not informed that firstname verification failed.
994        # It seems that the record doesn't exist.
995        self.assertTrue('No application record found.'
996            in self.browser.contents)
997        # Even with the correct firstname we can't register if a
998        # password has been set and used.
999        IWorkflowState(self.applicant).setState('started')
1000        self.browser.getControl(name="form.firstname").value = 'John'
1001        self.browser.getControl(name="form.reg_number").value = '1234'
1002        self.browser.getControl("Send login credentials").click()
1003        self.assertTrue('Your password has already been set and used.'
1004            in self.browser.contents)
1005        #IUserAccount(
1006        #    self.app['applicants'][container_name_1][
1007        #    self.applicant.application_number]).context.password = None
1008        # Even without unsetting the password we can re-register if state
1009        # is 'initialized'
1010        IWorkflowState(self.applicant).setState('initialized')
1011        self.browser.open(self.container_path + '/register')
1012        # The firstname field, used for verification, is not case-sensitive.
1013        self.browser.getControl(name="form.firstname").value = 'jOhn'
1014        self.browser.getControl(name="form.reg_number").value = '1234'
1015        self.browser.getControl(name="form.email").value = 'new@yy.zz'
1016        self.browser.getControl("Send login credentials").click()
1017        # Yeah, we succeded ...
1018        self.assertTrue('Your registration was successful.'
1019            in self.browser.contents)
1020        # ... and  applicant can be found in the catalog via the email address
1021        cat = getUtility(ICatalog, name='applicants_catalog')
1022        results = list(
1023            cat.searchResults(
1024            email=('new@yy.zz', 'new@yy.zz')))
1025        self.assertEqual(self.applicant,results[0])
1026        return
1027
1028    def test_change_password_request(self):
1029        self.browser.open('http://localhost/app/changepw')
1030        self.browser.getControl(name="form.identifier").value = '1234'
1031        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1032        self.browser.getControl("Send login credentials").click()
1033        self.assertTrue('No record found' in self.browser.contents)
1034        self.applicant.email = 'aa@aa.ng'
1035        # Update the catalog
1036        notify(grok.ObjectModifiedEvent(self.applicant))
1037        self.browser.open('http://localhost/app/changepw')
1038        self.browser.getControl(name="form.identifier").value = '1234'
1039        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1040        self.browser.getControl("Send login credentials").click()
1041        self.assertTrue(
1042            'An email with your user name and password has been sent'
1043            in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.