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

Last change on this file since 8405 was 8405, checked in by Henrik Bettermann, 13 years ago

Add browser tests for applicant search facility.

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