source: main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/applicants/tests/test_browser.py @ 10009

Last change on this file since 10009 was 9208, checked in by uli, 12 years ago

Merge changes from trunk r9171:9207.

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