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

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

Include permission waeup.uploadStudentFile to handle file uploads.

Fix pagetemplate filesuploadpage.pt.

Rearrange fileupload.pt

  • Property svn:keywords set to Id
File size: 62.4 KB
Line 
1##
2## test_browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Tue Mar 29 11:31:11 2011 Uli Fouquet
5## $Id: test_browser.py 7127 2011-11-17 13:55:14Z henrik $
6##
7## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""
23Test the student-related UI components.
24"""
25import shutil
26import tempfile
27from StringIO import StringIO
28from datetime import datetime
29import grok
30from zope.event import notify
31from zope.component import createObject
32from zope.component.hooks import setSite, clearSite
33from zope.security.interfaces import Unauthorized
34from zope.testbrowser.testing import Browser
35from hurry.workflow.interfaces import IWorkflowInfo
36from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
37from waeup.sirp.app import University
38from waeup.sirp.configuration import SessionConfiguration
39from waeup.sirp.students.student import Student
40from waeup.sirp.university.faculty import Faculty
41from waeup.sirp.university.department import Department
42from waeup.sirp.interfaces import IUserAccount
43from waeup.sirp.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
44
45PH_LEN = 2059  # Length of placeholder file
46
47def lookup_submit_value(name, value, browser):
48    """Find a button with a certain value."""
49    for num in range(0, 100):
50        try:
51            button = browser.getControl(name=name, index=num)
52            if button.value.endswith(value):
53                return button
54        except IndexError:
55            break
56    return None
57
58class StudentsFullSetup(FunctionalTestCase):
59    # A test case that only contains a setup and teardown
60    #
61    # Complete setup for students handlings is rather complex and
62    # requires lots of things created before we can start. This is a
63    # setup that does all this, creates a university, creates PINs,
64    # etc.  so that we do not have to bother with that in different
65    # test cases.
66
67    layer = FunctionalLayer
68
69    def setUp(self):
70        super(StudentsFullSetup, self).setUp()
71
72        # Setup a sample site for each test
73        app = University()
74        self.dc_root = tempfile.mkdtemp()
75        app['datacenter'].setStoragePath(self.dc_root)
76
77        # Prepopulate the ZODB...
78        self.getRootFolder()['app'] = app
79        # we add the site immediately after creation to the
80        # ZODB. Catalogs and other local utilities are not setup
81        # before that step.
82        self.app = self.getRootFolder()['app']
83        # Set site here. Some of the following setup code might need
84        # to access grok.getSite() and should get our new app then
85        setSite(app)
86
87        # Add student with subobjects
88        student = Student()
89        student.fullname = u'Anna Tester'
90        student.reg_number = u'123'
91        student.matric_number = u'234'
92        student.sex = u'm'
93        self.app['students'].addStudent(student)
94        self.student_id = student.student_id
95        self.student = self.app['students'][self.student_id]
96
97        # Set password
98        IUserAccount(
99            self.app['students'][self.student_id]).setPassword('spwd')
100
101        self.login_path = 'http://localhost/app/login'
102        self.container_path = 'http://localhost/app/students'
103        self.manage_container_path = self.container_path + '/@@manage'
104        self.add_student_path = self.container_path + '/addstudent'
105        self.student_path = self.container_path + '/' + self.student_id
106        self.manage_student_path = self.student_path + '/edit_base'
107        self.clearance_student_path = self.student_path + '/view_clearance'
108        self.personal_student_path = self.student_path + '/view_personal'
109        self.edit_clearance_student_path = self.student_path + '/edit_clearance'
110        self.edit_personal_student_path = self.student_path + '/edit_personal'
111        self.studycourse_student_path = self.student_path + '/studycourse'
112        self.payments_student_path = self.student_path + '/payments'
113        self.acco_student_path = self.student_path + '/accommodation'
114        self.history_student_path = self.student_path + '/history'
115
116        # Create 5 access codes with prefix'PWD'
117        pin_container = self.app['accesscodes']
118        pin_container.createBatch(
119            datetime.now(), 'some_userid', 'PWD', 9.99, 5)
120        pins = pin_container['PWD-1'].values()
121        self.pwdpins = [x.representation for x in pins]
122        self.existing_pwdpin = self.pwdpins[0]
123        parts = self.existing_pwdpin.split('-')[1:]
124        self.existing_pwdseries, self.existing_pwdnumber = parts
125        # Create 5 access codes with prefix 'CLR'
126        pin_container.createBatch(
127            datetime.now(), 'some_userid', 'CLR', 9.99, 5)
128        pins = pin_container['CLR-1'].values()
129        pins[0].owner = u'Hans Wurst'
130        self.existing_clrac = pins[0]
131        self.existing_clrpin = pins[0].representation
132        parts = self.existing_clrpin.split('-')[1:]
133        self.existing_clrseries, self.existing_clrnumber = parts
134        # Create 2 access codes with prefix 'HOS'
135        pin_container.createBatch(
136            datetime.now(), 'some_userid', 'HOS', 9.99, 2)
137        pins = pin_container['HOS-1'].values()
138        self.existing_hosac = pins[0]
139        self.existing_hospin = pins[0].representation
140        parts = self.existing_hospin.split('-')[1:]
141        self.existing_hosseries, self.existing_hosnumber = parts
142
143        # Populate university
144        self.certificate = createObject('waeup.Certificate')
145        self.certificate.code = u'CERT1'
146        self.certificate.application_category = 'basic'
147        self.certificate.study_mode = 'ug_ft'
148        self.certificate.start_level = 100
149        self.certificate.end_level = 500
150        self.app['faculties']['fac1'] = Faculty()
151        self.app['faculties']['fac1']['dep1'] = Department()
152        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
153            self.certificate)
154        self.course = createObject('waeup.Course')
155        self.course.code = 'COURSE1'
156        self.course.semester = 1
157        self.course.credits = 10
158        self.course.passmark = 40
159        self.app['faculties']['fac1']['dep1'].courses.addCourse(
160            self.course)
161        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCourseRef(
162            self.course, level=100)
163
164        # Configure university
165        self.app['configuration'].accommodation_states = ['admitted']
166        self.app['configuration'].accommodation_session = 2004
167        configuration = SessionConfiguration()
168        # These attributes must also exist in the customization packages.
169        configuration.academic_session = 2004
170        configuration.fee_1 = 20000
171        configuration.boocking_fee = 500
172        self.app['configuration'].addSessionConfiguration(configuration)
173
174        # Create a hostel with two beds
175        hostel = Hostel()
176        hostel.hostel_id = u'hall-1'
177        hostel.hostel_name = u'Hall 1'
178        self.app['hostels'].addHostel(hostel)
179        bed = Bed()
180        bed.bed_id = u'hall-1_A_101_A'
181        bed.bed_number = 1
182        bed.owner = NOT_OCCUPIED
183        bed.bed_type = u'regular_male_fr'
184        self.app['hostels'][hostel.hostel_id].addBed(bed)
185        bed = Bed()
186        bed.bed_id = u'hall-1_A_101_B'
187        bed.bed_number = 2
188        bed.owner = NOT_OCCUPIED
189        bed.bed_type = u'regular_female_fr'
190        self.app['hostels'][hostel.hostel_id].addBed(bed)
191
192        # Set study course attributes of test student
193        self.student['studycourse'].certificate = self.certificate
194        self.student['studycourse'].current_session = 2004
195        self.student['studycourse'].entry_session = 2004
196        self.student['studycourse'].current_verdict = 'A'
197        self.student['studycourse'].current_level = 100
198
199        # Put the prepopulated site into test ZODB and prepare test
200        # browser
201        self.browser = Browser()
202        self.browser.handleErrors = False
203
204    def tearDown(self):
205        super(StudentsFullSetup, self).tearDown()
206        clearSite()
207        shutil.rmtree(self.dc_root)
208
209
210
211class StudentsContainerUITests(StudentsFullSetup):
212    # Tests for StudentsContainer class views and pages
213
214    layer = FunctionalLayer
215
216    def test_anonymous_access(self):
217        # Anonymous users can't access students containers
218        self.assertRaises(
219            Unauthorized, self.browser.open, self.container_path)
220        self.assertRaises(
221            Unauthorized, self.browser.open, self.manage_container_path)
222        return
223
224    def test_manage_access(self):
225        # Managers can access the view page of students
226        # containers and can perform actions
227        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
228        self.browser.open(self.container_path)
229        self.assertEqual(self.browser.headers['Status'], '200 Ok')
230        self.assertEqual(self.browser.url, self.container_path)
231        self.browser.getLink("Manage student section").click()
232        self.assertEqual(self.browser.headers['Status'], '200 Ok')
233        self.assertEqual(self.browser.url, self.manage_container_path)
234        return
235
236    def test_add_search_delete_students(self):
237        # Managers can add search and remove students
238        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
239        self.browser.open(self.manage_container_path)
240        self.browser.getLink("Add student").click()
241        self.assertEqual(self.browser.headers['Status'], '200 Ok')
242        self.assertEqual(self.browser.url, self.add_student_path)
243        self.browser.getControl(name="form.fullname").value = 'Bob Tester'
244        self.browser.getControl("Create student record").click()
245        self.assertTrue('Student record created' in self.browser.contents)
246
247        # Registration and matric numbers must be unique
248        self.browser.getLink("Manage").click()
249        self.browser.getControl(name="form.reg_number").value = '123'
250        self.browser.getControl("Save").click()
251        self.assertMatches('...Registration number exists...',
252                           self.browser.contents)
253        self.browser.getControl(name="form.reg_number").value = '789'
254        self.browser.getControl(name="form.matric_number").value = '234'
255        self.browser.getControl("Save").click()
256        self.assertMatches('...Matriculation number exists...',
257                           self.browser.contents)
258
259        self.browser.open(self.container_path)
260        self.browser.getControl("Search").click()
261        self.assertTrue('Empty search string' in self.browser.contents)
262        self.browser.getControl(name="searchtype").value = ['student_id']
263        self.browser.getControl(name="searchterm").value = self.student_id
264        self.browser.getControl("Search").click()
265        self.assertTrue('Anna Tester' in self.browser.contents)
266
267        self.browser.open(self.manage_container_path)
268        self.browser.getControl("Search").click()
269        self.assertTrue('Empty search string' in self.browser.contents)
270        self.browser.getControl(name="searchtype").value = ['fullname']
271        self.browser.getControl(name="searchterm").value = 'Anna Tester'
272        self.browser.getControl("Search").click()
273        self.assertTrue('Anna Tester' in self.browser.contents)
274        # The old searchterm will be used again
275        self.browser.getControl("Search").click()
276        self.assertTrue('Anna Tester' in self.browser.contents)
277
278        ctrl = self.browser.getControl(name='entries')
279        ctrl.getControl(value=self.student_id).selected = True
280        self.browser.getControl("Remove selected", index=0).click()
281        self.assertTrue('Successfully removed' in self.browser.contents)
282        self.browser.getControl(name="searchtype").value = ['student_id']
283        self.browser.getControl(name="searchterm").value = self.student_id
284        self.browser.getControl("Search").click()
285        self.assertTrue('No student found' in self.browser.contents)
286
287        self.browser.open(self.container_path)
288        self.browser.getControl(name="searchtype").value = ['student_id']
289        self.browser.getControl(name="searchterm").value = self.student_id
290        self.browser.getControl("Search").click()
291        self.assertTrue('No student found' in self.browser.contents)
292        return
293
294class StudentUITests(StudentsFullSetup):
295    # Tests for Student class views and pages
296
297    layer = FunctionalLayer
298
299    def test_manage_access(self):
300        # Managers can access the pages of students
301        # and can perform actions
302        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
303
304        self.browser.open(self.student_path)
305        self.assertEqual(self.browser.headers['Status'], '200 Ok')
306        self.assertEqual(self.browser.url, self.student_path)
307        self.browser.getLink("Manage").click()
308        self.assertEqual(self.browser.headers['Status'], '200 Ok')
309        self.assertEqual(self.browser.url, self.manage_student_path)
310        # Managers can edit base data and fire transitions
311        self.browser.getControl(name="transition").value = ['admit']
312        self.browser.getControl(name="form.fullname").value = 'John Tester'
313        self.browser.getControl(name="form.reg_number").value = '345'
314        self.browser.getControl(name="password").value = 'secret'
315        self.browser.getControl(name="control_password").value = 'secret'
316        self.browser.getControl("Save").click()
317        self.assertMatches('...Form has been saved...',
318                           self.browser.contents)
319        self.browser.open(self.student_path)
320        self.browser.getLink("Clearance Data").click()
321        self.assertEqual(self.browser.headers['Status'], '200 Ok')
322        self.assertEqual(self.browser.url, self.clearance_student_path)
323        self.browser.getLink("Manage").click()
324        self.assertEqual(self.browser.headers['Status'], '200 Ok')
325        self.assertEqual(self.browser.url, self.edit_clearance_student_path)
326        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
327        self.browser.getControl("Save").click()
328        self.assertMatches('...Form has been saved...',
329                           self.browser.contents)
330
331        self.browser.open(self.student_path)
332        self.browser.getLink("Personal Data").click()
333        self.assertEqual(self.browser.headers['Status'], '200 Ok')
334        self.assertEqual(self.browser.url, self.personal_student_path)
335        self.browser.getLink("Manage").click()
336        self.assertEqual(self.browser.headers['Status'], '200 Ok')
337        self.assertEqual(self.browser.url, self.edit_personal_student_path)
338        self.browser.getControl("Save").click()
339        self.assertMatches('...Form has been saved...',
340                           self.browser.contents)
341
342        # Managers can browse all subobjects
343        self.browser.open(self.student_path)
344        self.browser.getLink("Payments").click()
345        self.assertEqual(self.browser.headers['Status'], '200 Ok')
346        self.assertEqual(self.browser.url, self.payments_student_path)
347        self.browser.open(self.student_path)
348        self.browser.getLink("Accommodation").click()
349        self.assertEqual(self.browser.headers['Status'], '200 Ok')
350        self.assertEqual(self.browser.url, self.acco_student_path)
351        self.browser.open(self.student_path)
352        self.browser.getLink("History").click()
353        self.assertEqual(self.browser.headers['Status'], '200 Ok')
354        self.assertEqual(self.browser.url, self.history_student_path)
355        self.assertMatches('...Student admitted by zope.mgr...',
356                           self.browser.contents)
357        return
358
359    def test_manage_upload_file(self):
360        # Managers can upload a file via the StudentClearanceManageFormPage
361        # The image is stored even if form has errors
362        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
363        self.browser.open(self.edit_clearance_student_path)
364        # No birth certificate has been uploaded yet
365        # Browsing the link shows a placerholder image
366        self.browser.open('birth_certificate')
367        self.assertEqual(
368            self.browser.headers['content-type'], 'image/jpeg')
369        self.assertEqual(len(self.browser.contents), PH_LEN)
370        # Create a pseudo image file and select it to be uploaded in form
371        # as birth certificate
372        self.browser.open(self.edit_clearance_student_path)
373        pseudo_image = StringIO('I pretend to be a graphics file')
374        ctrl = self.browser.getControl(name='birthcertificateupload')
375        file_ctrl = ctrl.mech_control
376        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.jpg')
377        # The Save action does not upload files
378        self.browser.getControl("Save (no upload)").click() # submit form
379        self.assertFalse(
380            '<a target="image" href="birth_certificate">'
381            in self.browser.contents)
382        # ... but the correct upload submit button does
383        pseudo_image = StringIO('I pretend to be a graphics file')
384        ctrl = self.browser.getControl(name='birthcertificateupload')
385        file_ctrl = ctrl.mech_control
386        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.jpg')
387        self.browser.getControl(
388            name='upload_birthcertificateupload').click()
389        # There is a correct <img> link included
390        self.assertTrue(
391            '<a target="image" href="birth_certificate">'
392            in self.browser.contents)
393
394        # Browsing the link shows a real image
395        self.browser.open('birth_certificate')
396        self.assertEqual(
397            self.browser.headers['content-type'], 'image/jpeg')
398        self.assertEqual(len(self.browser.contents), 31)
399        # Reuploading a file which is bigger than 150k will raise an error
400        self.browser.open(self.edit_clearance_student_path)
401        photo_content = 'A' * 1024 * 151  # A string of 21 KB size
402        pseudo_image = StringIO(photo_content)
403        ctrl = self.browser.getControl(name='birthcertificateupload')
404        file_ctrl = ctrl.mech_control
405        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.jpg')
406        self.browser.getControl(
407            name='upload_birthcertificateupload').click()
408        self.assertTrue(
409            'Uploaded file is too big' in self.browser.contents)
410        # File names must meet several conditions
411        pseudo_image = StringIO('I pretend to be a graphics file')
412        ctrl = self.browser.getControl(name='birthcertificateupload')
413        file_ctrl = ctrl.mech_control
414        file_ctrl.add_file(pseudo_image, filename='my.photo.jpg')
415        self.browser.getControl(
416            name='upload_birthcertificateupload').click()
417        self.assertTrue('File name contains more than one dot'
418            in self.browser.contents)
419        ctrl = self.browser.getControl(name='birthcertificateupload')
420        file_ctrl = ctrl.mech_control
421        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate')
422        self.browser.getControl(
423            name='upload_birthcertificateupload').click()
424        self.assertTrue('File name has no extension' in self.browser.contents)
425        ctrl = self.browser.getControl(name='birthcertificateupload')
426        file_ctrl = ctrl.mech_control
427        file_ctrl.add_file(pseudo_image, filename='my_birth_certificate.bmp')
428        self.browser.getControl(
429            name='upload_birthcertificateupload').click()
430        self.assertTrue('Only the following extension are allowed'
431            in self.browser.contents)
432        # Managers can delete files
433        self.browser.getControl(name='delete_birthcertificateupload').click()
434        self.assertTrue(
435            'birth_certificate deleted' in self.browser.contents)
436        # Managers can add and delete second file
437        self.browser.open(self.edit_clearance_student_path)
438        pseudo_image = StringIO('I pretend to be a graphics file')
439        ctrl = self.browser.getControl(name='birthcertificateupload')
440        file_ctrl = ctrl.mech_control
441        file_ctrl.add_file(pseudo_image, filename='my_acceptance_letter.jpg')
442        self.browser.getControl(
443            name='upload_acceptanceletterupload').click()
444        self.assertFalse(
445            '<a target="image" href="acceptance_letter">'
446            in self.browser.contents)
447        ctrl = self.browser.getControl(name='acceptanceletterupload')
448        file_ctrl = ctrl.mech_control
449        file_ctrl.add_file(pseudo_image, filename='my_acceptance_letter.jpg')
450        self.browser.getControl(
451            name='upload_acceptanceletterupload').click()
452        self.assertTrue(
453            '<a target="image" href="acceptance_letter">'
454            in self.browser.contents)
455        self.browser.getControl(
456            name='delete_acceptanceletterupload').click()
457        self.assertTrue(
458            'acceptance_letter deleted'
459            in self.browser.contents)
460        # Managers can upload a file via the StudentBaseManageFormPage
461        self.browser.open(self.manage_student_path)
462        pseudo_image = StringIO('I pretend to be a graphics file')
463        ctrl = self.browser.getControl(name='passportuploadmanage')
464        file_ctrl = ctrl.mech_control
465        file_ctrl.add_file(pseudo_image, filename='my_photo.bmp')
466        self.browser.getControl(
467            name='upload_passportuploadmanage').click()
468        self.assertTrue('jpg file extension expected'
469            in self.browser.contents)
470        ctrl = self.browser.getControl(name='passportuploadmanage')
471        file_ctrl = ctrl.mech_control
472        file_ctrl.add_file(pseudo_image, filename='my_photo.jpg')
473        self.browser.getControl(
474            name='upload_passportuploadmanage').click()
475        self.assertTrue(
476            '<img align="middle" height="125px" src="passport.jpg" />'
477            in self.browser.contents)
478
479    def test_manage_course_lists(self):
480        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
481        self.browser.open(self.student_path)
482        self.browser.getLink("Study Course").click()
483        self.assertEqual(self.browser.headers['Status'], '200 Ok')
484        self.assertEqual(self.browser.url, self.studycourse_student_path)
485        self.browser.getLink("Manage").click()
486        self.assertTrue('Manage study course' in self.browser.contents)
487        # Before we can select a level, the certificate must
488        # be selected and saved
489        self.browser.getControl(name="form.certificate").value = ['CERT1']
490        self.browser.getControl(name="form.current_session").value = ['2004']
491        self.browser.getControl(name="form.current_verdict").value = ['A']
492        self.browser.getControl("Save").click()
493        # Now we can save also the current level which depends on start and end
494        # level of the certificate
495        self.browser.getControl(name="form.current_level").value = ['100']
496        self.browser.getControl("Save").click()
497        # Managers can add and remove any study level (course list)
498        self.browser.getControl(name="addlevel").value = ['100']
499        self.browser.getControl("Add study level").click()
500        self.assertMatches('...<span>100</span>...', self.browser.contents)
501        self.browser.getControl("Add study level").click()
502        self.assertMatches('...This level exists...', self.browser.contents)
503        self.browser.getControl("Remove selected").click()
504        self.assertMatches(
505            '...No study level selected...', self.browser.contents)
506        self.browser.getControl(name="val_id").value = ['100']
507        self.browser.getControl("Remove selected").click()
508        self.assertMatches('...Successfully removed...', self.browser.contents)
509        # Add level again
510        self.browser.getControl(name="addlevel").value = ['100']
511        self.browser.getControl("Add study level").click()
512        self.browser.getControl(name="addlevel").value = ['100']
513
514        # Managers can view and manage course lists
515        self.browser.getLink("100").click()
516        self.assertMatches(
517            '...: Study Level 100 (Year 1)...', self.browser.contents)
518        self.browser.getLink("Manage").click()
519        self.browser.getControl(name="form.level_session").value = ['2002']
520        self.browser.getControl("Save").click()
521        self.browser.getControl("Remove selected").click()
522        self.assertMatches('...No ticket selected...', self.browser.contents)
523        ctrl = self.browser.getControl(name='val_id')
524        ctrl.getControl(value='COURSE1').selected = True
525        self.browser.getControl("Remove selected", index=0).click()
526        self.assertTrue('Successfully removed' in self.browser.contents)
527        self.browser.getControl("Add course ticket").click()
528        self.browser.getControl(name="form.course").value = ['COURSE1']
529        self.browser.getControl("Add course ticket").click()
530        self.assertTrue('Successfully added' in self.browser.contents)
531        self.browser.getControl("Add course ticket").click()
532        self.browser.getControl(name="form.course").value = ['COURSE1']
533        self.browser.getControl("Add course ticket").click()
534        self.assertTrue('The ticket exists' in self.browser.contents)
535        self.browser.getControl("Cancel").click()
536        self.browser.getLink("COURSE1").click()
537        self.browser.getLink("Manage").click()
538        self.browser.getControl(name="form.score").value = '10'
539        self.browser.getControl("Save").click()
540        self.assertTrue('Form has been saved' in self.browser.contents)
541        return
542
543    def test_manage_workflow(self):
544        # Managers can pass through the whole workflow
545        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
546        student = self.app['students'][self.student_id]
547        self.browser.open(self.manage_student_path)
548        self.assertTrue(student.clearance_locked)
549        self.browser.getControl(name="transition").value = ['admit']
550        self.browser.getControl("Save").click()
551        self.assertTrue(student.clearance_locked)
552        self.browser.getControl(name="transition").value = ['start_clearance']
553        self.browser.getControl("Save").click()
554        self.assertFalse(student.clearance_locked)
555        self.browser.getControl(name="transition").value = ['request_clearance']
556        self.browser.getControl("Save").click()
557        self.assertTrue(student.clearance_locked)
558        self.browser.getControl(name="transition").value = ['clear']
559        self.browser.getControl("Save").click()
560        self.browser.getControl(
561            name="transition").value = ['pay_first_school_fee']
562        self.browser.getControl("Save").click()
563        self.browser.getControl(name="transition").value = ['reset6']
564        self.browser.getControl("Save").click()
565        # In state returning the pay_school_fee transition triggers some
566        # changes of attributes
567        self.browser.getControl(name="transition").value = ['pay_school_fee']
568        self.browser.getControl("Save").click()
569        self.assertEqual(student['studycourse'].current_session, 2005) # +1
570        self.assertEqual(student['studycourse'].current_level, 200) # +100
571        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = not set
572        self.assertEqual(student['studycourse'].previous_verdict, 'A')
573        self.browser.getControl(name="transition").value = ['register_courses']
574        self.browser.getControl("Save").click()
575        self.browser.getControl(name="transition").value = ['validate_courses']
576        self.browser.getControl("Save").click()
577        self.browser.getControl(name="transition").value = ['return']
578        self.browser.getControl("Save").click()
579        return
580
581    def test_manage_import(self):
582        # Managers can import student data files
583        datacenter_path = 'http://localhost/app/datacenter'
584        # Prepare a csv file for students
585        open('students.csv', 'wb').write(
586"""firstname,lastname,fullname,reg_number,date_of_birth,matric_number
587Aaren,Pieri,Aaren Pieri,1,1990-01-02,100000
588Claus,Finau,Claus Finau,2,1990-01-03,100001
589Brit,Berson,Brit Berson,3,1990-01-04,100001
590""")
591        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
592        self.browser.open(datacenter_path)
593        self.browser.getLink('Upload CSV file').click()
594        filecontents = StringIO(open('students.csv', 'rb').read())
595        filewidget = self.browser.getControl(name='uploadfile:file')
596        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
597        self.browser.getControl(name='SUBMIT').click()
598        self.browser.getLink('Batch processing').click()
599        button = lookup_submit_value(
600            'select', 'students_zope.mgr.csv', self.browser)
601        button.click()
602        importerselect = self.browser.getControl(name='importer')
603        modeselect = self.browser.getControl(name='mode')
604        importerselect.getControl('Student Importer').selected = True
605        modeselect.getControl(value='create').selected = True
606        self.browser.getControl('Proceed to step 3...').click()
607        self.assertTrue('Header fields OK' in self.browser.contents)
608        self.browser.getControl('Perform import...').click()
609        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
610        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
611        self.assertTrue('Batch processing finished' in self.browser.contents)
612        open('studycourses.csv', 'wb').write(
613"""reg_number,matric_number,certificate,current_session,current_level
6141,,CERT1,2008,100
615,100001,CERT1,2008,100
616,100002,CERT1,2008,100
617""")
618        self.browser.open(datacenter_path)
619        self.browser.getLink('Upload CSV file').click()
620        filecontents = StringIO(open('studycourses.csv', 'rb').read())
621        filewidget = self.browser.getControl(name='uploadfile:file')
622        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
623        self.browser.getControl(name='SUBMIT').click()
624        self.browser.getLink('Batch processing').click()
625        button = lookup_submit_value(
626            'select', 'studycourses_zope.mgr.csv', self.browser)
627        button.click()
628        importerselect = self.browser.getControl(name='importer')
629        modeselect = self.browser.getControl(name='mode')
630        importerselect.getControl(
631            'StudentStudyCourse Importer (update only)').selected = True
632        modeselect.getControl(value='create').selected = True
633        self.browser.getControl('Proceed to step 3...').click()
634        self.assertTrue('Update mode only' in self.browser.contents)
635        self.browser.getControl('Proceed to step 3...').click()
636        self.assertTrue('Header fields OK' in self.browser.contents)
637        self.browser.getControl('Perform import...').click()
638        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
639        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
640        return
641
642    def test_student_change_password(self):
643        # Students can change the password
644        self.browser.open(self.login_path)
645        self.browser.getControl(name="form.login").value = self.student_id
646        self.browser.getControl(name="form.password").value = 'spwd'
647        self.browser.getControl("Login").click()
648        self.assertEqual(self.browser.url, self.student_path)
649        self.assertTrue('You logged in' in self.browser.contents)
650        # Change password
651        self.browser.getLink("Change password").click()
652        self.browser.getControl(name="form.password").value = 'pw'
653        self.browser.getControl(
654            name="form.password_repeat").value = 'pw'
655        self.browser.getControl("Save").click()
656        self.assertTrue('Constraint not satisfied' in self.browser.contents)
657        self.browser.getControl(name="form.password").value = 'new_password'
658        self.browser.getControl(
659            name="form.password_repeat").value = 'new_passssword'
660        self.browser.getControl("Save").click()
661        self.assertTrue('passwords do not match' in self.browser.contents)
662        self.browser.getControl(name="form.password").value = 'new_password'
663        self.browser.getControl(
664            name="form.password_repeat").value = 'new_password'
665        self.browser.getControl("Save").click()
666        self.assertTrue('Form has been saved' in self.browser.contents)
667        # We are still logged in. Changing the password hasn't thrown us out.
668        self.browser.getLink("My Data").click()
669        self.assertEqual(self.browser.url, self.student_path)
670        # We can logout
671        self.browser.getLink("Logout").click()
672        self.assertTrue('You have been logged out' in self.browser.contents)
673        self.assertEqual(self.browser.url, 'http://localhost/app')
674        # We can login again with the new password
675        self.browser.getLink("Login").click()
676        self.browser.open(self.login_path)
677        self.browser.getControl(name="form.login").value = self.student_id
678        self.browser.getControl(name="form.password").value = 'new_password'
679        self.browser.getControl("Login").click()
680        self.assertEqual(self.browser.url, self.student_path)
681        self.assertTrue('You logged in' in self.browser.contents)
682        return
683
684    def test_setpassword(self):
685        # Set password for first-time access
686        student = Student()
687        student.reg_number = u'123456'
688        student.fullname = u'Klaus Tester'
689        self.app['students'].addStudent(student)
690        setpassword_path = 'http://localhost/app/setpassword'
691        student_path = 'http://localhost/app/students/%s' % student.student_id
692        self.browser.open(setpassword_path)
693        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
694        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
695        self.browser.getControl(name="reg_number").value = '223456'
696        self.browser.getControl("Show").click()
697        self.assertMatches('...No student found...',
698                           self.browser.contents)
699        self.browser.getControl(name="reg_number").value = '123456'
700        self.browser.getControl(name="ac_number").value = '999999'
701        self.browser.getControl("Show").click()
702        self.assertMatches('...Access code is invalid...',
703                           self.browser.contents)
704        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
705        self.browser.getControl("Show").click()
706        self.assertMatches('...Password has been set. Your Student Id is...',
707                           self.browser.contents)
708        self.browser.getControl("Show").click()
709        self.assertMatches(
710            '...Password has already been set. Your Student Id is...',
711            self.browser.contents)
712        existing_pwdpin = self.pwdpins[1]
713        parts = existing_pwdpin.split('-')[1:]
714        existing_pwdseries, existing_pwdnumber = parts
715        self.browser.getControl(name="ac_series").value = existing_pwdseries
716        self.browser.getControl(name="ac_number").value = existing_pwdnumber
717        self.browser.getControl(name="reg_number").value = '123456'
718        self.browser.getControl("Show").click()
719        self.assertMatches(
720            '...You are using the wrong Access Code...',
721            self.browser.contents)
722        # The student can login with the new credentials
723        self.browser.open(self.login_path)
724        self.browser.getControl(name="form.login").value = student.student_id
725        self.browser.getControl(
726            name="form.password").value = self.existing_pwdnumber
727        self.browser.getControl("Login").click()
728        self.assertEqual(self.browser.url, student_path)
729        self.assertTrue('You logged in' in self.browser.contents)
730        return
731
732    def test_student_access(self):
733        # Students can access their own objects
734        # and can perform actions
735        IWorkflowInfo(self.student).fireTransition('admit')
736        self.browser.open(self.login_path)
737        self.browser.getControl(name="form.login").value = self.student_id
738        self.browser.getControl(name="form.password").value = 'spwd'
739        self.browser.getControl("Login").click()
740        # Student can upload a passport picture
741        self.browser.open(self.student_path + '/change_portrait')
742        pseudo_image = StringIO('I pretend to be a graphics file')
743        ctrl = self.browser.getControl(name='passportuploadedit')
744        file_ctrl = ctrl.mech_control
745        file_ctrl.add_file(pseudo_image, filename='my_photo.jpg')
746        self.browser.getControl(
747            name='upload_passportuploadedit').click()
748        self.assertTrue(
749            '<img align="middle" height="125px" src="passport.jpg" />'
750            in self.browser.contents)
751        # Student can view the clearance data
752        self.browser.getLink("Clearance Data").click()
753        # Student can't open clearance edit form before starting clearance
754        self.browser.open(self.student_path + '/cedit')
755        self.assertMatches('...The requested form is locked...',
756                           self.browser.contents)
757        self.browser.getLink("Clearance Data").click()
758        self.browser.getLink("Start clearance").click()
759        self.browser.getControl(name="ac_series").value = '3'
760        self.browser.getControl(name="ac_number").value = '4444444'
761        self.browser.getControl("Start clearance now").click()
762        self.assertMatches('...Activation code is invalid...',
763                           self.browser.contents)
764        self.browser.getControl(name="ac_series").value = self.existing_clrseries
765        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
766        # Owner is Hans Wurst, AC can't be invalidated
767        self.browser.getControl("Start clearance now").click()
768        self.assertMatches('...You are not the owner of this access code...',
769                           self.browser.contents)
770        # Set the correct owner
771        self.existing_clrac.owner = self.student_id
772        self.browser.getControl("Start clearance now").click()
773        self.assertMatches('...Clearance process has been started...',
774                           self.browser.contents)
775        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
776        self.browser.getControl("Save", index=0).click()
777        # Student can view the clearance data
778        self.browser.getLink("Clearance Data").click()
779        # and go back to the edit form
780        self.browser.getLink("Edit").click()
781        self.browser.getControl("Save and request clearance").click()
782        self.browser.getControl(name="ac_series").value = self.existing_clrseries
783        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
784        self.browser.getControl("Request clearance now").click()
785        self.assertMatches('...Clearance has been requested...',
786                           self.browser.contents)
787        # Student can't reopen clearance form after requesting clearance
788        self.browser.open(self.student_path + '/cedit')
789        self.assertMatches('...The requested form is locked...',
790                           self.browser.contents)
791        # Student can't add study level if not in state 'school fee paid'
792        self.browser.open(self.student_path + '/studycourse/add')
793        self.assertMatches('...The requested form is locked...',
794                           self.browser.contents)
795        # ... and must be transferred first
796        IWorkflowInfo(self.student).fireTransition('clear')
797        IWorkflowInfo(self.student).fireTransition('pay_first_school_fee')
798        # Now students can add the current study level
799        self.browser.getLink("Study Course").click()
800        self.browser.getLink("Add course list").click()
801        self.assertMatches('...Add current level 100 (Year 1)...',
802                           self.browser.contents)
803        self.browser.getControl("Create course list now").click()
804        self.browser.getLink("100").click()
805        self.browser.getLink("Add and remove courses").click()
806        self.browser.getControl("Add course ticket").click()
807        self.browser.getControl(name="form.course").value = ['COURSE1']
808        self.browser.getControl("Add course ticket").click()
809        self.assertMatches('...The ticket exists...',
810                           self.browser.contents)
811        self.student['studycourse'].current_level = 200
812        self.browser.getLink("Study Course").click()
813        self.browser.getLink("Add course list").click()
814        self.assertMatches('...Add current level 200 (Year 2)...',
815                           self.browser.contents)
816        self.browser.getControl("Create course list now").click()
817        self.browser.getLink("200").click()
818        self.browser.getLink("Add and remove courses").click()
819        self.browser.getControl("Add course ticket").click()
820        self.browser.getControl(name="form.course").value = ['COURSE1']
821        self.browser.getControl("Add course ticket").click()
822        self.assertMatches('...Successfully added COURSE1...',
823                           self.browser.contents)
824        # Students can open the pdf course registration slip
825        self.browser.open(self.student_path + '/studycourse/200')
826        self.browser.getLink("Download course registration slip").click()
827        self.assertEqual(self.browser.headers['Status'], '200 Ok')
828        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
829        # Students can remove course tickets
830        self.browser.open(self.student_path + '/studycourse/200/edit')
831        self.browser.getControl("Remove selected", index=0).click()
832        self.assertTrue('No ticket selected' in self.browser.contents)
833        ctrl = self.browser.getControl(name='val_id')
834        ctrl.getControl(value='COURSE1').selected = True
835        self.browser.getControl("Remove selected", index=0).click()
836        self.assertTrue('Successfully removed' in self.browser.contents)
837        self.browser.getControl("Register course list").click()
838        self.assertTrue('Course list has been registered' in self.browser.contents)
839        self.assertEqual(self.student.state, 'courses registered')
840        return
841
842    def test_manage_payments(self):
843        # Managers can add online school fee payment tickets
844        # if certain requirements are met
845        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
846        self.browser.open(self.payments_student_path)
847        self.browser.getControl("Add online payment ticket").click()
848        self.browser.getControl(name="form.p_category").value = ['schoolfee']
849        self.browser.getControl("Create ticket").click()
850        self.assertMatches('...ticket created...',
851                           self.browser.contents)
852        ctrl = self.browser.getControl(name='val_id')
853        value = ctrl.options[0]
854        self.browser.getLink(value).click()
855        self.assertMatches('...Amount Authorized...',
856                           self.browser.contents)
857        payment_url = self.browser.url
858
859        # The pdf payment receipt can't yet be opened
860        self.browser.open(payment_url + '/payment_receipt.pdf')
861        self.assertMatches('...Ticket not yet paid...',
862                           self.browser.contents)
863
864        # The same payment ticket (with same p_item, p_session and p_category)
865        # can't be added twice.
866        self.browser.open(self.payments_student_path)
867        self.browser.getControl("Add online payment ticket").click()
868        self.browser.getControl(name="form.p_category").value = ['schoolfee']
869        self.browser.getControl("Create ticket").click()
870        self.assertMatches('...This payment ticket already exists...',
871                           self.browser.contents)
872
873        # Managers can open the callback view which simulates a valid callback
874        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
875        self.browser.open(payment_url)
876        self.browser.getLink("Request callback").click()
877        self.assertMatches('...Valid callback received...',
878                          self.browser.contents)
879
880        # Callback can't be applied twice
881        self.browser.open(payment_url + '/callback')
882        self.assertMatches('...This ticket has already been paid...',
883                          self.browser.contents)
884
885        # Managers can open the pdf payment receipt
886        self.browser.getLink("Download payment receipt").click()
887        self.assertEqual(self.browser.headers['Status'], '200 Ok')
888        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
889
890        # Managers can remove online school fee payment tickets
891        self.browser.open(self.payments_student_path)
892        self.browser.getControl("Remove selected").click()
893        self.assertMatches('...No payment selected...', self.browser.contents)
894        ctrl = self.browser.getControl(name='val_id')
895        value = ctrl.options[0]
896        ctrl.getControl(value=value).selected = True
897        self.browser.getControl("Remove selected", index=0).click()
898        self.assertTrue('Successfully removed' in self.browser.contents)
899
900        # Managers can add online clearance payment tickets
901        self.browser.open(self.payments_student_path + '/addop')
902        self.browser.getControl(name="form.p_category").value = ['clearance']
903        self.browser.getControl("Create ticket").click()
904        self.assertMatches('...ticket created...',
905                           self.browser.contents)
906
907        # Managers can open the callback view which simulates a valid callback
908        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
909        ctrl = self.browser.getControl(name='val_id')
910        value = ctrl.options[0]
911        self.browser.getLink(value).click()
912        self.browser.open(self.browser.url + '/callback')
913        self.assertMatches('...Valid callback received...',
914                          self.browser.contents)
915        expected = '''...
916        <td>
917          Paid
918        </td>...'''
919        self.assertMatches(expected,self.browser.contents)
920        # The new CLR-0 pin has been created
921        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
922        pin = self.app['accesscodes']['CLR-0'].keys()[0]
923        ac = self.app['accesscodes']['CLR-0'][pin]
924        ac.owner = self.student_id
925        # The new CLR-0 pin can be used for starting clearance
926        IWorkflowInfo(self.student).fireTransition('admit')
927        self.browser.open(self.student_path + '/start_clearance')
928        parts = pin.split('-')[1:]
929        clrseries, clrnumber = parts
930        self.browser.getControl(name="ac_series").value = clrseries
931        self.browser.getControl(name="ac_number").value = clrnumber
932        self.browser.getControl("Start clearance now").click()
933        self.assertMatches('...Clearance process has been started...',
934                           self.browser.contents)
935        return
936
937    def test_student_payments(self):
938        # Login
939        self.browser.open(self.login_path)
940        self.browser.getControl(name="form.login").value = self.student_id
941        self.browser.getControl(name="form.password").value = 'spwd'
942        self.browser.getControl("Login").click()
943
944        # Students can add online clearance payment tickets
945        self.browser.open(self.payments_student_path + '/addop')
946        self.browser.getControl(name="form.p_category").value = ['clearance']
947        self.browser.getControl("Create ticket").click()
948        self.assertMatches('...ticket created...',
949                           self.browser.contents)
950
951        # Students can open the callback view which simulates a valid callback
952        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
953        ctrl = self.browser.getControl(name='val_id')
954        value = ctrl.options[0]
955        self.browser.getLink(value).click()
956        payment_url = self.browser.url
957        self.browser.open(payment_url + '/callback')
958        self.assertMatches('...Valid callback received...',
959                          self.browser.contents)
960        expected = '''...
961        <td>
962          Paid
963        </td>...'''
964        self.assertMatches(expected,self.browser.contents)
965        # The new CLR-0 pin has been created
966        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
967        pin = self.app['accesscodes']['CLR-0'].keys()[0]
968        ac = self.app['accesscodes']['CLR-0'][pin]
969        ac.owner = self.student_id
970
971        # Students can open the pdf payment receipt
972        self.browser.open(payment_url + '/payment_receipt.pdf')
973        self.assertEqual(self.browser.headers['Status'], '200 Ok')
974        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
975
976        # The new CLR-0 pin can be used for starting clearance
977        IWorkflowInfo(self.student).fireTransition('admit')
978        self.browser.open(self.student_path + '/start_clearance')
979        parts = pin.split('-')[1:]
980        clrseries, clrnumber = parts
981        self.browser.getControl(name="ac_series").value = clrseries
982        self.browser.getControl(name="ac_number").value = clrnumber
983        self.browser.getControl("Start clearance now").click()
984        self.assertMatches('...Clearance process has been started...',
985                           self.browser.contents)
986
987        # Students can add online school fee payment tickets
988        self.browser.open(self.payments_student_path)
989        self.browser.getControl("Add online payment ticket").click()
990        self.browser.getControl(name="form.p_category").value = ['schoolfee']
991        self.browser.getControl("Create ticket").click()
992        self.assertMatches('...ticket created...',
993                           self.browser.contents)
994        ctrl = self.browser.getControl(name='val_id')
995        value = ctrl.options[0]
996        self.browser.getLink(value).click()
997        self.assertMatches('...Amount Authorized...',
998                           self.browser.contents)
999
1000        # Students can open the callback view which simulates a valid callback
1001        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
1002        self.browser.open(self.browser.url + '/callback')
1003        self.assertMatches('...Valid callback received...',
1004                          self.browser.contents)
1005
1006        # Students can remove only online payment tickets which have
1007        # not received a valid callback
1008        self.browser.open(self.payments_student_path)
1009        self.assertRaises(
1010            LookupError, self.browser.getControl, name='val_id')
1011        self.browser.open(self.payments_student_path + '/addop')
1012        self.browser.getControl(name="form.p_category").value = ['gown']
1013        self.browser.getControl("Create ticket").click()
1014        self.browser.open(self.payments_student_path)
1015        ctrl = self.browser.getControl(name='val_id')
1016        value = ctrl.options[0]
1017        ctrl.getControl(value=value).selected = True
1018        self.browser.getControl("Remove selected", index=0).click()
1019        self.assertTrue('Successfully removed' in self.browser.contents)
1020
1021        # The new SFE-0 pin can be used for starting course registration
1022        IWorkflowInfo(self.student).fireTransition('request_clearance')
1023        IWorkflowInfo(self.student).fireTransition('clear')
1024        self.browser.open(self.studycourse_student_path)
1025        self.browser.getLink('Start course registration').click()
1026        pin = self.app['accesscodes']['SFE-0'].keys()[0]
1027        parts = pin.split('-')[1:]
1028        sfeseries, sfenumber = parts
1029        self.browser.getControl(name="ac_series").value = sfeseries
1030        self.browser.getControl(name="ac_number").value = sfenumber
1031        self.browser.getControl("Start course registration now").click()
1032        self.assertMatches('...Course registration has been started...',
1033                           self.browser.contents)
1034        self.assertTrue(self.student.state == 'school fee paid')
1035        return
1036
1037    def test_manage_accommodation(self):
1038        # Managers can add online booking fee payment tickets and open the
1039        # callback view (see test_manage_payments)
1040        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1041        self.browser.open(self.payments_student_path)
1042        self.browser.getControl("Add online payment ticket").click()
1043        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1044        # If student is not in accommodation session, payment cannot be processed
1045        self.app['configuration'].accommodation_session = 2011
1046        self.browser.getControl("Create ticket").click()
1047        self.assertMatches('...Your current session does not match...',
1048                           self.browser.contents)
1049        self.app['configuration'].accommodation_session = 2004
1050        self.browser.getControl("Add online payment ticket").click()
1051        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1052        self.browser.getControl("Create ticket").click()
1053        ctrl = self.browser.getControl(name='val_id')
1054        value = ctrl.options[0]
1055        self.browser.getLink(value).click()
1056        self.browser.open(self.browser.url + '/callback')
1057        # The new HOS-0 pin has been created
1058        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1059        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1060        ac = self.app['accesscodes']['HOS-0'][pin]
1061        ac.owner = self.student_id
1062        parts = pin.split('-')[1:]
1063        sfeseries, sfenumber = parts
1064        # Managers can use HOS code and book a bed space with it
1065        self.browser.open(self.acco_student_path)
1066        self.browser.getLink("Book accommodation").click()
1067        self.assertMatches('...You are in the wrong...',
1068                           self.browser.contents)
1069        IWorkflowInfo(self.student).fireTransition('admit')
1070        # An existing HOS code can only be used if students
1071        # are in accommodation session
1072        self.student['studycourse'].current_session = 2003
1073        self.browser.getLink("Book accommodation").click()
1074        self.assertMatches('...Your current session does not match...',
1075                           self.browser.contents)
1076        self.student['studycourse'].current_session = 2004
1077        # All requirements are met and ticket can be created
1078        self.browser.getLink("Book accommodation").click()
1079        self.assertMatches('...Activation Code:...',
1080                           self.browser.contents)
1081        self.browser.getControl(name="ac_series").value = sfeseries
1082        self.browser.getControl(name="ac_number").value = sfenumber
1083        self.browser.getControl("Create bed ticket").click()
1084        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1085                           self.browser.contents)
1086        # Bed has been allocated
1087        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
1088        self.assertTrue(bed1.owner == self.student_id)
1089        # BedTicketAddPage is now blocked
1090        self.browser.getLink("Book accommodation").click()
1091        self.assertMatches('...You already booked a bed space...',
1092            self.browser.contents)
1093        # The bed ticket displays the data correctly
1094        self.browser.open(self.acco_student_path + '/2004')
1095        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1096                           self.browser.contents)
1097        self.assertMatches('...2004/2005...', self.browser.contents)
1098        self.assertMatches('...regular_male_fr...', self.browser.contents)
1099        self.assertMatches('...%s...' % pin, self.browser.contents)
1100        # Managers can relocate students if the student's bed_type has changed
1101        self.browser.getLink("Relocate student").click()
1102        self.assertMatches(
1103            "...Student can't be relocated...", self.browser.contents)
1104        self.student.sex = u'f'
1105        self.browser.getLink("Relocate student").click()
1106        self.assertMatches(
1107            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1108        self.assertTrue(bed1.owner == NOT_OCCUPIED)
1109        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1110        self.assertTrue(bed2.owner == self.student_id)
1111        self.assertTrue(self.student['accommodation'][
1112            '2004'].bed_type == u'regular_female_fr')
1113        # The payment object still shows the original payment item
1114        payment_id = self.student['payments'].keys()[0]
1115        payment = self.student['payments'][payment_id]
1116        self.assertTrue(payment.p_item == u'regular_male_fr')
1117        # Managers can relocate students if the bed's bed_type has changed
1118        bed1.bed_type = u'regular_female_fr'
1119        bed2.bed_type = u'regular_male_fr'
1120        notify(grok.ObjectModifiedEvent(bed1))
1121        notify(grok.ObjectModifiedEvent(bed2))
1122        self.browser.getLink("Relocate student").click()
1123        self.assertMatches(
1124            "...Student relocated...", self.browser.contents)
1125        self.assertMatches(
1126            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
1127        self.assertMatches(bed1.owner, self.student_id)
1128        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1129        # Managers can't relocate students if bed is reserved
1130        self.student.sex = u'm'
1131        bed1.bed_type = u'regular_female_reserved'
1132        notify(grok.ObjectModifiedEvent(bed1))
1133        self.browser.getLink("Relocate student").click()
1134        self.assertMatches(
1135            "...Students in reserved beds can't be relocated...",
1136            self.browser.contents)
1137        # Managers can relocate students if booking has been cancelled but
1138        # other bed space has been manually allocated after cancellation
1139        old_owner = bed1.releaseBed()
1140        self.assertMatches(old_owner, self.student_id)
1141        bed2.owner = self.student_id
1142        self.browser.open(self.acco_student_path + '/2004')
1143        self.assertMatches(
1144            "...booking cancelled...", self.browser.contents)
1145        self.browser.getLink("Relocate student").click()
1146        # We didn't informed the catalog therefore the new owner is not found
1147        self.assertMatches(
1148            "...There is no free bed in your category regular_male_fr...",
1149            self.browser.contents)
1150        # Now we fire the event properly
1151        notify(grok.ObjectModifiedEvent(bed2))
1152        self.browser.getLink("Relocate student").click()
1153        self.assertMatches(
1154            "...Student relocated...", self.browser.contents)
1155        self.assertMatches(
1156            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1157          # Managers can delete bed tickets
1158        self.browser.open(self.acco_student_path)
1159        ctrl = self.browser.getControl(name='val_id')
1160        value = ctrl.options[0]
1161        ctrl.getControl(value=value).selected = True
1162        self.browser.getControl("Remove selected", index=0).click()
1163        self.assertMatches('...Successfully removed...', self.browser.contents)
1164        # The bed has been properly released by the event handler
1165        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1166        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1167        return
1168
1169    def test_student_accommodation(self):
1170        # Login
1171        self.browser.open(self.login_path)
1172        self.browser.getControl(name="form.login").value = self.student_id
1173        self.browser.getControl(name="form.password").value = 'spwd'
1174        self.browser.getControl("Login").click()
1175
1176        # Students can add online booking fee payment tickets and open the
1177        # callback view (see test_manage_payments)
1178        self.browser.getLink("Payments").click()
1179        self.browser.getControl("Add online payment ticket").click()
1180        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1181        self.browser.getControl("Create ticket").click()
1182        ctrl = self.browser.getControl(name='val_id')
1183        value = ctrl.options[0]
1184        self.browser.getLink(value).click()
1185        self.browser.open(self.browser.url + '/callback')
1186        # The new HOS-0 pin has been created
1187        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1188        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1189        ac = self.app['accesscodes']['HOS-0'][pin]
1190        ac.owner = u'Anybody'
1191        parts = pin.split('-')[1:]
1192        sfeseries, sfenumber = parts
1193
1194        # Students can use HOS code and book a bed space with it
1195        self.browser.open(self.acco_student_path)
1196        self.browser.getLink("Book accommodation").click()
1197        self.assertMatches('...You are in the wrong...',
1198                           self.browser.contents)
1199        IWorkflowInfo(self.student).fireTransition('admit')
1200        self.browser.getLink("Book accommodation").click()
1201        self.assertMatches('...Activation Code:...',
1202                           self.browser.contents)
1203        self.browser.getControl(name="ac_series").value = u'nonsense'
1204        self.browser.getControl(name="ac_number").value = sfenumber
1205        self.browser.getControl("Create bed ticket").click()
1206        self.assertMatches('...Activation code is invalid...',
1207                           self.browser.contents)
1208        self.browser.getControl(name="ac_series").value = sfeseries
1209        self.browser.getControl(name="ac_number").value = sfenumber
1210        self.browser.getControl("Create bed ticket").click()
1211        self.assertMatches('...You are not the owner of this access code...',
1212                           self.browser.contents)
1213        ac.owner = self.student_id
1214        self.browser.getControl(name="ac_series").value = sfeseries
1215        self.browser.getControl(name="ac_number").value = sfenumber
1216        self.browser.getControl("Create bed ticket").click()
1217        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1218                           self.browser.contents)
1219
1220        # Bed has been allocated
1221        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
1222        self.assertTrue(bed.owner == self.student_id)
1223
1224        # BedTicketAddPage is now blocked
1225        self.browser.getLink("Book accommodation").click()
1226        self.assertMatches('...You already booked a bed space...',
1227            self.browser.contents)
1228
1229        # The bed ticket displays the data correctly
1230        self.browser.open(self.acco_student_path + '/2004')
1231        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1232                           self.browser.contents)
1233        self.assertMatches('...2004/2005...', self.browser.contents)
1234        self.assertMatches('...regular_male_fr...', self.browser.contents)
1235        self.assertMatches('...%s...' % pin, self.browser.contents)
1236
1237        # Students can open the pdf slip
1238        self.browser.open(self.browser.url + '/bed_allocation.pdf')
1239        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1240        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1241
1242        # Students can't relocate themselves
1243        self.assertFalse('Relocate' in self.browser.contents)
1244        relocate_path = self.acco_student_path + '/2004/relocate'
1245        self.assertRaises(
1246            Unauthorized, self.browser.open, relocate_path)
1247
1248        # Students can't the Remove button and check boxes
1249        self.browser.open(self.acco_student_path)
1250        self.assertFalse('Remove' in self.browser.contents)
1251        self.assertFalse('val_id' in self.browser.contents)
1252        return
Note: See TracBrowser for help on using the repository browser.