source: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py @ 7887

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

Remove settings not needed.

  • Property svn:keywords set to Id
File size: 84.8 KB
Line 
1## $Id: test_browser.py 7887 2012-03-14 21:39:27Z 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 student-related UI components.
20"""
21import shutil
22import tempfile
23from StringIO import StringIO
24from datetime import datetime
25import os
26import grok
27from zope.event import notify
28from zope.component import createObject, queryUtility
29from zope.component.hooks import setSite, clearSite
30from zope.catalog.interfaces import ICatalog
31from zope.security.interfaces import Unauthorized
32from zope.securitypolicy.interfaces import IPrincipalRoleManager
33from zope.testbrowser.testing import Browser
34from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
35from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
36from waeup.kofa.app import University
37from waeup.kofa.configuration import SessionConfiguration
38from waeup.kofa.students.student import Student
39from waeup.kofa.students.studylevel import StudentStudyLevel
40from waeup.kofa.university.faculty import Faculty
41from waeup.kofa.university.department import Department
42from waeup.kofa.interfaces import IUserAccount
43from waeup.kofa.authentication import LocalRoleSetEvent
44from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
45
46PH_LEN = 2059  # Length of placeholder file
47
48def lookup_submit_value(name, value, browser):
49    """Find a button with a certain value."""
50    for num in range(0, 100):
51        try:
52            button = browser.getControl(name=name, index=num)
53            if button.value.endswith(value):
54                return button
55        except IndexError:
56            break
57    return None
58
59class StudentsFullSetup(FunctionalTestCase):
60    # A test case that only contains a setup and teardown
61    #
62    # Complete setup for students handlings is rather complex and
63    # requires lots of things created before we can start. This is a
64    # setup that does all this, creates a university, creates PINs,
65    # etc.  so that we do not have to bother with that in different
66    # test cases.
67
68    layer = FunctionalLayer
69
70    def setUp(self):
71        super(StudentsFullSetup, self).setUp()
72
73        # Setup a sample site for each test
74        app = University()
75        self.dc_root = tempfile.mkdtemp()
76        app['datacenter'].setStoragePath(self.dc_root)
77
78        # Prepopulate the ZODB...
79        self.getRootFolder()['app'] = app
80        # we add the site immediately after creation to the
81        # ZODB. Catalogs and other local utilities are not setup
82        # before that step.
83        self.app = self.getRootFolder()['app']
84        # Set site here. Some of the following setup code might need
85        # to access grok.getSite() and should get our new app then
86        setSite(app)
87
88        # Add student with subobjects
89        student = Student()
90        student.firstname = u'Anna'
91        student.lastname = u'Tester'
92        student.reg_number = u'123'
93        student.matric_number = u'234'
94        student.sex = u'm'
95        student.email = 'aa@aa.ng'
96        student.phone = u'1234'
97        self.app['students'].addStudent(student)
98        self.student_id = student.student_id
99        self.student = self.app['students'][self.student_id]
100
101        # Set password
102        IUserAccount(
103            self.app['students'][self.student_id]).setPassword('spwd')
104
105        self.login_path = 'http://localhost/app/login'
106        self.container_path = 'http://localhost/app/students'
107        self.manage_container_path = self.container_path + '/@@manage'
108        self.add_student_path = self.container_path + '/addstudent'
109        self.student_path = self.container_path + '/' + self.student_id
110        self.manage_student_path = self.student_path + '/manage_base'
111        self.clearance_student_path = self.student_path + '/view_clearance'
112        self.personal_student_path = self.student_path + '/view_personal'
113        self.edit_clearance_student_path = self.student_path + '/edit_clearance'
114        self.edit_personal_student_path = self.student_path + '/edit_personal'
115        self.studycourse_student_path = self.student_path + '/studycourse'
116        self.payments_student_path = self.student_path + '/payments'
117        self.acco_student_path = self.student_path + '/accommodation'
118        self.history_student_path = self.student_path + '/history'
119
120        # Create 5 access codes with prefix'PWD'
121        pin_container = self.app['accesscodes']
122        pin_container.createBatch(
123            datetime.now(), 'some_userid', 'PWD', 9.99, 5)
124        pins = pin_container['PWD-1'].values()
125        self.pwdpins = [x.representation for x in pins]
126        self.existing_pwdpin = self.pwdpins[0]
127        parts = self.existing_pwdpin.split('-')[1:]
128        self.existing_pwdseries, self.existing_pwdnumber = parts
129        # Create 5 access codes with prefix 'CLR'
130        pin_container.createBatch(
131            datetime.now(), 'some_userid', 'CLR', 9.99, 5)
132        pins = pin_container['CLR-1'].values()
133        pins[0].owner = u'Hans Wurst'
134        self.existing_clrac = pins[0]
135        self.existing_clrpin = pins[0].representation
136        parts = self.existing_clrpin.split('-')[1:]
137        self.existing_clrseries, self.existing_clrnumber = parts
138        # Create 2 access codes with prefix 'HOS'
139        pin_container.createBatch(
140            datetime.now(), 'some_userid', 'HOS', 9.99, 2)
141        pins = pin_container['HOS-1'].values()
142        self.existing_hosac = pins[0]
143        self.existing_hospin = pins[0].representation
144        parts = self.existing_hospin.split('-')[1:]
145        self.existing_hosseries, self.existing_hosnumber = parts
146
147        # Populate university
148        self.certificate = createObject('waeup.Certificate')
149        self.certificate.code = u'CERT1'
150        self.certificate.application_category = 'basic'
151        self.certificate.study_mode = 'ug_ft'
152        self.certificate.start_level = 100
153        self.certificate.end_level = 500
154        self.app['faculties']['fac1'] = Faculty()
155        self.app['faculties']['fac1']['dep1'] = Department(code='dep1')
156        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
157            self.certificate)
158        self.course = createObject('waeup.Course')
159        self.course.code = 'COURSE1'
160        self.course.semester = 1
161        self.course.credits = 10
162        self.course.passmark = 40
163        self.app['faculties']['fac1']['dep1'].courses.addCourse(
164            self.course)
165        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCourseRef(
166            self.course, level=100)
167
168        # Configure university
169        self.app['configuration'].accommodation_states = ['admitted']
170        self.app['configuration'].accommodation_session = 2004
171        self.app['configuration'].carry_over = True
172        configuration = SessionConfiguration()
173        configuration.academic_session = 2004
174        self.app['configuration'].addSessionConfiguration(configuration)
175
176        # Create a hostel with two beds
177        hostel = Hostel()
178        hostel.hostel_id = u'hall-1'
179        hostel.hostel_name = u'Hall 1'
180        self.app['hostels'].addHostel(hostel)
181        bed = Bed()
182        bed.bed_id = u'hall-1_A_101_A'
183        bed.bed_number = 1
184        bed.owner = NOT_OCCUPIED
185        bed.bed_type = u'regular_male_fr'
186        self.app['hostels'][hostel.hostel_id].addBed(bed)
187        bed = Bed()
188        bed.bed_id = u'hall-1_A_101_B'
189        bed.bed_number = 2
190        bed.owner = NOT_OCCUPIED
191        bed.bed_type = u'regular_female_fr'
192        self.app['hostels'][hostel.hostel_id].addBed(bed)
193
194        # Set study course attributes of test student
195        self.student['studycourse'].certificate = self.certificate
196        self.student['studycourse'].current_session = 2004
197        self.student['studycourse'].entry_session = 2004
198        self.student['studycourse'].current_verdict = 'A'
199        self.student['studycourse'].current_level = 100
200        # Update the catalog
201        notify(grok.ObjectModifiedEvent(self.student))
202
203        # Put the prepopulated site into test ZODB and prepare test
204        # browser
205        self.browser = Browser()
206        self.browser.handleErrors = False
207
208    def tearDown(self):
209        super(StudentsFullSetup, self).tearDown()
210        clearSite()
211        shutil.rmtree(self.dc_root)
212
213
214
215class StudentsContainerUITests(StudentsFullSetup):
216    # Tests for StudentsContainer class views and pages
217
218    layer = FunctionalLayer
219
220    def test_anonymous_access(self):
221        # Anonymous users can't access students containers
222        self.assertRaises(
223            Unauthorized, self.browser.open, self.container_path)
224        self.assertRaises(
225            Unauthorized, self.browser.open, self.manage_container_path)
226        return
227
228    def test_manage_access(self):
229        # Managers can access the view page of students
230        # containers and can perform actions
231        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
232        self.browser.open(self.container_path)
233        self.assertEqual(self.browser.headers['Status'], '200 Ok')
234        self.assertEqual(self.browser.url, self.container_path)
235        self.browser.getLink("Manage student section").click()
236        self.assertEqual(self.browser.headers['Status'], '200 Ok')
237        self.assertEqual(self.browser.url, self.manage_container_path)
238        return
239
240    def test_add_search_delete_students(self):
241        # Managers can add search and remove students
242        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
243        self.browser.open(self.manage_container_path)
244        self.browser.getLink("Add student").click()
245        self.assertEqual(self.browser.headers['Status'], '200 Ok')
246        self.assertEqual(self.browser.url, self.add_student_path)
247        self.browser.getControl(name="form.firstname").value = 'Bob'
248        self.browser.getControl(name="form.lastname").value = 'Tester'
249        self.browser.getControl(name="form.reg_number").value = '1234'
250        self.browser.getControl("Create student record").click()
251        self.assertTrue('Student record created' in self.browser.contents)
252
253        # Registration and matric numbers must be unique
254        self.browser.getLink("Manage").click()
255        self.browser.getControl(name="form.reg_number").value = '123'
256        self.browser.getControl("Save").click()
257        self.assertMatches('...Registration number exists...',
258                           self.browser.contents)
259        self.browser.getControl(name="form.reg_number").value = '789'
260        self.browser.getControl(name="form.matric_number").value = '234'
261        self.browser.getControl("Save").click()
262        self.assertMatches('...Matriculation number exists...',
263                           self.browser.contents)
264
265        # We can find a student with a certain student_id
266        self.browser.open(self.container_path)
267        self.browser.getControl("Search").click()
268        self.assertTrue('Empty search string' in self.browser.contents)
269        self.browser.getControl(name="searchtype").value = ['student_id']
270        self.browser.getControl(name="searchterm").value = self.student_id
271        self.browser.getControl("Search").click()
272        self.assertTrue('Anna Tester' in self.browser.contents)
273
274        # We can find a student in a certain session
275        self.browser.open(self.container_path)
276        self.browser.getControl(name="searchtype").value = ['current_session']
277        self.browser.getControl(name="searchterm").value = '2004'
278        self.browser.getControl("Search").click()
279        self.assertTrue('Anna Tester' in self.browser.contents)
280
281        # We can find a student in a certain study_mode
282        self.browser.open(self.container_path)
283        self.browser.getControl(name="searchtype").value = ['current_mode']
284        self.browser.getControl(name="searchterm").value = 'ug_ft'
285        self.browser.getControl("Search").click()
286        self.assertTrue('Anna Tester' in self.browser.contents)
287
288        # We can find a student in a certain department
289        self.browser.open(self.container_path)
290        self.browser.getControl(name="searchtype").value = ['depcode']
291        self.browser.getControl(name="searchterm").value = 'dep1'
292        self.browser.getControl("Search").click()
293        self.assertTrue('Anna Tester' in self.browser.contents)
294
295        # We can find a student by searching for all kind of name parts
296        self.browser.open(self.manage_container_path)
297        self.browser.getControl("Search").click()
298        self.assertTrue('Empty search string' in self.browser.contents)
299        self.browser.getControl(name="searchtype").value = ['fullname']
300        self.browser.getControl(name="searchterm").value = 'Anna Tester'
301        self.browser.getControl("Search").click()
302        self.assertTrue('Anna Tester' in self.browser.contents)
303        self.browser.open(self.manage_container_path)
304        self.browser.getControl(name="searchtype").value = ['fullname']
305        self.browser.getControl(name="searchterm").value = 'Anna'
306        self.browser.getControl("Search").click()
307        self.assertTrue('Anna Tester' in self.browser.contents)
308        self.browser.open(self.manage_container_path)
309        self.browser.getControl(name="searchtype").value = ['fullname']
310        self.browser.getControl(name="searchterm").value = 'Tester'
311        self.browser.getControl("Search").click()
312        self.assertTrue('Anna Tester' in self.browser.contents)
313        self.browser.open(self.manage_container_path)
314        self.browser.getControl(name="searchtype").value = ['fullname']
315        self.browser.getControl(name="searchterm").value = 'An'
316        self.browser.getControl("Search").click()
317        self.assertFalse('Anna Tester' in self.browser.contents)
318        self.browser.open(self.manage_container_path)
319        self.browser.getControl(name="searchtype").value = ['fullname']
320        self.browser.getControl(name="searchterm").value = 'An*'
321        self.browser.getControl("Search").click()
322        self.assertTrue('Anna Tester' in self.browser.contents)
323        self.browser.open(self.manage_container_path)
324        self.browser.getControl(name="searchtype").value = ['fullname']
325        self.browser.getControl(name="searchterm").value = 'tester'
326        self.browser.getControl("Search").click()
327        self.assertTrue('Anna Tester' in self.browser.contents)
328        self.browser.open(self.manage_container_path)
329        self.browser.getControl(name="searchtype").value = ['fullname']
330        self.browser.getControl(name="searchterm").value = 'Tester Ana'
331        self.browser.getControl("Search").click()
332        self.assertFalse('Anna Tester' in self.browser.contents)
333        self.browser.open(self.manage_container_path)
334        self.browser.getControl(name="searchtype").value = ['fullname']
335        self.browser.getControl(name="searchterm").value = 'Tester Anna'
336        self.browser.getControl("Search").click()
337        self.assertTrue('Anna Tester' in self.browser.contents)
338        # The old searchterm will be used again
339        self.browser.getControl("Search").click()
340        self.assertTrue('Anna Tester' in self.browser.contents)
341
342        ctrl = self.browser.getControl(name='entries')
343        ctrl.getControl(value=self.student_id).selected = True
344        self.browser.getControl("Remove selected", index=0).click()
345        self.assertTrue('Successfully removed' in self.browser.contents)
346        self.browser.getControl(name="searchtype").value = ['student_id']
347        self.browser.getControl(name="searchterm").value = self.student_id
348        self.browser.getControl("Search").click()
349        self.assertTrue('No student found' in self.browser.contents)
350
351        self.browser.open(self.container_path)
352        self.browser.getControl(name="searchtype").value = ['student_id']
353        self.browser.getControl(name="searchterm").value = self.student_id
354        self.browser.getControl("Search").click()
355        self.assertTrue('No student found' in self.browser.contents)
356        return
357
358class StudentUITests(StudentsFullSetup):
359    # Tests for Student class views and pages
360
361    layer = FunctionalLayer
362
363    def test_basic_auth(self):
364        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
365        self.browser.open('http://localhost/app')
366        self.browser.getLink("Logout").click()
367        self.assertTrue('You have been logged out' in self.browser.contents)
368        # But we are still logged in since we've used basic authentication here.
369        # Wikipedia says: Existing browsers retain authentication information
370        # until the tab or browser is closed or the user clears the history.
371        # HTTP does not provide a method for a server to direct clients to
372        # discard these cached credentials. This means that there is no
373        # effective way for a server to "log out" the user without closing
374        # the browser. This is a significant defect that requires browser
375        # manufacturers to support a "logout" user interface element ...
376        self.assertTrue('Manager' in self.browser.contents)
377
378    def test_manage_access(self):
379        # Managers can access the pages of students
380        # and can perform actions
381        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
382        self.browser.open(self.student_path)
383        self.assertEqual(self.browser.headers['Status'], '200 Ok')
384        self.assertEqual(self.browser.url, self.student_path)
385        self.browser.getLink("Manage").click()
386        self.assertEqual(self.browser.headers['Status'], '200 Ok')
387        self.assertEqual(self.browser.url, self.manage_student_path)
388        # Managers can edit base data and fire transitions
389        self.browser.getControl(name="transition").value = ['admit']
390        self.browser.getControl(name="form.firstname").value = 'John'
391        self.browser.getControl(name="form.lastname").value = 'Tester'
392        self.browser.getControl(name="form.reg_number").value = '345'
393        self.browser.getControl(name="password").value = 'secret'
394        self.browser.getControl(name="control_password").value = 'secret'
395        self.browser.getControl("Save").click()
396        self.assertMatches('...Form has been saved...',
397                           self.browser.contents)
398        self.browser.open(self.student_path)
399        self.browser.getLink("Clearance Data").click()
400        self.assertEqual(self.browser.headers['Status'], '200 Ok')
401        self.assertEqual(self.browser.url, self.clearance_student_path)
402        self.browser.getLink("Manage").click()
403        self.assertEqual(self.browser.headers['Status'], '200 Ok')
404        self.assertEqual(self.browser.url, self.edit_clearance_student_path)
405        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
406        self.browser.getControl("Save").click()
407        self.assertMatches('...Form has been saved...',
408                           self.browser.contents)
409
410        self.browser.open(self.student_path)
411        self.browser.getLink("Personal Data").click()
412        self.assertEqual(self.browser.headers['Status'], '200 Ok')
413        self.assertEqual(self.browser.url, self.personal_student_path)
414        self.browser.getLink("Edit").click()
415        self.assertEqual(self.browser.headers['Status'], '200 Ok')
416        self.assertEqual(self.browser.url, self.edit_personal_student_path)
417        self.browser.getControl("Save").click()
418        self.assertMatches('...Form has been saved...',
419                           self.browser.contents)
420
421        # Managers can browse all subobjects
422        self.browser.open(self.student_path)
423        self.browser.getLink("Payments").click()
424        self.assertEqual(self.browser.headers['Status'], '200 Ok')
425        self.assertEqual(self.browser.url, self.payments_student_path)
426        self.browser.open(self.student_path)
427        self.browser.getLink("Accommodation").click()
428        self.assertEqual(self.browser.headers['Status'], '200 Ok')
429        self.assertEqual(self.browser.url, self.acco_student_path)
430        self.browser.open(self.student_path)
431        self.browser.getLink("History").click()
432        self.assertEqual(self.browser.headers['Status'], '200 Ok')
433        self.assertEqual(self.browser.url, self.history_student_path)
434        self.assertMatches('...Student admitted by Manager...',
435                           self.browser.contents)
436        # Only the Application Slip does not exist
437        self.assertFalse('Application Slip' in self.browser.contents)
438        return
439
440    def test_manage_contact_student(self):
441        # Managers can contact student
442        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
443        self.browser.open(self.student_path)
444        self.browser.getLink("Send email").click()
445        self.browser.getControl(name="form.subject").value = 'Important subject'
446        self.browser.getControl(name="form.body").value = 'Hello!'
447        self.browser.getControl("Send message now").click()
448        self.assertTrue('Your message has been sent' in self.browser.contents)
449        return
450
451    def test_manage_remove_department(self):
452        # Lazy student is studying CERT1
453        lazystudent = Student()
454        lazystudent.firstname = u'Lazy'
455        lazystudent.lastname = u'Student'
456        self.app['students'].addStudent(lazystudent)
457        student_id = lazystudent.student_id
458        student_path = self.container_path + '/' + student_id
459        lazystudent['studycourse'].certificate = self.certificate
460        notify(grok.ObjectModifiedEvent(lazystudent))
461        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
462        self.browser.open(student_path + '/studycourse')
463        self.assertTrue('CERT1' in self.browser.contents)
464        # After some years the department is removed
465        del self.app['faculties']['fac1']['dep1']
466        # So CERT1 does no longer exist and lazy student's
467        # certificate reference is removed too
468        self.browser.open(student_path + '/studycourse')
469        self.assertEqual(self.browser.headers['Status'], '200 Ok')
470        self.assertEqual(self.browser.url, student_path + '/studycourse')
471        self.assertFalse('CERT1' in self.browser.contents)
472        self.assertMatches('...<div>Nothing</div>...',
473                           self.browser.contents)
474
475    def test_manage_upload_file(self):
476        # Managers can upload a file via the StudentClearanceManageFormPage
477        # The image is stored even if form has errors
478        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
479        self.browser.open(self.edit_clearance_student_path)
480        # No birth certificate has been uploaded yet
481        # Browsing the link shows a placerholder image
482        self.browser.open('birth_certificate')
483        self.assertEqual(
484            self.browser.headers['content-type'], 'image/jpeg')
485        self.assertEqual(len(self.browser.contents), PH_LEN)
486        # Create a pseudo image file and select it to be uploaded in form
487        # as birth certificate
488        self.browser.open(self.edit_clearance_student_path)
489        pseudo_image = StringIO('I pretend to be a graphics file')
490        ctrl = self.browser.getControl(name='birthcertificateupload')
491        file_ctrl = ctrl.mech_control
492        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.jpg')
493        # The Save action does not upload files
494        self.browser.getControl("Save").click() # submit form
495        self.assertFalse(
496            '<a target="image" href="birth_certificate">'
497            in self.browser.contents)
498        # ... but the correct upload submit button does
499        pseudo_image = StringIO('I pretend to be a graphics file')
500        ctrl = self.browser.getControl(name='birthcertificateupload')
501        file_ctrl = ctrl.mech_control
502        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.jpg')
503        self.browser.getControl(
504            name='upload_birthcertificateupload').click()
505        # There is a correct <img> link included
506        self.assertTrue(
507            '<a target="image" href="birth_certificate">'
508            in self.browser.contents)
509
510        # Browsing the link shows a real image
511        self.browser.open('birth_certificate')
512        self.assertEqual(
513            self.browser.headers['content-type'], 'image/jpeg')
514        self.assertEqual(len(self.browser.contents), 31)
515        # Reuploading a file which is bigger than 150k will raise an error
516        self.browser.open(self.edit_clearance_student_path)
517        photo_content = 'A' * 1024 * 151  # A string of 21 KB size
518        pseudo_image = StringIO(photo_content)
519        ctrl = self.browser.getControl(name='birthcertificateupload')
520        file_ctrl = ctrl.mech_control
521        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.jpg')
522        self.browser.getControl(
523            name='upload_birthcertificateupload').click()
524        self.assertTrue(
525            'Uploaded file is too big' in self.browser.contents)
526        # File names must meet several conditions
527        pseudo_image = StringIO('I pretend to be a graphics file')
528        ctrl = self.browser.getControl(name='birthcertificateupload')
529        file_ctrl = ctrl.mech_control
530        file_ctrl.add_file(pseudo_image, filename='my.photo.jpg')
531        self.browser.getControl(
532            name='upload_birthcertificateupload').click()
533        self.assertTrue('File name contains more than one dot'
534            in self.browser.contents)
535        ctrl = self.browser.getControl(name='birthcertificateupload')
536        file_ctrl = ctrl.mech_control
537        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate')
538        self.browser.getControl(
539            name='upload_birthcertificateupload').click()
540        self.assertTrue('File name has no extension' in self.browser.contents)
541        ctrl = self.browser.getControl(name='birthcertificateupload')
542        file_ctrl = ctrl.mech_control
543        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.bmp')
544        self.browser.getControl(
545            name='upload_birthcertificateupload').click()
546        self.assertTrue('Only the following extension are allowed'
547            in self.browser.contents)
548        # Managers can delete files
549        self.browser.getControl(name='delete_birthcertificateupload').click()
550        self.assertTrue(
551            'birth_certificate deleted' in self.browser.contents)
552        # Managers can add and delete second file
553        self.browser.open(self.edit_clearance_student_path)
554        pseudo_image = StringIO('I pretend to be a graphics file')
555        ctrl = self.browser.getControl(name='birthcertificateupload')
556        file_ctrl = ctrl.mech_control
557        file_ctrl.add_file(pseudo_image, filename='my_acceptance_letter.jpg')
558        self.browser.getControl(
559            name='upload_acceptanceletterupload').click()
560        self.assertFalse(
561            '<a target="image" href="acceptance_letter">'
562            in self.browser.contents)
563        ctrl = self.browser.getControl(name='acceptanceletterupload')
564        file_ctrl = ctrl.mech_control
565        file_ctrl.add_file(pseudo_image, filename='my_acceptance_letter.jpg')
566        self.browser.getControl(
567            name='upload_acceptanceletterupload').click()
568        self.assertTrue(
569            '<a target="image" href="acceptance_letter">'
570            in self.browser.contents)
571        self.browser.getControl(
572            name='delete_acceptanceletterupload').click()
573        self.assertTrue(
574            'acceptance_letter deleted'
575            in self.browser.contents)
576        # Managers can upload a file via the StudentBaseManageFormPage
577        self.browser.open(self.manage_student_path)
578        pseudo_image = StringIO('I pretend to be a graphics file')
579        ctrl = self.browser.getControl(name='passportuploadmanage')
580        file_ctrl = ctrl.mech_control
581        file_ctrl.add_file(pseudo_image, filename='my_photo.bmp')
582        self.browser.getControl(
583            name='upload_passportuploadmanage').click()
584        self.assertTrue('jpg file extension expected'
585            in self.browser.contents)
586        ctrl = self.browser.getControl(name='passportuploadmanage')
587        file_ctrl = ctrl.mech_control
588        file_ctrl.add_file(pseudo_image, filename='my_photo.jpg')
589        self.browser.getControl(
590            name='upload_passportuploadmanage').click()
591        self.assertTrue(
592            '<img align="middle" height="125px" src="passport.jpg" />'
593            in self.browser.contents)
594        # The clearance slip can't be opened because it requires a proper
595        # passport jpg file.
596        self.browser.open(self.student_path + '/clearance.pdf')
597        self.assertEqual(self.browser.headers['Status'], '200 Ok')
598        self.assertTrue('Error in image file' in self.browser.contents)
599        # We remove the passport file again
600        self.browser.open(self.manage_student_path)
601        self.browser.getControl('Delete').click()
602        self.browser.open(self.student_path + '/clearance.pdf')
603        self.assertEqual(self.browser.headers['Status'], '200 Ok')
604        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
605
606    def test_manage_course_lists(self):
607        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
608        self.browser.open(self.student_path)
609        self.browser.getLink("Study Course").click()
610        self.assertEqual(self.browser.headers['Status'], '200 Ok')
611        self.assertEqual(self.browser.url, self.studycourse_student_path)
612        self.assertTrue('Undergraduate Full-Time' in self.browser.contents)
613        self.browser.getLink("Manage").click()
614        self.assertTrue('Manage study course' in self.browser.contents)
615        # Before we can select a level, the certificate must
616        # be selected and saved
617        self.browser.getControl(name="form.certificate").value = ['CERT1']
618        self.browser.getControl(name="form.current_session").value = ['2004']
619        self.browser.getControl(name="form.current_verdict").value = ['A']
620        self.browser.getControl("Save").click()
621        # Now we can save also the current level which depends on start and end
622        # level of the certificate
623        self.browser.getControl(name="form.current_level").value = ['100']
624        self.browser.getControl("Save").click()
625        # Managers can add and remove any study level (course list)
626        self.browser.getControl(name="addlevel").value = ['100']
627        self.browser.getControl("Add study level").click()
628        self.assertMatches('...<span>100</span>...', self.browser.contents)
629        self.browser.getControl("Add study level").click()
630        self.assertMatches('...This level exists...', self.browser.contents)
631        self.browser.getControl("Remove selected").click()
632        self.assertMatches(
633            '...No study level selected...', self.browser.contents)
634        self.browser.getControl(name="val_id").value = ['100']
635        self.browser.getControl("Remove selected").click()
636        self.assertMatches('...Successfully removed...', self.browser.contents)
637        # Add level again
638        self.browser.getControl(name="addlevel").value = ['100']
639        self.browser.getControl("Add study level").click()
640
641        # Managers can view and manage course lists
642        self.browser.getLink("100").click()
643        self.assertMatches(
644            '...: Study Level 100 (Year 1)...', self.browser.contents)
645        self.browser.getLink("Manage").click()
646        self.browser.getControl(name="form.level_session").value = ['2002']
647        self.browser.getControl("Save").click()
648        self.browser.getControl("Remove selected").click()
649        self.assertMatches('...No ticket selected...', self.browser.contents)
650        ctrl = self.browser.getControl(name='val_id')
651        ctrl.getControl(value='COURSE1').selected = True
652        self.browser.getControl("Remove selected", index=0).click()
653        self.assertTrue('Successfully removed' in self.browser.contents)
654        self.browser.getControl("Add course ticket").click()
655        self.browser.getControl(name="form.course").value = ['COURSE1']
656        self.browser.getControl("Add course ticket").click()
657        self.assertTrue('Successfully added' in self.browser.contents)
658        self.browser.getControl("Add course ticket").click()
659        self.browser.getControl(name="form.course").value = ['COURSE1']
660        self.browser.getControl("Add course ticket").click()
661        self.assertTrue('The ticket exists' in self.browser.contents)
662        self.browser.getControl("Cancel").click()
663        self.browser.getLink("COURSE1").click()
664        self.browser.getLink("Manage").click()
665        self.browser.getControl(name="form.score").value = '10'
666        self.browser.getControl("Save").click()
667        self.assertTrue('Form has been saved' in self.browser.contents)
668        # Carry-over courses will be collected when next level is created
669        self.browser.open(self.student_path + '/studycourse/manage')
670        # Add next level
671        self.browser.getControl(name="addlevel").value = ['200']
672        self.browser.getControl("Add study level").click()
673        self.browser.getLink("200").click()
674        self.assertMatches(
675            '...: Study Level 200 (Year 2)...', self.browser.contents)
676        # COURSE1 has score 0 and thus will become a carry-over course
677        # in level 200
678        self.assertEqual(
679            sorted(self.student['studycourse']['200'].keys()), [u'COURSE1'])
680        self.assertTrue(
681            self.student['studycourse']['200']['COURSE1'].carry_over)
682        return
683
684    def test_manage_workflow(self):
685        # Managers can pass through the whole workflow
686        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
687        student = self.app['students'][self.student_id]
688        self.browser.open(self.manage_student_path)
689        self.assertTrue(student.clearance_locked)
690        self.browser.getControl(name="transition").value = ['admit']
691        self.browser.getControl("Save").click()
692        self.assertTrue(student.clearance_locked)
693        self.browser.getControl(name="transition").value = ['start_clearance']
694        self.browser.getControl("Save").click()
695        self.assertFalse(student.clearance_locked)
696        self.browser.getControl(name="transition").value = ['request_clearance']
697        self.browser.getControl("Save").click()
698        self.assertTrue(student.clearance_locked)
699        self.browser.getControl(name="transition").value = ['clear']
700        self.browser.getControl("Save").click()
701        self.browser.getControl(
702            name="transition").value = ['pay_first_school_fee']
703        self.browser.getControl("Save").click()
704        self.browser.getControl(name="transition").value = ['reset6']
705        self.browser.getControl("Save").click()
706        # In state returning the pay_school_fee transition triggers some
707        # changes of attributes
708        self.browser.getControl(name="transition").value = ['pay_school_fee']
709        self.browser.getControl("Save").click()
710        self.assertEqual(student['studycourse'].current_session, 2005) # +1
711        self.assertEqual(student['studycourse'].current_level, 200) # +100
712        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = not set
713        self.assertEqual(student['studycourse'].previous_verdict, 'A')
714        self.browser.getControl(name="transition").value = ['register_courses']
715        self.browser.getControl("Save").click()
716        self.browser.getControl(name="transition").value = ['validate_courses']
717        self.browser.getControl("Save").click()
718        self.browser.getControl(name="transition").value = ['return']
719        self.browser.getControl("Save").click()
720        return
721
722    def test_manage_import(self):
723        # Managers can import student data files
724        datacenter_path = 'http://localhost/app/datacenter'
725        # Prepare a csv file for students
726        open('students.csv', 'wb').write(
727"""firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,password
728Aaren,Pieri,1,1990-01-02,100000,aa@aa.ng,1234,mypwd1
729Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,mypwd1
730Brit,Berson,3,1990-01-04,100001,aa@aa.ng,1234,mypwd1
731""")
732        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
733        self.browser.open(datacenter_path)
734        self.browser.getLink('Upload CSV file').click()
735        filecontents = StringIO(open('students.csv', 'rb').read())
736        filewidget = self.browser.getControl(name='uploadfile:file')
737        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
738        self.browser.getControl(name='SUBMIT').click()
739        self.browser.getLink('Batch processing').click()
740        button = lookup_submit_value(
741            'select', 'students_zope.mgr.csv', self.browser)
742        button.click()
743        importerselect = self.browser.getControl(name='importer')
744        modeselect = self.browser.getControl(name='mode')
745        importerselect.getControl('Student Importer').selected = True
746        modeselect.getControl(value='create').selected = True
747        self.browser.getControl('Proceed to step 3').click()
748        self.assertTrue('Header fields OK' in self.browser.contents)
749        self.browser.getControl('Perform import').click()
750        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
751        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
752        self.assertTrue('Batch processing finished' in self.browser.contents)
753        open('studycourses.csv', 'wb').write(
754"""reg_number,matric_number,certificate,current_session,current_level
7551,,CERT1,2008,100
756,100001,CERT1,2008,100
757,100002,CERT1,2008,100
758""")
759        self.browser.open(datacenter_path)
760        self.browser.getLink('Upload CSV file').click()
761        filecontents = StringIO(open('studycourses.csv', 'rb').read())
762        filewidget = self.browser.getControl(name='uploadfile:file')
763        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
764        self.browser.getControl(name='SUBMIT').click()
765        self.browser.getLink('Batch processing').click()
766        button = lookup_submit_value(
767            'select', 'studycourses_zope.mgr.csv', self.browser)
768        button.click()
769        importerselect = self.browser.getControl(name='importer')
770        modeselect = self.browser.getControl(name='mode')
771        importerselect.getControl(
772            'StudentStudyCourse Importer (update only)').selected = True
773        modeselect.getControl(value='create').selected = True
774        self.browser.getControl('Proceed to step 3').click()
775        self.assertTrue('Update mode only' in self.browser.contents)
776        self.browser.getControl('Proceed to step 3').click()
777        self.assertTrue('Header fields OK' in self.browser.contents)
778        self.browser.getControl('Perform import').click()
779        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
780        self.assertTrue('Successfully processed 2 rows'
781                        in self.browser.contents)
782        # The students are properly indexed and we can
783        # thus find a student in  the department
784        self.browser.open(self.manage_container_path)
785        self.browser.getControl(name="searchtype").value = ['depcode']
786        self.browser.getControl(name="searchterm").value = 'dep1'
787        self.browser.getControl("Search").click()
788        self.assertTrue('Aaren Pieri' in self.browser.contents)
789        # We can search for a new student by name ...
790        self.browser.getControl(name="searchtype").value = ['fullname']
791        self.browser.getControl(name="searchterm").value = 'Claus'
792        self.browser.getControl("Search").click()
793        self.assertTrue('Claus Finau' in self.browser.contents)
794        # ... and check if the imported password has been properly set
795        ctrl = self.browser.getControl(name='entries')
796        value = ctrl.options[0]
797        claus = self.app['students'][value]
798        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
799        return
800
801    def test_handle_clearance_by_co(self):
802        # Create clearance officer
803        self.app['users'].addUser('mrclear', 'mrclearsecret')
804        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
805        self.app['users']['mrclear'].title = 'Carlo Pitter'
806        # Clearance officers need not necessarily to get
807        # the StudentsOfficer site role
808        #prmglobal = IPrincipalRoleManager(self.app)
809        #prmglobal.assignRoleToPrincipal('waeup.StudentsOfficer', 'mrclear')
810        # Assign local ClearanceOfficer role
811        department = self.app['faculties']['fac1']['dep1']
812        prmlocal = IPrincipalRoleManager(department)
813        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
814        IWorkflowState(self.student).setState('clearance started')
815        # Login as clearance officer
816        self.browser.open(self.login_path)
817        self.browser.getControl(name="form.login").value = 'mrclear'
818        self.browser.getControl(name="form.password").value = 'mrclearsecret'
819        self.browser.getControl("Login").click()
820        self.assertMatches('...You logged in...', self.browser.contents)
821        # CO can see his roles
822        self.browser.getLink("My Roles").click()
823        self.assertMatches(
824            '...<div>Academics Officer (view only)</div>...',
825            self.browser.contents)
826        #self.assertMatches(
827        #    '...<div>Students Officer (view only)</div>...',
828        #    self.browser.contents)
829        # But not his local role ...
830        self.assertFalse('Clearance Officer' in self.browser.contents)
831        # ... because we forgot to notify the department that the local role
832        # has changed
833        notify(LocalRoleSetEvent(
834            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
835        self.browser.open('http://localhost/app/users/mrclear/my_roles')
836        self.assertTrue('Clearance Officer' in self.browser.contents)
837        self.assertMatches(
838            '...<a href="http://localhost/app/faculties/fac1/dep1">...',
839            self.browser.contents)
840        # CO can view the student ...
841        self.browser.open(self.clearance_student_path)
842        self.assertEqual(self.browser.headers['Status'], '200 Ok')
843        self.assertEqual(self.browser.url, self.clearance_student_path)
844        # ... but not other students
845        other_student = Student()
846        other_student.firstname = u'Dep2'
847        other_student.lastname = u'Student'
848        self.app['students'].addStudent(other_student)
849        other_student_path = (
850            'http://localhost/app/students/%s' % other_student.student_id)
851        self.assertRaises(
852            Unauthorized, self.browser.open, other_student_path)
853        # Only in state clearance requested the CO does see the 'Clear' button
854        self.browser.open(self.clearance_student_path)
855        self.assertFalse('Clear student' in self.browser.contents)
856        IWorkflowInfo(self.student).fireTransition('request_clearance')
857        self.browser.open(self.clearance_student_path)
858        self.assertTrue('Clear student' in self.browser.contents)
859        self.browser.getLink("Clear student").click()
860        self.assertTrue('Student has been cleared' in self.browser.contents)
861        self.assertTrue('cleared' in self.browser.contents)
862        self.browser.getLink("Reject clearance").click()
863        self.assertTrue('Clearance has been annulled' in self.browser.contents)
864        urlmessage = 'Clearance+has+been+annulled.'
865        # CO does now see the contact form
866        self.assertEqual(self.browser.url, self.student_path +
867            '/contactstudent?subject=%s' % urlmessage)
868        self.assertTrue('clearance started' in self.browser.contents)
869        IWorkflowInfo(self.student).fireTransition('request_clearance')
870        self.browser.open(self.clearance_student_path)
871        self.browser.getLink("Reject clearance").click()
872        self.assertTrue('Clearance request has been rejected'
873            in self.browser.contents)
874        self.assertTrue('clearance started' in self.browser.contents)
875        # CO does now also see the contact form and can send a message
876        self.browser.getControl(name="form.subject").value = 'Important subject'
877        self.browser.getControl(name="form.body").value = 'Clearance rejected'
878        self.browser.getControl("Send message now").click()
879        self.assertTrue('Your message has been sent' in self.browser.contents)
880        # The CO can't clear students if not in state
881        # clearance requested
882        self.browser.open(self.student_path + '/clear')
883        self.assertTrue('Student is in wrong state'
884            in self.browser.contents)
885        # The CO can go to his department throug the my_roles page
886        self.browser.open('http://localhost/app/users/mrclear/my_roles')
887        self.browser.getLink("http://localhost/app/faculties/fac1/dep1").click()
888        # and view the list of students
889        self.browser.getLink("Show students").click()
890        self.assertTrue(self.student_id in self.browser.contents)
891
892    def test_handle_courses_by_ca(self):
893        # Create course adviser
894        self.app['users'].addUser('mrsadvise', 'mrsadvisesecret')
895        self.app['users']['mrsadvise'].email = 'mradvise@foo.ng'
896        self.app['users']['mrsadvise'].title = 'Helen Procter'
897        # Assign local CourseAdviser100 role for a certificate
898        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
899        prmlocal = IPrincipalRoleManager(cert)
900        prmlocal.assignRoleToPrincipal('waeup.local.CourseAdviser100', 'mrsadvise')
901        IWorkflowState(self.student).setState('school fee paid')
902        # Login as course adviser
903        self.browser.open(self.login_path)
904        self.browser.getControl(name="form.login").value = 'mrsadvise'
905        self.browser.getControl(name="form.password").value = 'mrsadvisesecret'
906        self.browser.getControl("Login").click()
907        self.assertMatches('...You logged in...', self.browser.contents)
908        # CO can see his roles
909        self.browser.getLink("My Roles").click()
910        self.assertMatches(
911            '...<div>Academics Officer (view only)</div>...',
912            self.browser.contents)
913        # But not his local role ...
914        self.assertFalse('Course Adviser' in self.browser.contents)
915        # ... because we forgot to notify the certificate that the local role
916        # has changed
917        notify(LocalRoleSetEvent(
918            cert, 'waeup.local.CourseAdviser100', 'mrsadvise', granted=True))
919        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
920        self.assertTrue('Course Adviser 100L' in self.browser.contents)
921        self.assertMatches(
922            '...<a href="http://localhost/app/faculties/fac1/dep1/certificates/CERT1">...',
923            self.browser.contents)
924        # CA can view the student ...
925        self.browser.open(self.student_path)
926        self.assertEqual(self.browser.headers['Status'], '200 Ok')
927        self.assertEqual(self.browser.url, self.student_path)
928        # ... but not other students
929        other_student = Student()
930        other_student.firstname = u'Dep2'
931        other_student.lastname = u'Student'
932        self.app['students'].addStudent(other_student)
933        other_student_path = (
934            'http://localhost/app/students/%s' % other_student.student_id)
935        self.assertRaises(
936            Unauthorized, self.browser.open, other_student_path)
937        # We add study level 110 to the student's studycourse
938        studylevel = StudentStudyLevel()
939        studylevel.level = 110
940        self.student['studycourse'].addStudentStudyLevel(
941            cert,studylevel)
942        L110_student_path = self.studycourse_student_path + '/110'
943        # Only in state courses registered and only if the current level
944        # corresponds with the name of the study level object
945        # the 100L CA does see the 'Validate' button
946        self.browser.open(L110_student_path)
947        self.assertFalse('Validate' in self.browser.contents)
948        IWorkflowInfo(self.student).fireTransition('register_courses')
949        self.browser.open(L110_student_path)
950        self.assertFalse('Validate' in self.browser.contents)
951        self.student['studycourse'].current_level = 110
952        self.browser.open(L110_student_path)
953        self.assertTrue('Validate' in self.browser.contents)
954        # ... but a 100L CA does not see the button on other levels
955        studylevel2 = StudentStudyLevel()
956        studylevel2.level = 200
957        self.student['studycourse'].addStudentStudyLevel(
958            cert,studylevel2)
959        L200_student_path = self.studycourse_student_path + '/200'
960        self.browser.open(L200_student_path)
961        self.assertFalse('Validate' in self.browser.contents)
962        self.browser.open(L110_student_path)
963        self.browser.getLink("Validate courses").click()
964        self.assertTrue('Course list has been validated' in self.browser.contents)
965        self.assertTrue('courses validated' in self.browser.contents)
966        self.browser.getLink("Reject courses").click()
967        self.assertTrue('Course list request has been annulled.'
968            in self.browser.contents)
969        urlmessage = 'Course+list+request+has+been+annulled.'
970        self.assertEqual(self.browser.url, self.student_path +
971            '/contactstudent?subject=%s' % urlmessage)
972        self.assertTrue('school fee paid' in self.browser.contents)
973        IWorkflowInfo(self.student).fireTransition('register_courses')
974        self.browser.open(L110_student_path)
975        self.browser.getLink("Reject courses").click()
976        self.assertTrue('Course list request has been rejected'
977            in self.browser.contents)
978        self.assertTrue('school fee paid' in self.browser.contents)
979        # CA does now see the contact form and can send a message
980        self.browser.getControl(name="form.subject").value = 'Important subject'
981        self.browser.getControl(name="form.body").value = 'Course list rejected'
982        self.browser.getControl("Send message now").click()
983        self.assertTrue('Your message has been sent' in self.browser.contents)
984        # The CA can't validate courses if not in state
985        # courses registered
986        self.browser.open(L110_student_path + '/validate_courses')
987        self.assertTrue('Student is in the wrong state'
988            in self.browser.contents)
989        # The CA can go to his certificate throug the my_roles page
990        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
991        self.browser.getLink(
992            "http://localhost/app/faculties/fac1/dep1/certificates/CERT1").click()
993        # and view the list of students
994        self.browser.getLink("Show students").click()
995        self.assertTrue(self.student_id in self.browser.contents)
996
997    def test_student_change_password(self):
998        # Students can change the password
999        self.browser.open(self.login_path)
1000        self.browser.getControl(name="form.login").value = self.student_id
1001        self.browser.getControl(name="form.password").value = 'spwd'
1002        self.browser.getControl("Login").click()
1003        self.assertEqual(self.browser.url, self.student_path)
1004        self.assertTrue('You logged in' in self.browser.contents)
1005        # Change password
1006        self.browser.getLink("Change password").click()
1007        self.browser.getControl(name="change_password").value = 'pw'
1008        self.browser.getControl(
1009            name="change_password_repeat").value = 'pw'
1010        self.browser.getControl("Save").click()
1011        self.assertTrue('Password must have at least' in self.browser.contents)
1012        self.browser.getControl(name="change_password").value = 'new_password'
1013        self.browser.getControl(
1014            name="change_password_repeat").value = 'new_passssword'
1015        self.browser.getControl("Save").click()
1016        self.assertTrue('Passwords do not match' in self.browser.contents)
1017        self.browser.getControl(name="change_password").value = 'new_password'
1018        self.browser.getControl(
1019            name="change_password_repeat").value = 'new_password'
1020        self.browser.getControl("Save").click()
1021        self.assertTrue('Password changed' in self.browser.contents)
1022        # We are still logged in. Changing the password hasn't thrown us out.
1023        self.browser.getLink("Base Data").click()
1024        self.assertEqual(self.browser.url, self.student_path)
1025        # We can logout
1026        self.browser.getLink("Logout").click()
1027        self.assertTrue('You have been logged out' in self.browser.contents)
1028        self.assertEqual(self.browser.url, 'http://localhost/app')
1029        # We can login again with the new password
1030        self.browser.getLink("Login").click()
1031        self.browser.open(self.login_path)
1032        self.browser.getControl(name="form.login").value = self.student_id
1033        self.browser.getControl(name="form.password").value = 'new_password'
1034        self.browser.getControl("Login").click()
1035        self.assertEqual(self.browser.url, self.student_path)
1036        self.assertTrue('You logged in' in self.browser.contents)
1037        return
1038
1039    def test_setpassword(self):
1040        # Set password for first-time access
1041        student = Student()
1042        student.reg_number = u'123456'
1043        student.firstname = u'Klaus'
1044        student.lastname = u'Tester'
1045        self.app['students'].addStudent(student)
1046        setpassword_path = 'http://localhost/app/setpassword'
1047        student_path = 'http://localhost/app/students/%s' % student.student_id
1048        self.browser.open(setpassword_path)
1049        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
1050        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1051        self.browser.getControl(name="reg_number").value = '223456'
1052        self.browser.getControl("Set").click()
1053        self.assertMatches('...No student found...',
1054                           self.browser.contents)
1055        self.browser.getControl(name="reg_number").value = '123456'
1056        self.browser.getControl(name="ac_number").value = '999999'
1057        self.browser.getControl("Set").click()
1058        self.assertMatches('...Access code is invalid...',
1059                           self.browser.contents)
1060        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1061        self.browser.getControl("Set").click()
1062        self.assertMatches('...Password has been set. Your Student Id is...',
1063                           self.browser.contents)
1064        self.browser.getControl("Set").click()
1065        self.assertMatches(
1066            '...Password has already been set. Your Student Id is...',
1067            self.browser.contents)
1068        existing_pwdpin = self.pwdpins[1]
1069        parts = existing_pwdpin.split('-')[1:]
1070        existing_pwdseries, existing_pwdnumber = parts
1071        self.browser.getControl(name="ac_series").value = existing_pwdseries
1072        self.browser.getControl(name="ac_number").value = existing_pwdnumber
1073        self.browser.getControl(name="reg_number").value = '123456'
1074        self.browser.getControl("Set").click()
1075        self.assertMatches(
1076            '...You are using the wrong Access Code...',
1077            self.browser.contents)
1078        # The student can login with the new credentials
1079        self.browser.open(self.login_path)
1080        self.browser.getControl(name="form.login").value = student.student_id
1081        self.browser.getControl(
1082            name="form.password").value = self.existing_pwdnumber
1083        self.browser.getControl("Login").click()
1084        self.assertEqual(self.browser.url, student_path)
1085        self.assertTrue('You logged in' in self.browser.contents)
1086        return
1087
1088    def test_student_access(self):
1089        # Students can access their own objects
1090        # and can perform actions
1091        IWorkflowInfo(self.student).fireTransition('admit')
1092        self.browser.open(self.login_path)
1093        self.browser.getControl(name="form.login").value = self.student_id
1094        self.browser.getControl(name="form.password").value = 'spwd'
1095        self.browser.getControl("Login").click()
1096        # Student can upload a passport picture
1097        self.browser.open(self.student_path + '/change_portrait')
1098        ctrl = self.browser.getControl(name='passportuploadedit')
1099        file_obj = open(
1100            os.path.join(os.path.dirname(__file__), 'test_image.jpg'),'rb')
1101        file_ctrl = ctrl.mech_control
1102        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1103        self.browser.getControl(
1104            name='upload_passportuploadedit').click()
1105        self.assertTrue(
1106            '<img align="middle" height="125px" src="passport.jpg" />'
1107            in self.browser.contents)
1108        # Student can view the clearance data
1109        self.browser.getLink("Clearance Data").click()
1110        # Student can't open clearance edit form before starting clearance
1111        self.browser.open(self.student_path + '/cedit')
1112        self.assertMatches('...The requested form is locked...',
1113                           self.browser.contents)
1114        self.browser.getLink("Clearance Data").click()
1115        self.browser.getLink("Start clearance").click()
1116        self.student.email = None
1117        # Uups, we forgot to fill the email fields
1118        self.browser.getControl("Start clearance").click()
1119        self.assertMatches('...Not all required fields filled...',
1120                           self.browser.contents)
1121        self.student.email = 'aa@aa.ng'
1122        self.browser.open(self.student_path + '/start_clearance')
1123        self.browser.getControl(name="ac_series").value = '3'
1124        self.browser.getControl(name="ac_number").value = '4444444'
1125        self.browser.getControl("Start clearance now").click()
1126        self.assertMatches('...Activation code is invalid...',
1127                           self.browser.contents)
1128        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1129        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1130        # Owner is Hans Wurst, AC can't be invalidated
1131        self.browser.getControl("Start clearance now").click()
1132        self.assertMatches('...You are not the owner of this access code...',
1133                           self.browser.contents)
1134        # Set the correct owner
1135        self.existing_clrac.owner = self.student_id
1136        self.browser.getControl("Start clearance now").click()
1137        self.assertMatches('...Clearance process has been started...',
1138                           self.browser.contents)
1139        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1140        self.browser.getControl("Save", index=0).click()
1141        # Student can view the clearance data
1142        self.browser.getLink("Clearance Data").click()
1143        # and go back to the edit form
1144        self.browser.getLink("Edit").click()
1145        self.browser.getControl("Save and request clearance").click()
1146       
1147        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1148        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1149        self.browser.getControl("Request clearance now").click()
1150        self.assertMatches('...Clearance has been requested...',
1151                           self.browser.contents)
1152        # Student can't reopen clearance form after requesting clearance
1153        self.browser.open(self.student_path + '/cedit')
1154        self.assertMatches('...The requested form is locked...',
1155                           self.browser.contents)
1156        # Student can't add study level if not in state 'school fee paid'
1157        self.browser.open(self.student_path + '/studycourse/add')
1158        self.assertMatches('...The requested form is locked...',
1159                           self.browser.contents)
1160        # ... and must be transferred first
1161        IWorkflowInfo(self.student).fireTransition('clear')
1162        IWorkflowInfo(self.student).fireTransition('pay_first_school_fee')
1163        # Now students can add the current study level
1164        self.browser.getLink("Study Course").click()
1165        self.browser.getLink("Add course list").click()
1166        self.assertMatches('...Add current level 100 (Year 1)...',
1167                           self.browser.contents)
1168        self.browser.getControl("Create course list now").click()
1169        self.browser.getLink("100").click()
1170        self.browser.getLink("Add and remove courses").click()
1171        self.browser.getControl("Add course ticket").click()
1172        self.browser.getControl(name="form.course").value = ['COURSE1']
1173        self.browser.getControl("Add course ticket").click()
1174        self.assertMatches('...The ticket exists...',
1175                           self.browser.contents)
1176        self.student['studycourse'].current_level = 200
1177        self.browser.getLink("Study Course").click()
1178        self.browser.getLink("Add course list").click()
1179        self.assertMatches('...Add current level 200 (Year 2)...',
1180                           self.browser.contents)
1181        self.browser.getControl("Create course list now").click()
1182        self.browser.getLink("200").click()
1183        self.browser.getLink("Add and remove courses").click()
1184        self.browser.getControl("Add course ticket").click()
1185        self.browser.getControl(name="form.course").value = ['COURSE1']
1186        self.browser.getControl("Add course ticket").click()
1187        self.assertMatches('...The ticket exists...',
1188                           self.browser.contents)
1189        # Indeed the ticket exists as carry-over course from level 100
1190        # since its score was 0
1191        self.assertTrue(
1192            self.student['studycourse']['200']['COURSE1'].carry_over is True)
1193        # Students can open the pdf course registration slip
1194        self.browser.open(self.student_path + '/studycourse/200')
1195        self.browser.getLink("Download course registration slip").click()
1196        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1197        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1198        # Students can remove course tickets
1199        self.browser.open(self.student_path + '/studycourse/200/edit')
1200        self.browser.getControl("Remove selected", index=0).click()
1201        self.assertTrue('No ticket selected' in self.browser.contents)
1202        # No ticket can be selected since the carry-over course is a core course
1203        self.assertRaises(
1204            LookupError, self.browser.getControl, name='val_id')
1205        self.student['studycourse']['200']['COURSE1'].mandatory = False
1206        self.browser.open(self.student_path + '/studycourse/200/edit')
1207        # Now the student can remove the ticket
1208        ctrl = self.browser.getControl(name='val_id')
1209        ctrl.getControl(value='COURSE1').selected = True
1210        self.browser.getControl("Remove selected", index=0).click()
1211        self.assertTrue('Successfully removed' in self.browser.contents)
1212        self.browser.getControl("Register course list").click()
1213        self.assertTrue('Course list has been registered' in self.browser.contents)
1214        self.assertEqual(self.student.state, 'courses registered')
1215        return
1216
1217    def test_manage_payments(self):
1218        # Managers can add online school fee payment tickets
1219        # if certain requirements are met
1220        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1221        self.browser.open(self.payments_student_path)
1222        self.browser.getControl("Add online payment ticket").click()
1223        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1224        self.browser.getControl("Create ticket").click()
1225        self.assertMatches('...ticket created...',
1226                           self.browser.contents)
1227        ctrl = self.browser.getControl(name='val_id')
1228        value = ctrl.options[0]
1229        self.browser.getLink(value).click()
1230        self.assertMatches('...Amount Authorized...',
1231                           self.browser.contents)
1232        payment_url = self.browser.url
1233
1234        # The pdf payment receipt can't yet be opened
1235        self.browser.open(payment_url + '/payment_receipt.pdf')
1236        self.assertMatches('...Ticket not yet paid...',
1237                           self.browser.contents)
1238
1239        # The same payment (with same p_item, p_session and p_category)
1240        # can be initialized a second time if the former ticket is not yet paid.
1241        self.browser.open(self.payments_student_path)
1242        self.browser.getControl("Add online payment ticket").click()
1243        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1244        self.browser.getControl("Create ticket").click()
1245        self.assertMatches('...Payment ticket created...',
1246                           self.browser.contents)
1247
1248        # Managers can open the callback view which simulates a valid callback
1249        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
1250        self.browser.open(payment_url)
1251        self.browser.getLink("Request callback").click()
1252        self.assertMatches('...Valid callback received...',
1253                          self.browser.contents)
1254
1255        # Callback can't be applied twice
1256        self.browser.open(payment_url + '/callback')
1257        self.assertMatches('...This ticket has already been paid...',
1258                          self.browser.contents)
1259
1260        # Now the first ticket is paid and no more ticket of same type
1261        # (with same p_item, p_session and p_category) can be added
1262        self.browser.open(self.payments_student_path)
1263        self.browser.getControl("Add online payment ticket").click()
1264        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1265        self.browser.getControl("Create ticket").click()
1266        self.assertMatches(
1267            '...This type of payment has already been made...',
1268            self.browser.contents)
1269
1270        # Managers can open the pdf payment receipt
1271        self.browser.open(payment_url)
1272        self.browser.getLink("Download payment receipt").click()
1273        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1274        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1275
1276        # Managers can remove online school fee payment tickets
1277        self.browser.open(self.payments_student_path)
1278        self.browser.getControl("Remove selected").click()
1279        self.assertMatches('...No payment selected...', self.browser.contents)
1280        ctrl = self.browser.getControl(name='val_id')
1281        value = ctrl.options[0]
1282        ctrl.getControl(value=value).selected = True
1283        self.browser.getControl("Remove selected", index=0).click()
1284        self.assertTrue('Successfully removed' in self.browser.contents)
1285
1286        # Managers can add online clearance payment tickets
1287        self.browser.open(self.payments_student_path + '/addop')
1288        self.browser.getControl(name="form.p_category").value = ['clearance']
1289        self.browser.getControl("Create ticket").click()
1290        self.assertMatches('...ticket created...',
1291                           self.browser.contents)
1292
1293        # Managers can open the callback view which simulates a valid callback
1294        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
1295        ctrl = self.browser.getControl(name='val_id')
1296        value = ctrl.options[1] # The clearance payment is the second in the table
1297        self.browser.getLink(value).click()
1298        self.browser.open(self.browser.url + '/callback')
1299        self.assertMatches('...Valid callback received...',
1300                          self.browser.contents)
1301        expected = '''...
1302        <td>
1303          Paid
1304        </td>...'''
1305        self.assertMatches(expected,self.browser.contents)
1306        # The new CLR-0 pin has been created
1307        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
1308        pin = self.app['accesscodes']['CLR-0'].keys()[0]
1309        ac = self.app['accesscodes']['CLR-0'][pin]
1310        ac.owner = self.student_id
1311        return
1312
1313    def test_student_payments(self):
1314        # Login
1315        self.browser.open(self.login_path)
1316        self.browser.getControl(name="form.login").value = self.student_id
1317        self.browser.getControl(name="form.password").value = 'spwd'
1318        self.browser.getControl("Login").click()
1319
1320        # Students can add online clearance payment tickets
1321        self.browser.open(self.payments_student_path + '/addop')
1322        self.browser.getControl(name="form.p_category").value = ['clearance']
1323        self.browser.getControl("Create ticket").click()
1324        self.assertMatches('...ticket created...',
1325                           self.browser.contents)
1326
1327        # Students can open the callback view which simulates a valid callback
1328        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
1329        ctrl = self.browser.getControl(name='val_id')
1330        value = ctrl.options[0]
1331        self.browser.getLink(value).click()
1332        payment_url = self.browser.url
1333        self.browser.open(payment_url + '/callback')
1334        self.assertMatches('...Valid callback received...',
1335                          self.browser.contents)
1336        expected = '''...
1337        <td>
1338          Paid
1339        </td>...'''
1340        self.assertMatches(expected,self.browser.contents)
1341        # The new CLR-0 pin has been created
1342        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
1343        pin = self.app['accesscodes']['CLR-0'].keys()[0]
1344        ac = self.app['accesscodes']['CLR-0'][pin]
1345        ac.owner = self.student_id
1346
1347        # Students can open the pdf payment receipt
1348        self.browser.open(payment_url + '/payment_receipt.pdf')
1349        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1350        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1351
1352        # The new CLR-0 pin can be used for starting clearance
1353        # but they have to upload a passport picture first
1354        # which is only possible in state admitted
1355        self.browser.open(self.student_path + '/change_portrait')
1356        self.assertMatches('...form is locked...',
1357                          self.browser.contents)
1358        IWorkflowInfo(self.student).fireTransition('admit')
1359        self.browser.open(self.student_path + '/change_portrait')
1360        pseudo_image = StringIO('I pretend to be a graphics file')
1361        ctrl = self.browser.getControl(name='passportuploadedit')
1362        file_ctrl = ctrl.mech_control
1363        file_ctrl.add_file(pseudo_image, filename='my_photo.jpg')
1364        self.browser.getControl(
1365            name='upload_passportuploadedit').click()
1366        self.browser.open(self.student_path + '/start_clearance')
1367        parts = pin.split('-')[1:]
1368        clrseries, clrnumber = parts
1369        self.browser.getControl(name="ac_series").value = clrseries
1370        self.browser.getControl(name="ac_number").value = clrnumber
1371        self.browser.getControl("Start clearance now").click()
1372        self.assertMatches('...Clearance process has been started...',
1373                           self.browser.contents)
1374
1375        # Students can add online school fee payment tickets
1376        self.browser.open(self.payments_student_path)
1377        self.browser.getControl("Add online payment ticket").click()
1378        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1379        self.browser.getControl("Create ticket").click()
1380        self.assertMatches('...ticket created...',
1381                           self.browser.contents)
1382        ctrl = self.browser.getControl(name='val_id')
1383        value = ctrl.options[0]
1384        self.browser.getLink(value).click()
1385        self.assertMatches('...Amount Authorized...',
1386                           self.browser.contents)
1387
1388        # Students can open the callback view which simulates a valid callback
1389        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
1390        self.browser.open(self.browser.url + '/callback')
1391        self.assertMatches('...Valid callback received...',
1392                          self.browser.contents)
1393
1394        # Students can remove only online payment tickets which have
1395        # not received a valid callback
1396        self.browser.open(self.payments_student_path)
1397        self.assertRaises(
1398            LookupError, self.browser.getControl, name='val_id')
1399        self.browser.open(self.payments_student_path + '/addop')
1400        self.browser.getControl(name="form.p_category").value = ['gown']
1401        self.browser.getControl("Create ticket").click()
1402        self.browser.open(self.payments_student_path)
1403        ctrl = self.browser.getControl(name='val_id')
1404        value = ctrl.options[0]
1405        ctrl.getControl(value=value).selected = True
1406        self.browser.getControl("Remove selected", index=0).click()
1407        self.assertTrue('Successfully removed' in self.browser.contents)
1408
1409        # The new SFE-0 pin can be used for starting course registration
1410        IWorkflowInfo(self.student).fireTransition('request_clearance')
1411        IWorkflowInfo(self.student).fireTransition('clear')
1412        self.browser.open(self.studycourse_student_path)
1413        self.browser.getLink('Start course registration').click()
1414        pin = self.app['accesscodes']['SFE-0'].keys()[0]
1415        parts = pin.split('-')[1:]
1416        sfeseries, sfenumber = parts
1417        self.browser.getControl(name="ac_series").value = sfeseries
1418        self.browser.getControl(name="ac_number").value = sfenumber
1419        self.browser.getControl("Start course registration now").click()
1420        self.assertMatches('...Course registration has been started...',
1421                           self.browser.contents)
1422        self.assertTrue(self.student.state == 'school fee paid')
1423        return
1424
1425    def test_manage_accommodation(self):
1426        # Managers can add online booking fee payment tickets and open the
1427        # callback view (see test_manage_payments)
1428        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1429        self.browser.open(self.payments_student_path)
1430        self.browser.getControl("Add online payment ticket").click()
1431        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1432        # If student is not in accommodation session, payment cannot be processed
1433        self.app['configuration'].accommodation_session = 2011
1434        self.browser.getControl("Create ticket").click()
1435        self.assertMatches('...Your current session does not match...',
1436                           self.browser.contents)
1437        self.app['configuration'].accommodation_session = 2004
1438        self.browser.getControl("Add online payment ticket").click()
1439        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1440        self.browser.getControl("Create ticket").click()
1441        ctrl = self.browser.getControl(name='val_id')
1442        value = ctrl.options[0]
1443        self.browser.getLink(value).click()
1444        self.browser.open(self.browser.url + '/callback')
1445        # The new HOS-0 pin has been created
1446        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1447        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1448        ac = self.app['accesscodes']['HOS-0'][pin]
1449        ac.owner = self.student_id
1450        parts = pin.split('-')[1:]
1451        sfeseries, sfenumber = parts
1452        # Managers can use HOS code and book a bed space with it
1453        self.browser.open(self.acco_student_path)
1454        self.browser.getLink("Book accommodation").click()
1455        self.assertMatches('...You are in the wrong...',
1456                           self.browser.contents)
1457        IWorkflowInfo(self.student).fireTransition('admit')
1458        # An existing HOS code can only be used if students
1459        # are in accommodation session
1460        self.student['studycourse'].current_session = 2003
1461        self.browser.getLink("Book accommodation").click()
1462        self.assertMatches('...Your current session does not match...',
1463                           self.browser.contents)
1464        self.student['studycourse'].current_session = 2004
1465        # All requirements are met and ticket can be created
1466        self.browser.getLink("Book accommodation").click()
1467        self.assertMatches('...Activation Code:...',
1468                           self.browser.contents)
1469        self.browser.getControl(name="ac_series").value = sfeseries
1470        self.browser.getControl(name="ac_number").value = sfenumber
1471        self.browser.getControl("Create bed ticket").click()
1472        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1473                           self.browser.contents)
1474        # Bed has been allocated
1475        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
1476        self.assertTrue(bed1.owner == self.student_id)
1477        # BedTicketAddPage is now blocked
1478        self.browser.getLink("Book accommodation").click()
1479        self.assertMatches('...You already booked a bed space...',
1480            self.browser.contents)
1481        # The bed ticket displays the data correctly
1482        self.browser.open(self.acco_student_path + '/2004')
1483        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1484                           self.browser.contents)
1485        self.assertMatches('...2004/2005...', self.browser.contents)
1486        self.assertMatches('...regular_male_fr...', self.browser.contents)
1487        self.assertMatches('...%s...' % pin, self.browser.contents)
1488        # Managers can relocate students if the student's bed_type has changed
1489        self.browser.getLink("Relocate student").click()
1490        self.assertMatches(
1491            "...Student can't be relocated...", self.browser.contents)
1492        self.student.sex = u'f'
1493        self.browser.getLink("Relocate student").click()
1494        self.assertMatches(
1495            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1496        self.assertTrue(bed1.owner == NOT_OCCUPIED)
1497        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1498        self.assertTrue(bed2.owner == self.student_id)
1499        self.assertTrue(self.student['accommodation'][
1500            '2004'].bed_type == u'regular_female_fr')
1501        # The payment object still shows the original payment item
1502        payment_id = self.student['payments'].keys()[0]
1503        payment = self.student['payments'][payment_id]
1504        self.assertTrue(payment.p_item == u'regular_male_fr')
1505        # Managers can relocate students if the bed's bed_type has changed
1506        bed1.bed_type = u'regular_female_fr'
1507        bed2.bed_type = u'regular_male_fr'
1508        notify(grok.ObjectModifiedEvent(bed1))
1509        notify(grok.ObjectModifiedEvent(bed2))
1510        self.browser.getLink("Relocate student").click()
1511        self.assertMatches(
1512            "...Student relocated...", self.browser.contents)
1513        self.assertMatches(
1514            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
1515        self.assertMatches(bed1.owner, self.student_id)
1516        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1517        # Managers can't relocate students if bed is reserved
1518        self.student.sex = u'm'
1519        bed1.bed_type = u'regular_female_reserved'
1520        notify(grok.ObjectModifiedEvent(bed1))
1521        self.browser.getLink("Relocate student").click()
1522        self.assertMatches(
1523            "...Students in reserved beds can't be relocated...",
1524            self.browser.contents)
1525        # Managers can relocate students if booking has been cancelled but
1526        # other bed space has been manually allocated after cancellation
1527        old_owner = bed1.releaseBed()
1528        self.assertMatches(old_owner, self.student_id)
1529        bed2.owner = self.student_id
1530        self.browser.open(self.acco_student_path + '/2004')
1531        self.assertMatches(
1532            "...booking cancelled...", self.browser.contents)
1533        self.browser.getLink("Relocate student").click()
1534        # We didn't informed the catalog therefore the new owner is not found
1535        self.assertMatches(
1536            "...There is no free bed in your category regular_male_fr...",
1537            self.browser.contents)
1538        # Now we fire the event properly
1539        notify(grok.ObjectModifiedEvent(bed2))
1540        self.browser.getLink("Relocate student").click()
1541        self.assertMatches(
1542            "...Student relocated...", self.browser.contents)
1543        self.assertMatches(
1544            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1545          # Managers can delete bed tickets
1546        self.browser.open(self.acco_student_path)
1547        ctrl = self.browser.getControl(name='val_id')
1548        value = ctrl.options[0]
1549        ctrl.getControl(value=value).selected = True
1550        self.browser.getControl("Remove selected", index=0).click()
1551        self.assertMatches('...Successfully removed...', self.browser.contents)
1552        # The bed has been properly released by the event handler
1553        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1554        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1555        return
1556
1557    def test_student_accommodation(self):
1558        # Login
1559        self.browser.open(self.login_path)
1560        self.browser.getControl(name="form.login").value = self.student_id
1561        self.browser.getControl(name="form.password").value = 'spwd'
1562        self.browser.getControl("Login").click()
1563
1564        # Students can add online booking fee payment tickets and open the
1565        # callback view (see test_manage_payments)
1566        self.browser.getLink("Payments").click()
1567        self.browser.getControl("Add online payment ticket").click()
1568        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1569        self.browser.getControl("Create ticket").click()
1570        ctrl = self.browser.getControl(name='val_id')
1571        value = ctrl.options[0]
1572        self.browser.getLink(value).click()
1573        self.browser.open(self.browser.url + '/callback')
1574        # The new HOS-0 pin has been created
1575        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1576        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1577        ac = self.app['accesscodes']['HOS-0'][pin]
1578        ac.owner = u'Anybody'
1579        parts = pin.split('-')[1:]
1580        sfeseries, sfenumber = parts
1581
1582        # Students can use HOS code and book a bed space with it
1583        self.browser.open(self.acco_student_path)
1584        self.browser.getLink("Book accommodation").click()
1585        self.assertMatches('...You are in the wrong...',
1586                           self.browser.contents)
1587        IWorkflowInfo(self.student).fireTransition('admit')
1588        self.browser.getLink("Book accommodation").click()
1589        self.assertMatches('...Activation Code:...',
1590                           self.browser.contents)
1591        self.browser.getControl(name="ac_series").value = u'nonsense'
1592        self.browser.getControl(name="ac_number").value = sfenumber
1593        self.browser.getControl("Create bed ticket").click()
1594        self.assertMatches('...Activation code is invalid...',
1595                           self.browser.contents)
1596        self.browser.getControl(name="ac_series").value = sfeseries
1597        self.browser.getControl(name="ac_number").value = sfenumber
1598        self.browser.getControl("Create bed ticket").click()
1599        self.assertMatches('...You are not the owner of this access code...',
1600                           self.browser.contents)
1601        ac.owner = self.student_id
1602        self.browser.getControl(name="ac_series").value = sfeseries
1603        self.browser.getControl(name="ac_number").value = sfenumber
1604        self.browser.getControl("Create bed ticket").click()
1605        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1606                           self.browser.contents)
1607
1608        # Bed has been allocated
1609        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
1610        self.assertTrue(bed.owner == self.student_id)
1611
1612        # BedTicketAddPage is now blocked
1613        self.browser.getLink("Book accommodation").click()
1614        self.assertMatches('...You already booked a bed space...',
1615            self.browser.contents)
1616
1617        # The bed ticket displays the data correctly
1618        self.browser.open(self.acco_student_path + '/2004')
1619        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1620                           self.browser.contents)
1621        self.assertMatches('...2004/2005...', self.browser.contents)
1622        self.assertMatches('...regular_male_fr...', self.browser.contents)
1623        self.assertMatches('...%s...' % pin, self.browser.contents)
1624
1625        # Students can open the pdf slip
1626        self.browser.open(self.browser.url + '/bed_allocation.pdf')
1627        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1628        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1629
1630        # Students can't relocate themselves
1631        self.assertFalse('Relocate' in self.browser.contents)
1632        relocate_path = self.acco_student_path + '/2004/relocate'
1633        self.assertRaises(
1634            Unauthorized, self.browser.open, relocate_path)
1635
1636        # Students can't the Remove button and check boxes
1637        self.browser.open(self.acco_student_path)
1638        self.assertFalse('Remove' in self.browser.contents)
1639        self.assertFalse('val_id' in self.browser.contents)
1640        return
1641
1642    def test_change_password_request(self):
1643        self.browser.open('http://localhost/app/changepw')
1644        self.browser.getControl(name="form.reg_number").value = '123'
1645        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1646        self.browser.getControl("Get new login credentials").click()
1647        self.assertTrue('An email with' in self.browser.contents)
1648
1649    def test_reindex(self):
1650        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1651        self.browser.open('http://localhost/app/reindex')
1652        self.assertTrue('No catalog name provided' in self.browser.contents)
1653        self.browser.open('http://localhost/app/reindex?ctlg=xyz')
1654        self.assertTrue('xyz_catalog does not exist' in self.browser.contents)
1655        cat = queryUtility(ICatalog, name='students_catalog')
1656        results = cat.searchResults(student_id=(None, None))
1657        self.assertEqual(len(results),1)
1658        cat.clear()
1659        results = cat.searchResults(student_id=(None, None))
1660        self.assertEqual(len(results),0)
1661        self.browser.open('http://localhost/app/reindex?ctlg=students')
1662        self.assertTrue('1 students re-indexed' in self.browser.contents)
1663        results = cat.searchResults(student_id=(None, None))
1664        self.assertEqual(len(results),1)
Note: See TracBrowser for help on using the repository browser.