source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py @ 7125

Last change on this file since 7125 was 7113, checked in by uli, 13 years ago

First sketch of PrincipalRoleManager? specifically designed for applicants.

File size: 40.4 KB
Line 
1##
2## test_browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Tue Mar 29 11:31:11 2011 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""
23Test the applicant-related UI components.
24"""
25import shutil
26import tempfile
27from StringIO import StringIO
28from datetime import datetime, date, timedelta
29from mechanize import LinkNotFoundError
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
35from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
36from waeup.sirp.app import University
37from waeup.sirp.applicants.container import ApplicantsContainer
38from waeup.sirp.applicants.applicant import Applicant
39from waeup.sirp.interfaces import IExtFileStore, IFileStoreNameChooser
40from waeup.sirp.university.faculty import Faculty
41from waeup.sirp.university.department import Department
42
43PH_LEN = 2059  # Length of placeholder file
44
45class ApplicantsFullSetup(FunctionalTestCase):
46    # A test case that only contains a setup and teardown
47    #
48    # Complete setup for applicants handlings is rather complex and
49    # requires lots of things created before we can start. This is a
50    # setup that does all this, creates a university, creates PINs,
51    # etc.  so that we do not have to bother with that in different
52    # test cases.
53
54    layer = FunctionalLayer
55
56    def setUp(self):
57        super(ApplicantsFullSetup, self).setUp()
58
59        # Setup a sample site for each test
60        app = University()
61        self.dc_root = tempfile.mkdtemp()
62        app['datacenter'].setStoragePath(self.dc_root)
63
64        # Prepopulate the ZODB...
65        self.getRootFolder()['app'] = app
66        # we add the site immediately after creation to the
67        # ZODB. Catalogs and other local utilities are not setup
68        # before that step.
69        self.app = self.getRootFolder()['app']
70        # Set site here. Some of the following setup code might need
71        # to access grok.getSite() and should get our new app then
72        setSite(app)
73
74        self.root_path = 'http://localhost/app/applicants'
75        self.manage_root_path = self.root_path + '/@@manage'
76        self.add_container_path = self.root_path + '/@@add'
77        self.container_path = 'http://localhost/app/applicants/app2009'
78        self.manage_container_path = self.container_path + '/@@manage'
79
80        # Add an applicants container
81        applicantscontainer = ApplicantsContainer()
82        applicantscontainer.ac_prefix = 'APP'
83        applicantscontainer.prefix = 'app'
84        applicantscontainer.year = 2009
85        applicantscontainer.application_category = 'basic'
86        delta = timedelta(days=10)
87        applicantscontainer.startdate = date.today() - delta
88        applicantscontainer.enddate = date.today() + delta
89        self.app['applicants']['app2009'] = applicantscontainer
90
91        # Populate university
92        certificate = createObject('waeup.Certificate')
93        certificate.code = 'CERT1'
94        certificate.application_category = 'basic'
95        self.certificate = certificate
96        self.app['faculties']['fac1'] = Faculty()
97        self.app['faculties']['fac1']['dep1'] = Department()
98        self.department = self.app['faculties']['fac1']['dep1']
99        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
100            certificate)
101
102        # Put the prepopulated site into test ZODB and prepare test
103        # browser
104        self.browser = Browser()
105        self.browser.handleErrors = False
106
107        # Create 5 access codes with prefix'FOO' and cost 9.99 each
108        pin_container = self.app['accesscodes']
109        pin_container.createBatch(
110            datetime.now(), 'some_userid', 'APP', 9.99, 5)
111        pins = pin_container[pin_container.keys()[0]].values()
112        self.pins = [x.representation for x in pins]
113        self.existing_pin = self.pins[0]
114        parts = self.existing_pin.split('-')[1:]
115        self.existing_series, self.existing_number = parts
116
117        # Add an applicant
118        self.applicant = Applicant()
119        self.pin_applicant = unicode(self.pins[1])
120        self.applicant.access_code = self.pin_applicant
121        app['applicants']['app2009'][self.pin_applicant] = self.applicant
122
123    def tearDown(self):
124        super(ApplicantsFullSetup, self).tearDown()
125        clearSite()
126        shutil.rmtree(self.dc_root)
127
128class ApplicantsRootUITests(ApplicantsFullSetup):
129    # Tests for ApplicantsRoot class
130
131    layer = FunctionalLayer
132
133    def test_anonymous_access(self):
134        # Anonymous users can access applicants root
135        self.browser.open(self.root_path)
136        self.assertEqual(self.browser.headers['Status'], '200 Ok')
137        self.assertFalse(
138            'Manage' in self.browser.contents)
139
140    def test_anonymous_no_actions(self):
141        # Make sure anonymous users cannot access actions
142        self.browser.open(self.root_path)
143        self.assertRaises(
144            LookupError, self.browser.getControl, "Add local role")
145        # Manage screen neither linked nor accessible for anonymous
146        self.assertRaises(
147            LinkNotFoundError,
148            self.browser.getLink, 'Manage application section')
149        self.assertRaises(
150            Unauthorized, self.browser.open, self.manage_root_path)
151        # Add container screen not accessible for anonymous
152        self.assertRaises(
153            Unauthorized, self.browser.open, self.add_container_path)
154        return
155
156    def test_manage_access(self):
157        # Managers can access the manage pages of applicants root
158        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
159        self.browser.open(self.root_path)
160        self.assertTrue('Manage application section' in self.browser.contents)
161        # There is a manage link
162        link = self.browser.getLink('Manage application section')
163        link.click()
164        self.assertEqual(self.browser.headers['Status'], '200 Ok')
165        self.assertEqual(self.browser.url, self.manage_root_path)
166
167    def test_manage_actions_access(self):
168        # Managers can access the action on manage screen
169        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
170        self.browser.open(self.manage_root_path)
171        self.browser.getControl("Add local role").click()
172        self.assertTrue('No user selected' in self.browser.contents)
173        return
174
175    def test_local_roles_add_delete(self):
176        # Managers can assign and delete local roles of applicants root
177        myusers = self.app['users']
178        myusers.addUser('bob', 'bobssecret')
179        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
180        self.browser.open(self.manage_root_path)
181        self.browser.getControl(name="user").value = ['bob']
182        self.browser.getControl(name="local_role").value = [
183            'waeup.ApplicationsOfficer']
184        self.browser.getControl("Add local role").click()
185        self.assertTrue('<td>bob</td>' in self.browser.contents)
186        # Remove the role assigned
187        ctrl = self.browser.getControl(name='role_id')
188        ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
189        self.browser.getControl("Remove selected local roles").click()
190        self.assertTrue('Successfully removed:' in self.browser.contents)
191        self.assertFalse('<td>bob</td>' in self.browser.contents)
192        return
193
194    def test_add_delete_container(self):
195        # Managers can add and delete applicants containers
196        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
197        self.browser.open(self.manage_root_path)
198        self.browser.getControl("Cancel").click()
199        self.assertEqual(self.browser.url, self.root_path)
200        self.browser.open(self.manage_root_path)
201        self.browser.getControl("Add applicants container").click()
202        self.assertEqual(self.browser.headers['Status'], '200 Ok')
203        self.assertEqual(self.browser.url, self.add_container_path)
204        self.browser.getControl(name="form.prefix").value = ['app']
205        self.browser.getControl("Add applicants container").click()
206        self.assertTrue(
207            'There were errors' in self.browser.contents)
208        self.browser.getControl(name="form.prefix").value = ['app']
209        self.browser.getControl(name="form.year").value = ['2010']
210        self.browser.getControl(name="form.provider").value = [
211            'waeup.sirp.applicants.ApplicantsContainer']
212        self.browser.getControl(name="form.ac_prefix").value = ['APP']
213        self.browser.getControl(name="form.application_category").value = ['basic']
214        self.browser.getControl("Add applicants container").click()
215        self.assertTrue('Added:' in self.browser.contents)
216        self.browser.open(self.add_container_path)
217        self.browser.getControl("Cancel").click()
218        self.assertEqual(self.browser.url, self.manage_root_path + '#tab-1')
219        self.browser.open(self.add_container_path)
220        self.browser.getControl(name="form.prefix").value = ['app']
221        self.browser.getControl(name="form.year").value = ['2010']
222        self.browser.getControl(name="form.provider").value = [
223            'waeup.sirp.applicants.ApplicantsContainer']
224        self.browser.getControl(name="form.ac_prefix").value = ['APP']
225        self.browser.getControl(name="form.application_category").value = ['basic']
226        self.browser.getControl("Add applicants container").click()
227        self.assertTrue('exists already in the database' in self.browser.contents)
228        self.browser.open(self.manage_root_path)
229        ctrl = self.browser.getControl(name='val_id')
230        ctrl.getControl(value='app2010').selected = True
231        self.browser.getControl("Remove selected", index=0).click()
232        self.assertTrue('Successfully removed:' in self.browser.contents)
233        self.browser.open(self.add_container_path)
234        self.browser.getControl(name="form.prefix").value = ['app']
235        self.browser.getControl(name="form.year").value = ['2010']
236        self.browser.getControl(name="form.provider").value = [
237            'waeup.sirp.applicants.ApplicantsContainer']
238        self.browser.getControl(name="form.ac_prefix").value = ['APP']
239        self.browser.getControl(name="form.application_category").value = ['basic']
240        self.browser.getControl("Add applicants container").click()
241        del self.app['applicants']['app2010']
242        ctrl = self.browser.getControl(name='val_id')
243        ctrl.getControl(value='app2010').selected = True
244        self.browser.getControl("Remove selected", index=0).click()
245        self.assertMatches('...Could not delete...', self.browser.contents)
246        return
247
248    def test_add_delete_applicants(self):
249        # Managers can add and delete applicants
250        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
251        self.add_applicant_path = self.container_path + '/addapplicant'
252        self.container_manage_path = self.container_path + '/@@manage'
253        self.browser.open(self.container_manage_path)
254        self.browser.getControl("Add applicant").click()
255        self.assertEqual(self.browser.headers['Status'], '200 Ok')
256        self.assertEqual(self.browser.url, self.add_applicant_path)
257        self.browser.getControl(name="form.ac_series").value = self.existing_series
258        self.browser.getControl(name="form.ac_number").value = self.existing_number
259        self.browser.getControl("Create application record").click()
260        self.assertTrue('Application initialized' in self.browser.contents)
261        self.browser.open(self.add_applicant_path)
262        self.browser.getControl(name="form.ac_series").value = '123'
263        self.browser.getControl(name="form.ac_number").value = '456'
264        self.browser.getControl("Create application record").click()
265        self.assertTrue('is not a valid access code' in self.browser.contents)
266        self.browser.open(self.container_manage_path)
267        self.assertEqual(self.browser.headers['Status'], '200 Ok')
268        ctrl = self.browser.getControl(name='val_id')
269        ctrl.getControl(value=self.existing_pin).selected = True
270        self.browser.getControl("Remove selected", index=0).click()
271        self.assertTrue('Successfully removed:' in self.browser.contents)
272        self.browser.open(self.add_applicant_path)
273        existing_pin = self.pins[2]
274        parts = existing_pin.split('-')[1:]
275        existing_series, existing_number = parts
276        self.browser.getControl(name="form.ac_series").value = existing_series
277        self.browser.getControl(name="form.ac_number").value = existing_number
278        self.browser.getControl("Create application record").click()
279        self.assertTrue('Application initialized' in self.browser.contents)
280        self.browser.open(self.container_manage_path)
281        self.assertTrue(existing_pin in self.browser.contents)
282        del self.app['applicants']['app2009'][existing_pin]
283        ctrl = self.browser.getControl(name='val_id')
284        ctrl.getControl(value=existing_pin).selected = True
285        self.browser.getControl("Remove selected", index=0).click()
286        self.assertMatches('...Could not delete...', self.browser.contents)
287        return
288
289    def test_manage_and_view_applicant(self):
290        # Managers can manage applicants
291        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
292        self.applicant_path = 'http://localhost/app/applicants/app2009/%s' % self.pin_applicant
293        self.applicant_view_path = self.applicant_path + '/index'
294        self.applicant_manage_path = self.applicant_path + '/edit_full'
295        self.applicant_slip_path = self.applicant_path + '/application_slip.pdf'
296        self.browser.open(self.applicant_manage_path)
297        self.assertEqual(self.browser.headers['Status'], '200 Ok')
298        self.browser.getControl(name="form.email").value = 'abc'
299        self.browser.getControl("Save").click()
300        self.assertMatches('...Invalid email address...', self.browser.contents)
301        self.browser.getControl(name="form.email").value = 'abc@def.gh'
302        self.browser.getControl(name="form.firstname").value = 'John'
303        self.browser.getControl(name="form.lastname").value = 'Tester'
304        self.browser.getControl(name="form.course1").value = ['CERT1']
305        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
306        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
307        self.browser.getControl(name="form.lga").value = ['foreigner']
308        self.browser.getControl(name="form.sex").value = ['m']
309        self.browser.getControl(name="transition").value = ['start']
310        self.browser.getControl("Save").click()
311        self.assertMatches('...Form has been saved...', self.browser.contents)
312        self.assertMatches('...Application started by zope.mgr...', self.browser.contents)
313        self.browser.open(self.applicant_view_path)
314        self.assertEqual(self.browser.headers['Status'], '200 Ok')
315        self.browser.open(self.applicant_slip_path)
316        self.assertEqual(self.browser.headers['Status'], '200 Ok')
317        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
318        self.browser.open(self.applicant_manage_path)
319        self.browser.getControl(name="form.course_admitted").value = []
320        self.browser.getControl("Save").click()
321        self.browser.open(self.applicant_slip_path)
322        self.assertEqual(self.browser.headers['Status'], '200 Ok')
323        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
324        return
325
326    def test_view_applicant(self):
327        # Applicants can login and view their application
328        self.login_path = 'http://localhost/app/applicants/app2009/login'
329        self.browser.open(self.login_path)
330        pin = self.pins[2]
331        parts = pin.split('-')[1:]
332        existing_series, existing_number = parts
333        ac_series = self.browser.getControl(name="form.ac_series")
334        ac_series.value = existing_series
335        ac_number = self.browser.getControl(name="form.ac_number")
336        ac_number.value = existing_number
337        self.browser.getControl(name="SUBMIT").click()
338        self.assertTrue(self.browser.url != self.login_path)
339        self.assertEqual(self.browser.headers['Status'], '200 Ok')
340        return
341
342    def test_passport_edit_view(self):
343        # We get a default image after login
344        login_path = 'http://localhost/app/applicants/app2009/login'
345        self.browser.open(login_path)
346        pin = self.pins[2]
347        parts = pin.split('-')[1:]
348        existing_series, existing_number = parts
349        ac_series = self.browser.getControl(name="form.ac_series")
350        ac_series.value = existing_series
351        ac_number = self.browser.getControl(name="form.ac_number")
352        ac_number.value = existing_number
353        self.browser.getControl(name="SUBMIT").click()
354        pin = self.pins[2]
355        #appl = self.getRootFolder()['app']['applicants']['app2009']
356        #appl = appl[pin]
357        #passp = appl.passport
358        #passp_len = len(passp.file.read())
359        #self.assertEqual(passp_len, PH_LEN)
360        #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg')
361        image_url = "%s/%s" % (self.browser.url, 'passport.jpg')
362        #self.browser.open(image_url)
363        self.browser.open('passport.jpg')
364        self.assertEqual(self.browser.headers['status'], '200 Ok')
365        self.assertEqual(self.browser.headers['content-type'], 'image/jpeg')
366        self.assertTrue('JFIF' in self.browser.contents)
367        self.assertEqual(
368            self.browser.headers['content-length'], str(PH_LEN))
369
370
371    def test_edit_applicant(self):
372        # Applicants can edit their record
373        self.login_path = 'http://localhost/app/applicants/app2009/login'
374        self.browser.open(self.login_path)
375        pin = self.pins[2]
376        parts = pin.split('-')[1:]
377        existing_series, existing_number = parts
378        ac_series = self.browser.getControl(name="form.ac_series")
379        ac_series.value = existing_series
380        ac_number = self.browser.getControl(name="form.ac_number")
381        ac_number.value = existing_number
382        self.browser.getControl(name="SUBMIT").click()
383        self.assertTrue(self.browser.url != self.login_path)
384        self.assertEqual(self.browser.headers['Status'], '200 Ok')
385        self.browser.getControl(name="form.firstname").value = 'John'
386        self.browser.getControl(name="form.lastname").value = 'Tester'
387        self.browser.getControl(name="form.course1").value = ['CERT1']
388        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
389        self.browser.getControl(name="form.lga").value = ['foreigner']
390        self.browser.getControl(name="form.sex").value = ['m']
391        self.browser.getControl("Save").click()
392        self.assertMatches('...Form has been saved...', self.browser.contents)
393        return
394
395class ApplicantsContainerUITests(ApplicantsFullSetup):
396    # Tests for ApplicantsContainer class views and pages
397
398    layer = FunctionalLayer
399
400    def test_anonymous_access(self):
401        # Anonymous users can access applicants containers
402        self.browser.open(self.container_path)
403        self.assertEqual(self.browser.headers['Status'], '200 Ok')
404        self.assertFalse(
405            'Manage' in self.browser.contents)
406        return
407
408    def test_manage_access(self):
409        # Managers can access the manage pages of applicants
410        # containers and can perform actions
411        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
412        self.browser.open(self.manage_container_path)
413        self.assertEqual(self.browser.headers['Status'], '200 Ok')
414        self.assertEqual(self.browser.url, self.manage_container_path)
415        self.browser.getControl("Save").click()
416        self.assertTrue('Data saved' in self.browser.contents)
417        self.browser.getControl("Remove selected", index=0).click()
418        self.assertTrue('No applicant selected' in self.browser.contents)
419        self.browser.getControl("Add local role").click()
420        self.assertTrue('No user selected' in self.browser.contents)
421        self.browser.getControl("Cancel", index=0).click()
422        self.assertEqual(self.browser.url, self.container_path)
423        return
424
425    def test_local_roles_add_delete(self):
426        # Managers can assign and delete local roles of applicants containers
427        myusers = self.app['users']
428        myusers.addUser('bob', 'bobssecret')
429        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
430        self.browser.open(self.manage_container_path)
431        self.browser.getControl(name="user").value = ['bob']
432        self.browser.getControl(name="local_role").value = [
433            'waeup.ApplicationsOfficer']
434        self.browser.getControl("Add local role").click()
435        self.assertTrue('<td>bob</td>' in self.browser.contents)
436        ctrl = self.browser.getControl(name='role_id')
437        ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
438        self.browser.getControl("Remove selected local roles").click()
439        self.assertTrue('Successfully removed:' in self.browser.contents)
440        self.assertFalse('<td>bob</td>' in self.browser.contents)
441        return
442
443class LoginTest(FunctionalTestCase):
444    # Here we check login view of applicants containers.
445    #
446    # Tests in here do only cover login attempts without any PINs
447    # created before.
448
449    layer = FunctionalLayer
450
451    def setUp(self):
452        super(LoginTest, self).setUp()
453
454        # Setup a sample site for each test
455        app = University()
456        self.dc_root = tempfile.mkdtemp()
457        app['datacenter'].setStoragePath(self.dc_root)
458        self.login_path = 'http://localhost/app/applicants/testapplicants/login'
459
460        # Add an applicants container where we can login (or not)
461        applicantscontainer = ApplicantsContainer()
462        applicantscontainer.ac_prefix = 'APP'
463        delta = timedelta(days=10)
464        applicantscontainer.startdate = date.today() - delta
465        applicantscontainer.enddate = date.today() + delta
466        app['applicants']['testapplicants'] = applicantscontainer
467
468        # Put the prepopulated site into test ZODB and prepare test
469        # browser
470        self.getRootFolder()['app'] = app
471        self.browser = Browser()
472        self.browser.handleErrors = False
473
474    def tearDown(self):
475        super(LoginTest, self).tearDown()
476        shutil.rmtree(self.dc_root)
477
478    def test_anonymous_access(self):
479        # Anonymous users can access a login page
480        self.browser.open(self.login_path)
481        self.assertEqual(self.browser.headers['Status'], '200 Ok')
482        return
483
484    def test_anonymous_invalid_creds(self):
485        # Anonymous users giving invalid credentials stay at the page
486        self.browser.open(self.login_path)
487        # We do not give credentials but send the form as-is
488        submit = self.browser.getControl(name='SUBMIT')
489        submit.click()
490        # We are still at the same page...
491        self.assertEqual(self.browser.url, self.login_path)
492        self.assertEqual(self.browser.headers['Status'], '200 Ok')
493        return
494
495    def test_anonymous_invalid_creds_warning(self):
496        # Entering wrong credentials will yield a warning
497        self.browser.open(self.login_path)
498        # We do not give credentials but send the form as-is
499        submit = self.browser.getControl(name='SUBMIT')
500        submit.click()
501        self.assertTrue(
502            'Entered credentials are invalid' in self.browser.contents)
503        return
504
505    def test_manager_no_warnings(self):
506        # Browsing the login screen as a manager, won't raise warnings
507        # Authenticate ourself as manager
508        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
509        self.browser.open(self.login_path)
510        # Submit the form w/o any credentials
511        self.browser.getControl(name="SUBMIT").click()
512        self.assertTrue(
513            'Entered credentials are invalid' not in self.browser.contents)
514        return
515
516    def test_manager_no_redirect(self):
517        # Browsing the login screen as a manager won't trigger a redirect
518        # Authenticate ourself as manager
519        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
520        self.browser.open(self.login_path)
521        # Submit the form w/o any credentials
522        self.browser.getControl(name="SUBMIT").click()
523        self.assertEqual(self.browser.url, self.login_path)
524        return
525
526    def test_display_entered_values(self):
527        # After submit the entered values are displayed in the form
528        self.browser.open(self.login_path)
529        # Enter some value we can look for after submit
530        ac_series = self.browser.getControl(name="form.ac_series")
531        ac_series.value = '666'
532        self.browser.getControl(name="SUBMIT").click()
533        self.assertTrue('666' in self.browser.contents)
534        return
535
536class LoginTestWithPINs(LoginTest):
537    # Here we check login view of applicants containers with PINs provided.
538
539    # As setting up pins is time-consuming we only set them up when
540    # really needed (i.e. in this separate TestCase).
541
542    layer = FunctionalLayer
543
544    def setUp(self):
545        super(LoginTestWithPINs, self).setUp()
546
547        # Create 5 access codes with prefix'FOO' and cost 9.99 each
548        pin_container = self.getRootFolder()['app']['accesscodes']
549        pin_container.createBatch(
550            datetime.now(), 'some_userid', 'APP', 9.99, 5)
551        pins = pin_container[pin_container.keys()[0]].values()
552        self.pins = [x.representation for x in pins]
553        self.existing_pin = self.pins[0]
554        parts = self.existing_pin.split('-')[1:]
555        self.existing_series, self.existing_number = parts
556        self.browser.handleErrors = False
557
558    def tearDown(self):
559        super(LoginTestWithPINs, self).tearDown()
560
561    def test_anonymous_valid_login(self):
562        # If we enter valid credentials, we get to the applicants form
563        self.browser.open(self.login_path)
564        # Enter some value we can look for after submit
565        ac_series = self.browser.getControl(name="form.ac_series")
566        ac_series.value = self.existing_series
567        ac_number = self.browser.getControl(name="form.ac_number")
568        ac_number.value = self.existing_number
569        self.browser.getControl(name="SUBMIT").click()
570        # We should be redirected to applicants form.
571        self.assertTrue(self.browser.url != self.login_path)
572        # Applicants see their Access Code in the contact form
573        self.browser.getLink("Contact").click()
574        self.assertTrue(
575            'Access Code:' in self.browser.contents)
576        return
577
578    def test_anonymous_invalid_login(self):
579        # If we enter wrong credentials we won't get far
580        self.browser.open(self.login_path)
581        # Enter some value we can look for after submit
582        ac_series = self.browser.getControl(name="form.ac_series")
583        ac_series.value = 'illegal series'
584        ac_number = self.browser.getControl(name="form.ac_number")
585        ac_number.value = 'invalid number'
586        self.browser.getControl(name="SUBMIT").click()
587        # We get a warning message
588        self.assertTrue(
589            'Entered credentials are invalid' in self.browser.contents)
590        # We stay at the login page (no redirect)
591        self.assertTrue(self.browser.url == self.login_path)
592        return
593
594class ApplicantsPassportTests(ApplicantsFullSetup):
595    # Tests for uploading/browsing the passport image of appplicants
596
597    layer = FunctionalLayer
598
599    def setUp(self):
600        super(ApplicantsPassportTests, self).setUp()
601        self.login_path = 'http://localhost/app/applicants/app2009/login'
602        self.pin = self.pins[2]
603        self.existing_series, self.existing_number = self.pin.split('-')[1:]
604        self.edit_path = 'http://localhost/app/applicants/app2009/%s/edit' % (
605            self.pin)
606        self.edit_full_path = 'http://localhost/app/applicants/%s/%s/%s' % (
607            'app2009', self.pin, 'edit_full')
608
609    def tearDown(self):
610        super(ApplicantsPassportTests, self).tearDown()
611
612    def login(self):
613        # Perform an applicant login. This creates an applicant record.
614        #
615        # This helper also sets `self.applicant`, which is the
616        # applicant object created.
617        self.browser.open(self.login_path)
618        ac_series = self.browser.getControl(name="form.ac_series")
619        ac_series.value = self.existing_series
620        ac_number = self.browser.getControl(name="form.ac_number")
621        ac_number.value = self.existing_number
622        self.browser.getControl(name="SUBMIT").click()
623        self.applicant = self.app['applicants']['app2009'][self.pin]
624
625    def fill_correct_values(self):
626        # Fill the edit form with suitable values
627        self.browser.getControl(name="form.firstname").value = 'John'
628        self.browser.getControl(name="form.lastname").value = 'Tester'
629        self.browser.getControl(name="form.course1").value = ['CERT1']
630        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
631        self.browser.getControl(name="form.lga").value = ['foreigner']
632        self.browser.getControl(name="form.sex").value = ['m']
633
634    def image_url(self, filename):
635        return self.edit_path.replace('edit', filename)
636
637    def test_after_login_default_browsable(self):
638        # After login we see the placeholder image in the edit view
639        #import pdb; pdb.set_trace()
640        self.login()
641        self.assertEqual(self.browser.url, self.edit_path)
642        # There is a correct <img> link included
643        self.assertTrue(
644            '<img src="passport.jpg" />' in self.browser.contents)
645        # Browsing the link shows a real image
646        self.browser.open(self.image_url('passport.jpg'))
647        self.assertEqual(
648            self.browser.headers['content-type'], 'image/jpeg')
649        self.assertEqual(len(self.browser.contents), PH_LEN)
650
651    def test_after_submit_default_browsable(self):
652        # After submitting an applicant form the default image is
653        # still visible
654        self.login()
655        self.browser.getControl("Save").click() # submit form
656        # There is a correct <img> link included
657        self.assertTrue(
658            '<img src="passport.jpg" />' in self.browser.contents)
659        # Browsing the link shows a real image
660        self.browser.open(self.image_url('passport.jpg'))
661        self.assertEqual(
662            self.browser.headers['content-type'], 'image/jpeg')
663        self.assertEqual(len(self.browser.contents), PH_LEN)
664
665    def test_uploaded_image_respects_file_size_restriction(self):
666        # When we upload an image that is too big ( > 10 KB) we will
667        # get an error message
668        self.login()
669        # Create a pseudo image file and select it to be uploaded in form
670        photo_content = 'A' * 1024 * 21  # A string of 21 KB size
671        pseudo_image = StringIO(photo_content)
672        ctrl = self.browser.getControl(name='form.passport')
673        file_ctrl = ctrl.mech_control
674        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
675        self.browser.getControl("Save").click() # submit form
676        # There is a correct <img> link included
677        self.assertTrue(
678            '<img src="passport.jpg" />' in self.browser.contents)
679        # We get a warning message
680        self.assertTrue(
681            'Uploaded image is too big' in self.browser.contents)
682        # Browsing the image gives us the default image, not the
683        # uploaded one.
684        self.browser.open(self.image_url('passport.jpg'))
685        self.assertEqual(
686            self.browser.headers['content-type'], 'image/jpeg')
687        self.assertEqual(len(self.browser.contents), PH_LEN)
688        # There is really no file stored for the applicant
689        img = getUtility(IExtFileStore).getFile(
690            IFileStoreNameChooser(self.applicant).chooseName())
691        self.assertTrue(img is None)
692
693    def test_uploaded_image_browsable_w_errors(self):
694        # We can upload a different image and browse it after submit,
695        # even if there are still errors in the form
696        self.login()
697        # Create a pseudo image file and select it to be uploaded in form
698        photo_content = 'I pretend to be a graphics file'
699        pseudo_image = StringIO(photo_content)
700        ctrl = self.browser.getControl(name='form.passport')
701        file_ctrl = ctrl.mech_control
702        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
703        self.browser.getControl("Save").click() # submit form
704        # There is a correct <img> link included
705        self.assertTrue(
706            '<img src="passport.jpg" />' in self.browser.contents)
707        # Browsing the link shows a real image
708        self.browser.open(self.image_url('passport.jpg'))
709        self.assertEqual(
710            self.browser.headers['content-type'], 'image/jpeg')
711        self.assertEqual(self.browser.contents, photo_content)
712
713    def test_uploaded_image_stored_in_imagestorage_w_errors(self):
714        # After uploading a new passport pic the file is correctly
715        # stored in an imagestorage
716        self.login()
717        # Create a pseudo image file and select it to be uploaded in form
718        pseudo_image = StringIO('I pretend to be a graphics file')
719        ctrl = self.browser.getControl(name='form.passport')
720        file_ctrl = ctrl.mech_control
721        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
722        self.browser.getControl("Save").click() # submit form
723        storage = getUtility(IExtFileStore)
724        file_id = IFileStoreNameChooser(self.applicant).chooseName()
725        pseudo_image.seek(0) # reset our file data source
726        self.assertEqual(
727            storage.getFile(file_id).read(), pseudo_image.read())
728        return
729
730    def test_uploaded_image_browsable_wo_errors(self):
731        # We can upload a different image and browse it after submit,
732        # if there are no errors in form
733        self.login()
734        self.fill_correct_values() # fill other fields with correct values
735        # Create a pseudo image file and select it to be uploaded in form
736        pseudo_image = StringIO('I pretend to be a graphics file')
737        ctrl = self.browser.getControl(name='form.passport')
738        file_ctrl = ctrl.mech_control
739        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
740        self.browser.getControl("Save").click() # submit form
741        # There is a correct <img> link included
742        self.assertTrue(
743            '<img src="passport.jpg" />' in self.browser.contents)
744        # Browsing the link shows a real image
745        self.browser.open(self.image_url('passport.jpg'))
746        self.assertEqual(
747            self.browser.headers['content-type'], 'image/jpeg')
748        self.assertEqual(len(self.browser.contents), 31)
749
750    def test_uploaded_image_stored_in_imagestorage_wo_errors(self):
751        # After uploading a new passport pic the file is correctly
752        # stored in an imagestorage if form contains no errors
753        self.login()
754        self.fill_correct_values() # fill other fields with correct values
755        # Create a pseudo image file and select it to be uploaded in form
756        pseudo_image = StringIO('I pretend to be a graphics file')
757        ctrl = self.browser.getControl(name='form.passport')
758        file_ctrl = ctrl.mech_control
759        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
760        self.browser.getControl("Save").click() # submit form
761        storage = getUtility(IExtFileStore)
762        file_id = IFileStoreNameChooser(self.applicant).chooseName()
763        # The stored image can be fetched
764        fd = storage.getFile(file_id)
765        file_len = len(fd.read())
766        self.assertEqual(file_len, 31)
767
768    def test_uploaded_images_equal(self):
769        # Make sure uploaded images do really differ if we eject a
770        # change notfication (and do not if we don't)
771        self.login() # Create applicant form
772        self.fill_correct_values() # fill other fields with correct values
773        self.browser.getControl("Save").click() # submit form
774        # Now go on as an officer
775        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
776        self.browser.open(self.edit_full_path)
777
778        # Create a pseudo image file and select it to be uploaded in form
779        pseudo_image = StringIO('I pretend to be a graphics file')
780        ctrl = self.browser.getControl(name='form.passport')
781        file_ctrl = ctrl.mech_control
782        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
783        file_id = IFileStoreNameChooser(self.applicant).chooseName()
784        setSite(self.app)
785        passport0 = getUtility(IExtFileStore).getFile(file_id)
786        self.browser.getControl("Save").click() # submit form with changed pic
787        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
788        self.browser.getControl("Save").click() # submit form w/o changes
789        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
790        self.assertTrue(passport0 is None)
791        self.assertTrue(passport0 != passport1)
792        self.assertTrue(passport1 == passport2)
793        return
794
795    def test_final_submit(self):
796        # Make sure that a correctly filled form with passport picture
797        # can be submitted
798        self.login() # Create applicant form
799        self.fill_correct_values() # fill other fields with correct values
800        # Create a pseudo image file and select it to be uploaded in form
801        pseudo_image = StringIO('I pretend to be a graphics file')
802        ctrl = self.browser.getControl(name='form.passport')
803        file_ctrl = ctrl.mech_control
804        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
805        self.browser.getControl("Final Submit").click() # (finally) submit form
806        self.assertEqual(self.browser.headers['Status'], '200 Ok')
807        self.assertTrue(
808            'Passport confirmation box not ticked' in self.browser.contents)
809        self.browser.getControl(name="confirm_passport").value = True
810        IWorkflowInfo(self.applicant).fireTransition('submit')
811        self.browser.getControl("Final Submit").click() # submit form again
812        self.assertTrue(
813            'Wrong state' in self.browser.contents)
814        IWorkflowInfo(self.applicant).fireTransition('reset1')
815        # Now do the whole thing again but with correct state
816        self.login()
817        self.fill_correct_values()
818        pseudo_image = StringIO('I pretend to be a graphics file')
819        ctrl = self.browser.getControl(name='form.passport')
820        file_ctrl = ctrl.mech_control
821        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
822        self.browser.getControl(name="confirm_passport").value = True
823        self.browser.getControl("Final Submit").click()
824        self.assertTrue(
825            '... submitted ...' in self.browser.contents)
826        self.browser.goBack(count=1)
827        self.browser.getControl("Save").click()
828        self.assertTrue(
829            'The requested form is locked' in self.browser.contents)
830        self.browser.goBack(count=1)
831        #import pdb; pdb.set_trace()
832        self.browser.getControl("Final Submit").click()
833        self.assertTrue(
834            'The requested form is locked' in self.browser.contents)
835        return
836
837    def test_locking(self):
838        # Make sure that locked forms can't be submitted
839        self.login() # Create applicant form
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        self.browser.getLink("Logout").click()
848
849        # Login as manager and lock the form
850        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
851        self.browser.open(self.edit_full_path)
852        self.browser.getControl(name="form.locked").value = True
853        self.browser.getControl("Save").click()
854        self.browser.getLink("Logout").click()
855
856        # Login as applicant again and try to open the edit form
857        self.login()
858        self.browser.open(self.edit_path)
859        self.assertEqual(self.browser.headers['Status'], '200 Ok')
860        #print self.browser.contents
861        self.assertTrue(
862            'The requested form is locked' in self.browser.contents)
863        return
Note: See TracBrowser for help on using the repository browser.