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

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

Remove unneeded default values in interfaces. Adjust tests.

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