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

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

Add browser test for reindex view.

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