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

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

Add test: If an applicant is removed also the passport image of this applicant is removed.

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