source: main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py @ 7510

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

Import student password as well. This is crucial for upcoming portal migration.

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