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

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

Disable application slip download in state initialized (managers only) and started.

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