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

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

Add current_mode index to students_catalog.

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