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

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

Adjust test.

  • Property svn:keywords set to Id
File size: 83.8 KB
Line 
1## $Id: test_browser.py 7650 2012-02-15 08:11:56Z 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        self.assertMatches(expected,self.browser.contents)
1285        # The new CLR-0 pin has been created
1286        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
1287        pin = self.app['accesscodes']['CLR-0'].keys()[0]
1288        ac = self.app['accesscodes']['CLR-0'][pin]
1289        ac.owner = self.student_id
1290        return
1291
1292    def test_student_payments(self):
1293        # Login
1294        self.browser.open(self.login_path)
1295        self.browser.getControl(name="form.login").value = self.student_id
1296        self.browser.getControl(name="form.password").value = 'spwd'
1297        self.browser.getControl("Login").click()
1298
1299        # Students can add online clearance payment tickets
1300        self.browser.open(self.payments_student_path + '/addop')
1301        self.browser.getControl(name="form.p_category").value = ['clearance']
1302        self.browser.getControl("Create ticket").click()
1303        self.assertMatches('...ticket created...',
1304                           self.browser.contents)
1305
1306        # Students can open the callback view which simulates a valid callback
1307        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
1308        ctrl = self.browser.getControl(name='val_id')
1309        value = ctrl.options[0]
1310        self.browser.getLink(value).click()
1311        payment_url = self.browser.url
1312        self.browser.open(payment_url + '/callback')
1313        self.assertMatches('...Valid callback received...',
1314                          self.browser.contents)
1315        expected = '''...
1316        <td>
1317          Paid
1318        </td>...'''
1319        self.assertMatches(expected,self.browser.contents)
1320        # The new CLR-0 pin has been created
1321        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
1322        pin = self.app['accesscodes']['CLR-0'].keys()[0]
1323        ac = self.app['accesscodes']['CLR-0'][pin]
1324        ac.owner = self.student_id
1325
1326        # Students can open the pdf payment receipt
1327        self.browser.open(payment_url + '/payment_receipt.pdf')
1328        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1329        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1330
1331        # The new CLR-0 pin can be used for starting clearance
1332        # but they have to upload a passport picture first
1333        # which is only possible in state admitted
1334        self.browser.open(self.student_path + '/change_portrait')
1335        self.assertMatches('...form is locked...',
1336                          self.browser.contents)
1337        IWorkflowInfo(self.student).fireTransition('admit')
1338        self.browser.open(self.student_path + '/change_portrait')
1339        pseudo_image = StringIO('I pretend to be a graphics file')
1340        ctrl = self.browser.getControl(name='passportuploadedit')
1341        file_ctrl = ctrl.mech_control
1342        file_ctrl.add_file(pseudo_image, filename='my_photo.jpg')
1343        self.browser.getControl(
1344            name='upload_passportuploadedit').click()
1345        self.browser.open(self.student_path + '/start_clearance')
1346        parts = pin.split('-')[1:]
1347        clrseries, clrnumber = parts
1348        self.browser.getControl(name="ac_series").value = clrseries
1349        self.browser.getControl(name="ac_number").value = clrnumber
1350        self.browser.getControl("Start clearance now").click()
1351        self.assertMatches('...Clearance process has been started...',
1352                           self.browser.contents)
1353
1354        # Students can add online school fee payment tickets
1355        self.browser.open(self.payments_student_path)
1356        self.browser.getControl("Add online payment ticket").click()
1357        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1358        self.browser.getControl("Create ticket").click()
1359        self.assertMatches('...ticket created...',
1360                           self.browser.contents)
1361        ctrl = self.browser.getControl(name='val_id')
1362        value = ctrl.options[0]
1363        self.browser.getLink(value).click()
1364        self.assertMatches('...Amount Authorized...',
1365                           self.browser.contents)
1366
1367        # Students can open the callback view which simulates a valid callback
1368        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
1369        self.browser.open(self.browser.url + '/callback')
1370        self.assertMatches('...Valid callback received...',
1371                          self.browser.contents)
1372
1373        # Students can remove only online payment tickets which have
1374        # not received a valid callback
1375        self.browser.open(self.payments_student_path)
1376        self.assertRaises(
1377            LookupError, self.browser.getControl, name='val_id')
1378        self.browser.open(self.payments_student_path + '/addop')
1379        self.browser.getControl(name="form.p_category").value = ['gown']
1380        self.browser.getControl("Create ticket").click()
1381        self.browser.open(self.payments_student_path)
1382        ctrl = self.browser.getControl(name='val_id')
1383        value = ctrl.options[0]
1384        ctrl.getControl(value=value).selected = True
1385        self.browser.getControl("Remove selected", index=0).click()
1386        self.assertTrue('Successfully removed' in self.browser.contents)
1387
1388        # The new SFE-0 pin can be used for starting course registration
1389        IWorkflowInfo(self.student).fireTransition('request_clearance')
1390        IWorkflowInfo(self.student).fireTransition('clear')
1391        self.browser.open(self.studycourse_student_path)
1392        self.browser.getLink('Start course registration').click()
1393        pin = self.app['accesscodes']['SFE-0'].keys()[0]
1394        parts = pin.split('-')[1:]
1395        sfeseries, sfenumber = parts
1396        self.browser.getControl(name="ac_series").value = sfeseries
1397        self.browser.getControl(name="ac_number").value = sfenumber
1398        self.browser.getControl("Start course registration now").click()
1399        self.assertMatches('...Course registration has been started...',
1400                           self.browser.contents)
1401        self.assertTrue(self.student.state == 'school fee paid')
1402        return
1403
1404    def test_manage_accommodation(self):
1405        # Managers can add online booking fee payment tickets and open the
1406        # callback view (see test_manage_payments)
1407        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1408        self.browser.open(self.payments_student_path)
1409        self.browser.getControl("Add online payment ticket").click()
1410        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1411        # If student is not in accommodation session, payment cannot be processed
1412        self.app['configuration'].accommodation_session = 2011
1413        self.browser.getControl("Create ticket").click()
1414        self.assertMatches('...Your current session does not match...',
1415                           self.browser.contents)
1416        self.app['configuration'].accommodation_session = 2004
1417        self.browser.getControl("Add online payment ticket").click()
1418        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1419        self.browser.getControl("Create ticket").click()
1420        ctrl = self.browser.getControl(name='val_id')
1421        value = ctrl.options[0]
1422        self.browser.getLink(value).click()
1423        self.browser.open(self.browser.url + '/callback')
1424        # The new HOS-0 pin has been created
1425        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1426        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1427        ac = self.app['accesscodes']['HOS-0'][pin]
1428        ac.owner = self.student_id
1429        parts = pin.split('-')[1:]
1430        sfeseries, sfenumber = parts
1431        # Managers can use HOS code and book a bed space with it
1432        self.browser.open(self.acco_student_path)
1433        self.browser.getLink("Book accommodation").click()
1434        self.assertMatches('...You are in the wrong...',
1435                           self.browser.contents)
1436        IWorkflowInfo(self.student).fireTransition('admit')
1437        # An existing HOS code can only be used if students
1438        # are in accommodation session
1439        self.student['studycourse'].current_session = 2003
1440        self.browser.getLink("Book accommodation").click()
1441        self.assertMatches('...Your current session does not match...',
1442                           self.browser.contents)
1443        self.student['studycourse'].current_session = 2004
1444        # All requirements are met and ticket can be created
1445        self.browser.getLink("Book accommodation").click()
1446        self.assertMatches('...Activation Code:...',
1447                           self.browser.contents)
1448        self.browser.getControl(name="ac_series").value = sfeseries
1449        self.browser.getControl(name="ac_number").value = sfenumber
1450        self.browser.getControl("Create bed ticket").click()
1451        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1452                           self.browser.contents)
1453        # Bed has been allocated
1454        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
1455        self.assertTrue(bed1.owner == self.student_id)
1456        # BedTicketAddPage is now blocked
1457        self.browser.getLink("Book accommodation").click()
1458        self.assertMatches('...You already booked a bed space...',
1459            self.browser.contents)
1460        # The bed ticket displays the data correctly
1461        self.browser.open(self.acco_student_path + '/2004')
1462        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1463                           self.browser.contents)
1464        self.assertMatches('...2004/2005...', self.browser.contents)
1465        self.assertMatches('...regular_male_fr...', self.browser.contents)
1466        self.assertMatches('...%s...' % pin, self.browser.contents)
1467        # Managers can relocate students if the student's bed_type has changed
1468        self.browser.getLink("Relocate student").click()
1469        self.assertMatches(
1470            "...Student can't be relocated...", self.browser.contents)
1471        self.student.sex = u'f'
1472        self.browser.getLink("Relocate student").click()
1473        self.assertMatches(
1474            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1475        self.assertTrue(bed1.owner == NOT_OCCUPIED)
1476        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1477        self.assertTrue(bed2.owner == self.student_id)
1478        self.assertTrue(self.student['accommodation'][
1479            '2004'].bed_type == u'regular_female_fr')
1480        # The payment object still shows the original payment item
1481        payment_id = self.student['payments'].keys()[0]
1482        payment = self.student['payments'][payment_id]
1483        self.assertTrue(payment.p_item == u'regular_male_fr')
1484        # Managers can relocate students if the bed's bed_type has changed
1485        bed1.bed_type = u'regular_female_fr'
1486        bed2.bed_type = u'regular_male_fr'
1487        notify(grok.ObjectModifiedEvent(bed1))
1488        notify(grok.ObjectModifiedEvent(bed2))
1489        self.browser.getLink("Relocate student").click()
1490        self.assertMatches(
1491            "...Student relocated...", self.browser.contents)
1492        self.assertMatches(
1493            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
1494        self.assertMatches(bed1.owner, self.student_id)
1495        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1496        # Managers can't relocate students if bed is reserved
1497        self.student.sex = u'm'
1498        bed1.bed_type = u'regular_female_reserved'
1499        notify(grok.ObjectModifiedEvent(bed1))
1500        self.browser.getLink("Relocate student").click()
1501        self.assertMatches(
1502            "...Students in reserved beds can't be relocated...",
1503            self.browser.contents)
1504        # Managers can relocate students if booking has been cancelled but
1505        # other bed space has been manually allocated after cancellation
1506        old_owner = bed1.releaseBed()
1507        self.assertMatches(old_owner, self.student_id)
1508        bed2.owner = self.student_id
1509        self.browser.open(self.acco_student_path + '/2004')
1510        self.assertMatches(
1511            "...booking cancelled...", self.browser.contents)
1512        self.browser.getLink("Relocate student").click()
1513        # We didn't informed the catalog therefore the new owner is not found
1514        self.assertMatches(
1515            "...There is no free bed in your category regular_male_fr...",
1516            self.browser.contents)
1517        # Now we fire the event properly
1518        notify(grok.ObjectModifiedEvent(bed2))
1519        self.browser.getLink("Relocate student").click()
1520        self.assertMatches(
1521            "...Student relocated...", self.browser.contents)
1522        self.assertMatches(
1523            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1524          # Managers can delete bed tickets
1525        self.browser.open(self.acco_student_path)
1526        ctrl = self.browser.getControl(name='val_id')
1527        value = ctrl.options[0]
1528        ctrl.getControl(value=value).selected = True
1529        self.browser.getControl("Remove selected", index=0).click()
1530        self.assertMatches('...Successfully removed...', self.browser.contents)
1531        # The bed has been properly released by the event handler
1532        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1533        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1534        return
1535
1536    def test_student_accommodation(self):
1537        # Login
1538        self.browser.open(self.login_path)
1539        self.browser.getControl(name="form.login").value = self.student_id
1540        self.browser.getControl(name="form.password").value = 'spwd'
1541        self.browser.getControl("Login").click()
1542
1543        # Students can add online booking fee payment tickets and open the
1544        # callback view (see test_manage_payments)
1545        self.browser.getLink("Payments").click()
1546        self.browser.getControl("Add online payment ticket").click()
1547        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1548        self.browser.getControl("Create ticket").click()
1549        ctrl = self.browser.getControl(name='val_id')
1550        value = ctrl.options[0]
1551        self.browser.getLink(value).click()
1552        self.browser.open(self.browser.url + '/callback')
1553        # The new HOS-0 pin has been created
1554        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1555        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1556        ac = self.app['accesscodes']['HOS-0'][pin]
1557        ac.owner = u'Anybody'
1558        parts = pin.split('-')[1:]
1559        sfeseries, sfenumber = parts
1560
1561        # Students can use HOS code and book a bed space with it
1562        self.browser.open(self.acco_student_path)
1563        self.browser.getLink("Book accommodation").click()
1564        self.assertMatches('...You are in the wrong...',
1565                           self.browser.contents)
1566        IWorkflowInfo(self.student).fireTransition('admit')
1567        self.browser.getLink("Book accommodation").click()
1568        self.assertMatches('...Activation Code:...',
1569                           self.browser.contents)
1570        self.browser.getControl(name="ac_series").value = u'nonsense'
1571        self.browser.getControl(name="ac_number").value = sfenumber
1572        self.browser.getControl("Create bed ticket").click()
1573        self.assertMatches('...Activation code is invalid...',
1574                           self.browser.contents)
1575        self.browser.getControl(name="ac_series").value = sfeseries
1576        self.browser.getControl(name="ac_number").value = sfenumber
1577        self.browser.getControl("Create bed ticket").click()
1578        self.assertMatches('...You are not the owner of this access code...',
1579                           self.browser.contents)
1580        ac.owner = self.student_id
1581        self.browser.getControl(name="ac_series").value = sfeseries
1582        self.browser.getControl(name="ac_number").value = sfenumber
1583        self.browser.getControl("Create bed ticket").click()
1584        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1585                           self.browser.contents)
1586
1587        # Bed has been allocated
1588        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
1589        self.assertTrue(bed.owner == self.student_id)
1590
1591        # BedTicketAddPage is now blocked
1592        self.browser.getLink("Book accommodation").click()
1593        self.assertMatches('...You already booked a bed space...',
1594            self.browser.contents)
1595
1596        # The bed ticket displays the data correctly
1597        self.browser.open(self.acco_student_path + '/2004')
1598        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1599                           self.browser.contents)
1600        self.assertMatches('...2004/2005...', self.browser.contents)
1601        self.assertMatches('...regular_male_fr...', self.browser.contents)
1602        self.assertMatches('...%s...' % pin, self.browser.contents)
1603
1604        # Students can open the pdf slip
1605        self.browser.open(self.browser.url + '/bed_allocation.pdf')
1606        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1607        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1608
1609        # Students can't relocate themselves
1610        self.assertFalse('Relocate' in self.browser.contents)
1611        relocate_path = self.acco_student_path + '/2004/relocate'
1612        self.assertRaises(
1613            Unauthorized, self.browser.open, relocate_path)
1614
1615        # Students can't the Remove button and check boxes
1616        self.browser.open(self.acco_student_path)
1617        self.assertFalse('Remove' in self.browser.contents)
1618        self.assertFalse('val_id' in self.browser.contents)
1619        return
1620
1621    def test_change_password_request(self):
1622        self.browser.open('http://localhost/app/changepw')
1623        self.browser.getControl(name="form.reg_number").value = '123'
1624        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1625        self.browser.getControl("Get new login credentials").click()
1626        self.assertTrue('An email with' in self.browser.contents)
1627
1628    def test_reindex(self):
1629        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1630        self.browser.open('http://localhost/app/reindex')
1631        self.assertTrue('No catalog name provided' in self.browser.contents)
1632        self.browser.open('http://localhost/app/reindex?ctlg=xyz')
1633        self.assertTrue('xyz_catalog does not exist' in self.browser.contents)
1634        cat = queryUtility(ICatalog, name='students_catalog')
1635        results = cat.searchResults(student_id=(None, None))
1636        self.assertEqual(len(results),1)
1637        cat.clear()
1638        results = cat.searchResults(student_id=(None, None))
1639        self.assertEqual(len(results),0)
1640        self.browser.open('http://localhost/app/reindex?ctlg=students')
1641        self.assertTrue('1 students re-indexed' in self.browser.contents)
1642        results = cat.searchResults(student_id=(None, None))
1643        self.assertEqual(len(results),1)
Note: See TracBrowser for help on using the repository browser.