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

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

Add test which shows that we can't logout when using basic authentication.

Wikipedia: Existing browsers retain authentication information until the tab or browser is closed or the user clears the history.[3] HTTP does not provide a method for a server to direct clients to discard these cached credentials. This means that there is no effective way for a server to "log out" the user without closing the browser. This is a significant defect that requires browser manufacturers to support a "logout" user interface element ...

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