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

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

Lock CourseTicketAddFormPage2 if not in state school fee paid.

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