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

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

Reorganize file upload. Each viewlet gets an upload and a delete button and writes its own log message. Thus there is one line per file deletion or file upload in the log file. The save action does no longer upload the files.

It works perfectly in the UI but I have still some problems with the browser test.

  • Property svn:keywords set to Id
File size: 57.6 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 7108 2011-11-14 10:01:17Z 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_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        # Create a pseudo image file and select it to be uploaded in form
365        # as birth certificate
366        pseudo_image = StringIO('I pretend to be a graphics file')
367        ctrl = self.browser.getControl(name='birth_certificate')
368        file_ctrl = ctrl.mech_control
369        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
370        # The Save action does not upload files
371        self.browser.getControl("Save (no upload)").click() # submit form
372        self.assertFalse(
373            '<a target="image" href="birth_certificate.jpg">' in self.browser.contents)
374        # ... but the Upload submit button
375        import pdb; pdb.set_trace()
376        ctrl = self.browser.getControl(name='birth_certificate')
377        file_ctrl = ctrl.mech_control
378        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
379        self.browser.getControl("Upload").click() # submit form
380        # There is a correct <img> link included
381        self.assertTrue(
382            '<a target="image" href="birth_certificate.jpg">'
383            in self.browser.contents)
384        # Browsing the link shows a real image
385        self.browser.open('birth_certificate.jpg')
386        self.assertEqual(
387            self.browser.headers['content-type'], 'image/jpeg')
388        self.assertEqual(len(self.browser.contents), 31)
389        # Reuploading a file which is bigger than 150k will raise an error
390        self.browser.open(self.edit_clearance_student_path)
391        photo_content = 'A' * 1024 * 151  # A string of 21 KB size
392        pseudo_image = StringIO(photo_content)
393        ctrl = self.browser.getControl(name='birth_certificate')
394        file_ctrl = ctrl.mech_control
395        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
396        self.browser.getControl("Save").click() # submit form
397        self.assertTrue(
398            'Uploaded file is too big' in self.browser.contents)
399
400    def test_manage_course_lists(self):
401        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
402        self.browser.open(self.student_path)
403        self.browser.getLink("Study Course").click()
404        self.assertEqual(self.browser.headers['Status'], '200 Ok')
405        self.assertEqual(self.browser.url, self.studycourse_student_path)
406        self.browser.getLink("Manage").click()
407        self.assertTrue('Manage study course' in self.browser.contents)
408        # Before we can select a level, the certificate must be selected and saved
409        self.browser.getControl(name="form.certificate").value = ['CERT1']
410        self.browser.getControl(name="form.current_session").value = ['2004']
411        self.browser.getControl(name="form.current_verdict").value = ['A']
412        self.browser.getControl("Save").click()
413        # Now we can save also the current level which depends on start and end
414        # level of the certificate
415        self.browser.getControl(name="form.current_level").value = ['100']
416        self.browser.getControl("Save").click()
417        # Managers can add and remove any study level (course list)
418        self.browser.getControl(name="addlevel").value = ['100']
419        self.browser.getControl("Add study level").click()
420        self.assertMatches('...<span>100</span>...', self.browser.contents)
421        self.browser.getControl("Add study level").click()
422        self.assertMatches('...This level exists...', self.browser.contents)
423        self.browser.getControl("Remove selected").click()
424        self.assertMatches('...No study level selected...', self.browser.contents)
425        self.browser.getControl(name="val_id").value = ['100']
426        self.browser.getControl("Remove selected").click()
427        self.assertMatches('...Successfully removed...', self.browser.contents)
428        # Add level again
429        self.browser.getControl(name="addlevel").value = ['100']
430        self.browser.getControl("Add study level").click()
431        self.browser.getControl(name="addlevel").value = ['100']
432
433        # Managers can view and manage course lists
434        self.browser.getLink("100").click()
435        self.assertMatches('...: Study Level 100 (Year 1)...', self.browser.contents)
436        self.browser.getLink("Manage").click()
437        self.browser.getControl(name="form.level_session").value = ['2002']
438        self.browser.getControl("Save").click()
439        self.browser.getControl("Remove selected").click()
440        self.assertMatches('...No ticket selected...', self.browser.contents)
441        ctrl = self.browser.getControl(name='val_id')
442        ctrl.getControl(value='COURSE1').selected = True
443        self.browser.getControl("Remove selected", index=0).click()
444        self.assertTrue('Successfully removed' in self.browser.contents)
445        self.browser.getControl("Add course ticket").click()
446        self.browser.getControl(name="form.course").value = ['COURSE1']
447        self.browser.getControl("Add course ticket").click()
448        self.assertTrue('Successfully added' in self.browser.contents)
449        self.browser.getControl("Add course ticket").click()
450        self.browser.getControl(name="form.course").value = ['COURSE1']
451        self.browser.getControl("Add course ticket").click()
452        self.assertTrue('The ticket exists' in self.browser.contents)
453        self.browser.getControl("Cancel").click()
454        self.browser.getLink("COURSE1").click()
455        self.browser.getLink("Manage").click()
456        self.browser.getControl(name="form.score").value = '10'
457        self.browser.getControl("Save").click()
458        self.assertTrue('Form has been saved' in self.browser.contents)
459        return
460
461    def test_manage_workflow(self):
462        # Managers can pass through the whole workflow
463        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
464        student = self.app['students'][self.student_id]
465        self.browser.open(self.manage_student_path)
466        self.assertTrue(student.clearance_locked)
467        self.browser.getControl(name="transition").value = ['admit']
468        self.browser.getControl("Save").click()
469        self.assertTrue(student.clearance_locked)
470        self.browser.getControl(name="transition").value = ['start_clearance']
471        self.browser.getControl("Save").click()
472        self.assertFalse(student.clearance_locked)
473        self.browser.getControl(name="transition").value = ['request_clearance']
474        self.browser.getControl("Save").click()
475        self.assertTrue(student.clearance_locked)
476        self.browser.getControl(name="transition").value = ['clear']
477        self.browser.getControl("Save").click()
478        self.browser.getControl(name="transition").value = ['pay_first_school_fee']
479        self.browser.getControl("Save").click()
480        self.browser.getControl(name="transition").value = ['reset6']
481        self.browser.getControl("Save").click()
482        # In state returning the pay_school_fee transition triggers some
483        # changes of attributes
484        self.browser.getControl(name="transition").value = ['pay_school_fee']
485        self.browser.getControl("Save").click()
486        self.assertEqual(student['studycourse'].current_session, 2005) # +1
487        self.assertEqual(student['studycourse'].current_level, 200) # +100
488        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = not set
489        self.assertEqual(student['studycourse'].previous_verdict, 'A')
490        self.browser.getControl(name="transition").value = ['register_courses']
491        self.browser.getControl("Save").click()
492        self.browser.getControl(name="transition").value = ['validate_courses']
493        self.browser.getControl("Save").click()
494        self.browser.getControl(name="transition").value = ['return']
495        self.browser.getControl("Save").click()
496        return
497
498    def test_manage_import(self):
499        # Managers can import student data files
500        datacenter_path = 'http://localhost/app/datacenter'
501        # Prepare a csv file for students
502        open('students.csv', 'wb').write(
503"""firstname,lastname,fullname,reg_number,date_of_birth,matric_number
504Aaren,Pieri,Aaren Pieri,1,1990-01-02,100000
505Claus,Finau,Claus Finau,2,1990-01-03,100001
506Brit,Berson,Brit Berson,3,1990-01-04,100001
507""")
508        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
509        self.browser.open(datacenter_path)
510        self.browser.getLink('Upload CSV file').click()
511        filecontents = StringIO(open('students.csv', 'rb').read())
512        filewidget = self.browser.getControl(name='uploadfile:file')
513        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
514        self.browser.getControl(name='SUBMIT').click()
515        self.browser.getLink('Batch processing').click()
516        button = lookup_submit_value(
517            'select', 'students_zope.mgr.csv', self.browser)
518        button.click()
519        importerselect = self.browser.getControl(name='importer')
520        modeselect = self.browser.getControl(name='mode')
521        importerselect.getControl('Student Importer').selected = True
522        modeselect.getControl(value='create').selected = True
523        self.browser.getControl('Proceed to step 3...').click()
524        self.assertTrue('Header fields OK' in self.browser.contents)
525        self.browser.getControl('Perform import...').click()
526        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
527        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
528        self.assertTrue('Batch processing finished' in self.browser.contents)
529        open('studycourses.csv', 'wb').write(
530"""reg_number,matric_number,certificate,current_session,current_level
5311,,CERT1,2008,100
532,100001,CERT1,2008,100
533,100002,CERT1,2008,100
534""")
535        self.browser.open(datacenter_path)
536        self.browser.getLink('Upload CSV file').click()
537        filecontents = StringIO(open('studycourses.csv', 'rb').read())
538        filewidget = self.browser.getControl(name='uploadfile:file')
539        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
540        self.browser.getControl(name='SUBMIT').click()
541        self.browser.getLink('Batch processing').click()
542        button = lookup_submit_value(
543            'select', 'studycourses_zope.mgr.csv', self.browser)
544        button.click()
545        importerselect = self.browser.getControl(name='importer')
546        modeselect = self.browser.getControl(name='mode')
547        importerselect.getControl(
548            'StudentStudyCourse Importer (update only)').selected = True
549        modeselect.getControl(value='create').selected = True
550        self.browser.getControl('Proceed to step 3...').click()
551        self.assertTrue('Update mode only' in self.browser.contents)
552        self.browser.getControl('Proceed to step 3...').click()
553        self.assertTrue('Header fields OK' in self.browser.contents)
554        self.browser.getControl('Perform import...').click()
555        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
556        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
557        return
558
559    def test_student_change_password(self):
560        # Students can change the password
561        self.browser.open(self.login_path)
562        self.browser.getControl(name="form.login").value = self.student_id
563        self.browser.getControl(name="form.password").value = 'spwd'
564        self.browser.getControl("Login").click()
565        self.assertEqual(self.browser.url, self.student_path)
566        self.assertTrue('You logged in' in self.browser.contents)
567        # Change password
568        self.browser.getLink("Change password").click()
569        self.browser.getControl(name="form.password").value = 'new_password'
570        self.browser.getControl(
571            name="form.password_repeat").value = 'new_passssword'
572        self.browser.getControl("Save").click()
573        self.assertTrue('passwords do not match' in self.browser.contents)
574        self.browser.getControl(name="form.password").value = 'new_password'
575        self.browser.getControl(
576            name="form.password_repeat").value = 'new_password'
577        self.browser.getControl("Save").click()
578        self.assertTrue('Form has been saved' in self.browser.contents)
579        # We are still logged in. Changing the password hasn't thrown us out.
580        self.browser.getLink("My Data").click()
581        self.assertEqual(self.browser.url, self.student_path)
582        # We can logout
583        self.browser.getLink("Logout").click()
584        self.assertTrue('You have been logged out' in self.browser.contents)
585        self.assertEqual(self.browser.url, 'http://localhost/app')
586        # We can login again with the new password
587        self.browser.getLink("Login").click()
588        self.browser.open(self.login_path)
589        self.browser.getControl(name="form.login").value = self.student_id
590        self.browser.getControl(name="form.password").value = 'new_password'
591        self.browser.getControl("Login").click()
592        self.assertEqual(self.browser.url, self.student_path)
593        self.assertTrue('You logged in' in self.browser.contents)
594        return
595
596    def test_setpassword(self):
597        # Set password for first-time access
598        student = Student()
599        student.reg_number = u'123456'
600        student.fullname = u'Klaus Tester'
601        self.app['students'].addStudent(student)
602        setpassword_path = 'http://localhost/app/setpassword'
603        student_path = 'http://localhost/app/students/%s' % student.student_id
604        self.browser.open(setpassword_path)
605        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
606        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
607        self.browser.getControl(name="reg_number").value = '223456'
608        self.browser.getControl("Show").click()
609        self.assertMatches('...No student found...',
610                           self.browser.contents)
611        self.browser.getControl(name="reg_number").value = '123456'
612        self.browser.getControl(name="ac_number").value = '999999'
613        self.browser.getControl("Show").click()
614        self.assertMatches('...Access code is invalid...',
615                           self.browser.contents)
616        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
617        self.browser.getControl("Show").click()
618        self.assertMatches('...Password has been set. Your Student Id is...',
619                           self.browser.contents)
620        self.browser.getControl("Show").click()
621        self.assertMatches(
622            '...Password has already been set. Your Student Id is...',
623            self.browser.contents)
624        existing_pwdpin = self.pwdpins[1]
625        parts = existing_pwdpin.split('-')[1:]
626        existing_pwdseries, existing_pwdnumber = parts
627        self.browser.getControl(name="ac_series").value = existing_pwdseries
628        self.browser.getControl(name="ac_number").value = existing_pwdnumber
629        self.browser.getControl(name="reg_number").value = '123456'
630        self.browser.getControl("Show").click()
631        self.assertMatches(
632            '...You are using the wrong Access Code...',
633            self.browser.contents)
634        # The student can login with the new credentials
635        self.browser.open(self.login_path)
636        self.browser.getControl(name="form.login").value = student.student_id
637        self.browser.getControl(
638            name="form.password").value = self.existing_pwdnumber
639        self.browser.getControl("Login").click()
640        self.assertEqual(self.browser.url, student_path)
641        self.assertTrue('You logged in' in self.browser.contents)
642        return
643
644    def test_student_access(self):
645        # Students can access their own objects
646        # and can perform actions
647        IWorkflowInfo(self.student).fireTransition('admit')
648        self.browser.open(self.login_path)
649        self.browser.getControl(name="form.login").value = self.student_id
650        self.browser.getControl(name="form.password").value = 'spwd'
651        self.browser.getControl("Login").click()
652        # Student can view the clearance data
653        self.browser.getLink("Clearance Data").click()
654        # Student can't open clearance edit form before starting clearance
655        self.browser.open(self.student_path + '/cedit')
656        self.assertMatches('...The requested form is locked...',
657                           self.browser.contents)
658        self.browser.getLink("Clearance Data").click()
659        self.browser.getLink("Start clearance").click()
660        self.browser.getControl(name="ac_series").value = '3'
661        self.browser.getControl(name="ac_number").value = '4444444'
662        self.browser.getControl("Start clearance now").click()
663        self.assertMatches('...Activation code is invalid...',
664                           self.browser.contents)
665        self.browser.getControl(name="ac_series").value = self.existing_clrseries
666        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
667        # Owner is Hans Wurst, AC can't be invalidated
668        self.browser.getControl("Start clearance now").click()
669        self.assertMatches('...You are not the owner of this access code...',
670                           self.browser.contents)
671        # Set the correct owner
672        self.existing_clrac.owner = self.student_id
673        self.browser.getControl("Start clearance now").click()
674        self.assertMatches('...Clearance process has been started...',
675                           self.browser.contents)
676        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
677        self.browser.getControl("Save", index=0).click()
678        # Student can view the clearance data
679        self.browser.getLink("Clearance Data").click()
680        # and go back to the edit form
681        self.browser.getLink("Edit").click()
682        self.browser.getControl("Save and request clearance").click()
683        self.browser.getControl(name="ac_series").value = self.existing_clrseries
684        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
685        self.browser.getControl("Request clearance now").click()
686        self.assertMatches('...Clearance has been requested...',
687                           self.browser.contents)
688        # Student can't reopen clearance form after requesting clearance
689        self.browser.open(self.student_path + '/cedit')
690        self.assertMatches('...The requested form is locked...',
691                           self.browser.contents)
692        # Student can't add study level if not in state 'school fee paid'
693        self.browser.open(self.student_path + '/studycourse/add')
694        self.assertMatches('...The requested form is locked...',
695                           self.browser.contents)
696        # ... and must be transferred first
697        IWorkflowInfo(self.student).fireTransition('clear')
698        IWorkflowInfo(self.student).fireTransition('pay_first_school_fee')
699        # Now students can add the current study level
700        self.browser.getLink("Study Course").click()
701        self.browser.getLink("Add course list").click()
702        self.assertMatches('...Add current level 100 (Year 1)...',
703                           self.browser.contents)
704        self.browser.getControl("Create course list now").click()
705        self.browser.getLink("100").click()
706        self.browser.getLink("Add and remove courses").click()
707        self.browser.getControl("Add course ticket").click()
708        self.browser.getControl(name="form.course").value = ['COURSE1']
709        self.browser.getControl("Add course ticket").click()
710        self.assertMatches('...The ticket exists...',
711                           self.browser.contents)
712        self.student['studycourse'].current_level = 200
713        self.browser.getLink("Study Course").click()
714        self.browser.getLink("Add course list").click()
715        self.assertMatches('...Add current level 200 (Year 2)...',
716                           self.browser.contents)
717        self.browser.getControl("Create course list now").click()
718        self.browser.getLink("200").click()
719        self.browser.getLink("Add and remove courses").click()
720        self.browser.getControl("Add course ticket").click()
721        self.browser.getControl(name="form.course").value = ['COURSE1']
722        self.browser.getControl("Add course ticket").click()
723        self.assertMatches('...Successfully added COURSE1...',
724                           self.browser.contents)
725        # Students can open the pdf course registration slip
726        self.browser.open(self.student_path + '/studycourse/200')
727        self.browser.getLink("Download course registration slip").click()
728        self.assertEqual(self.browser.headers['Status'], '200 Ok')
729        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
730        # Students can remove course tickets
731        self.browser.open(self.student_path + '/studycourse/200/edit')
732        self.browser.getControl("Remove selected", index=0).click()
733        self.assertTrue('No ticket selected' in self.browser.contents)
734        ctrl = self.browser.getControl(name='val_id')
735        ctrl.getControl(value='COURSE1').selected = True
736        self.browser.getControl("Remove selected", index=0).click()
737        self.assertTrue('Successfully removed' in self.browser.contents)
738        self.browser.getControl("Register course list").click()
739        self.assertTrue('Course list has been registered' in self.browser.contents)
740        self.assertEqual(self.student.state, 'courses registered')
741        return
742
743    def test_manage_payments(self):
744        # Managers can add online school fee payment tickets
745        # if certain requirements are met
746        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
747        self.browser.open(self.payments_student_path)
748        self.browser.getControl("Add online payment ticket").click()
749        self.browser.getControl(name="form.p_category").value = ['schoolfee']
750        self.browser.getControl("Create ticket").click()
751        self.assertMatches('...ticket created...',
752                           self.browser.contents)
753        ctrl = self.browser.getControl(name='val_id')
754        value = ctrl.options[0]
755        self.browser.getLink(value).click()
756        self.assertMatches('...Amount Authorized...',
757                           self.browser.contents)
758        payment_url = self.browser.url
759
760        # The pdf payment receipt can't yet be opened
761        self.browser.open(payment_url + '/payment_receipt.pdf')
762        self.assertMatches('...Ticket not yet paid...',
763                           self.browser.contents)
764
765        # The same payment ticket (with same p_item, p_session and p_category)
766        # can't be added twice.
767        self.browser.open(self.payments_student_path)
768        self.browser.getControl("Add online payment ticket").click()
769        self.browser.getControl(name="form.p_category").value = ['schoolfee']
770        self.browser.getControl("Create ticket").click()
771        self.assertMatches('...This payment ticket already exists...',
772                           self.browser.contents)
773
774        # Managers can open the callback view which simulates a valid callback
775        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
776        self.browser.open(payment_url)
777        self.browser.getLink("Request callback").click()
778        self.assertMatches('...Valid callback received...',
779                          self.browser.contents)
780
781        # Callback can't be applied twice
782        self.browser.open(payment_url + '/callback')
783        self.assertMatches('...This ticket has already been paid...',
784                          self.browser.contents)
785
786        # Managers can open the pdf payment receipt
787        self.browser.getLink("Download payment receipt").click()
788        self.assertEqual(self.browser.headers['Status'], '200 Ok')
789        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
790
791        # Managers can remove online school fee payment tickets
792        self.browser.open(self.payments_student_path)
793        self.browser.getControl("Remove selected").click()
794        self.assertMatches('...No payment selected...', self.browser.contents)
795        ctrl = self.browser.getControl(name='val_id')
796        value = ctrl.options[0]
797        ctrl.getControl(value=value).selected = True
798        self.browser.getControl("Remove selected", index=0).click()
799        self.assertTrue('Successfully removed' in self.browser.contents)
800
801        # Managers can add online clearance payment tickets
802        self.browser.open(self.payments_student_path + '/addop')
803        self.browser.getControl(name="form.p_category").value = ['clearance']
804        self.browser.getControl("Create ticket").click()
805        self.assertMatches('...ticket created...',
806                           self.browser.contents)
807
808        # Managers can open the callback view which simulates a valid callback
809        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
810        ctrl = self.browser.getControl(name='val_id')
811        value = ctrl.options[0]
812        self.browser.getLink(value).click()
813        self.browser.open(self.browser.url + '/callback')
814        self.assertMatches('...Valid callback received...',
815                          self.browser.contents)
816        expected = '''...
817        <td>
818          Paid
819        </td>...'''
820        self.assertMatches(expected,self.browser.contents)
821        # The new CLR-0 pin has been created
822        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
823        pin = self.app['accesscodes']['CLR-0'].keys()[0]
824        ac = self.app['accesscodes']['CLR-0'][pin]
825        ac.owner = self.student_id
826        # The new CLR-0 pin can be used for starting clearance
827        IWorkflowInfo(self.student).fireTransition('admit')
828        self.browser.open(self.student_path + '/start_clearance')
829        parts = pin.split('-')[1:]
830        clrseries, clrnumber = parts
831        self.browser.getControl(name="ac_series").value = clrseries
832        self.browser.getControl(name="ac_number").value = clrnumber
833        self.browser.getControl("Start clearance now").click()
834        self.assertMatches('...Clearance process has been started...',
835                           self.browser.contents)
836        return
837
838    def test_student_payments(self):
839        # Login
840        self.browser.open(self.login_path)
841        self.browser.getControl(name="form.login").value = self.student_id
842        self.browser.getControl(name="form.password").value = 'spwd'
843        self.browser.getControl("Login").click()
844
845        # Students can add online clearance payment tickets
846        self.browser.open(self.payments_student_path + '/addop')
847        self.browser.getControl(name="form.p_category").value = ['clearance']
848        self.browser.getControl("Create ticket").click()
849        self.assertMatches('...ticket created...',
850                           self.browser.contents)
851
852        # Students can open the callback view which simulates a valid callback
853        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
854        ctrl = self.browser.getControl(name='val_id')
855        value = ctrl.options[0]
856        self.browser.getLink(value).click()
857        payment_url = self.browser.url
858        self.browser.open(payment_url + '/callback')
859        self.assertMatches('...Valid callback received...',
860                          self.browser.contents)
861        expected = '''...
862        <td>
863          Paid
864        </td>...'''
865        self.assertMatches(expected,self.browser.contents)
866        # The new CLR-0 pin has been created
867        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
868        pin = self.app['accesscodes']['CLR-0'].keys()[0]
869        ac = self.app['accesscodes']['CLR-0'][pin]
870        ac.owner = self.student_id
871
872        # Students can open the pdf payment receipt
873        self.browser.open(payment_url + '/payment_receipt.pdf')
874        self.assertEqual(self.browser.headers['Status'], '200 Ok')
875        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
876
877        # The new CLR-0 pin can be used for starting clearance
878        IWorkflowInfo(self.student).fireTransition('admit')
879        self.browser.open(self.student_path + '/start_clearance')
880        parts = pin.split('-')[1:]
881        clrseries, clrnumber = parts
882        self.browser.getControl(name="ac_series").value = clrseries
883        self.browser.getControl(name="ac_number").value = clrnumber
884        self.browser.getControl("Start clearance now").click()
885        self.assertMatches('...Clearance process has been started...',
886                           self.browser.contents)
887
888        # Students can add online school fee payment tickets
889        self.browser.open(self.payments_student_path)
890        self.browser.getControl("Add online payment ticket").click()
891        self.browser.getControl(name="form.p_category").value = ['schoolfee']
892        self.browser.getControl("Create ticket").click()
893        self.assertMatches('...ticket created...',
894                           self.browser.contents)
895        ctrl = self.browser.getControl(name='val_id')
896        value = ctrl.options[0]
897        self.browser.getLink(value).click()
898        self.assertMatches('...Amount Authorized...',
899                           self.browser.contents)
900
901        # Students can open the callback view which simulates a valid callback
902        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
903        self.browser.open(self.browser.url + '/callback')
904        self.assertMatches('...Valid callback received...',
905                          self.browser.contents)
906
907        # Students can remove only online payment tickets which have
908        # not received a valid callback
909        self.browser.open(self.payments_student_path)
910        self.assertRaises(
911            LookupError, self.browser.getControl, name='val_id')
912        self.browser.open(self.payments_student_path + '/addop')
913        self.browser.getControl(name="form.p_category").value = ['gown']
914        self.browser.getControl("Create ticket").click()
915        self.browser.open(self.payments_student_path)
916        ctrl = self.browser.getControl(name='val_id')
917        value = ctrl.options[0]
918        ctrl.getControl(value=value).selected = True
919        self.browser.getControl("Remove selected", index=0).click()
920        self.assertTrue('Successfully removed' in self.browser.contents)
921
922        # The new SFE-0 pin can be used for starting course registration
923        IWorkflowInfo(self.student).fireTransition('request_clearance')
924        IWorkflowInfo(self.student).fireTransition('clear')
925        self.browser.open(self.studycourse_student_path)
926        self.browser.getLink('Start course registration').click()
927        pin = self.app['accesscodes']['SFE-0'].keys()[0]
928        parts = pin.split('-')[1:]
929        sfeseries, sfenumber = parts
930        self.browser.getControl(name="ac_series").value = sfeseries
931        self.browser.getControl(name="ac_number").value = sfenumber
932        self.browser.getControl("Start course registration now").click()
933        self.assertMatches('...Course registration has been started...',
934                           self.browser.contents)
935        self.assertTrue(self.student.state == 'school fee paid')
936        return
937
938    def test_manage_accommodation(self):
939        # Managers can add online booking fee payment tickets and open the
940        # callback view (see test_manage_payments)
941        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
942        self.browser.open(self.payments_student_path)
943        self.browser.getControl("Add online payment ticket").click()
944        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
945        # If student is not in accommodation session, payment cannot be processed
946        self.app['configuration'].accommodation_session = 2011
947        self.browser.getControl("Create ticket").click()
948        self.assertMatches('...Your current session does not match...',
949                           self.browser.contents)
950        self.app['configuration'].accommodation_session = 2004
951        self.browser.getControl("Add online payment ticket").click()
952        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
953        self.browser.getControl("Create ticket").click()
954        ctrl = self.browser.getControl(name='val_id')
955        value = ctrl.options[0]
956        self.browser.getLink(value).click()
957        self.browser.open(self.browser.url + '/callback')
958        # The new HOS-0 pin has been created
959        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
960        pin = self.app['accesscodes']['HOS-0'].keys()[0]
961        ac = self.app['accesscodes']['HOS-0'][pin]
962        ac.owner = self.student_id
963        parts = pin.split('-')[1:]
964        sfeseries, sfenumber = parts
965        # Managers can use HOS code and book a bed space with it
966        self.browser.open(self.acco_student_path)
967        self.browser.getLink("Book accommodation").click()
968        self.assertMatches('...You are in the wrong...',
969                           self.browser.contents)
970        IWorkflowInfo(self.student).fireTransition('admit')
971        # An existing HOS code can only be used if students
972        # are in accommodation session
973        self.student['studycourse'].current_session = 2003
974        self.browser.getLink("Book accommodation").click()
975        self.assertMatches('...Your current session does not match...',
976                           self.browser.contents)
977        self.student['studycourse'].current_session = 2004
978        # All requirements are met and ticket can be created
979        self.browser.getLink("Book accommodation").click()
980        self.assertMatches('...Activation Code:...',
981                           self.browser.contents)
982        self.browser.getControl(name="ac_series").value = sfeseries
983        self.browser.getControl(name="ac_number").value = sfenumber
984        self.browser.getControl("Create bed ticket").click()
985        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
986                           self.browser.contents)
987        # Bed has been allocated
988        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
989        self.assertTrue(bed1.owner == self.student_id)
990        # BedTicketAddPage is now blocked
991        self.browser.getLink("Book accommodation").click()
992        self.assertMatches('...You already booked a bed space...',
993            self.browser.contents)
994        # The bed ticket displays the data correctly
995        self.browser.open(self.acco_student_path + '/2004')
996        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
997                           self.browser.contents)
998        self.assertMatches('...2004/2005...', self.browser.contents)
999        self.assertMatches('...regular_male_fr...', self.browser.contents)
1000        self.assertMatches('...%s...' % pin, self.browser.contents)
1001        # Managers can relocate students if the student's bed_type has changed
1002        self.browser.getLink("Relocate student").click()
1003        self.assertMatches(
1004            "...Student can't be relocated...", self.browser.contents)
1005        self.student.sex = u'f'
1006        self.browser.getLink("Relocate student").click()
1007        self.assertMatches(
1008            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1009        self.assertTrue(bed1.owner == NOT_OCCUPIED)
1010        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1011        self.assertTrue(bed2.owner == self.student_id)
1012        self.assertTrue(self.student['accommodation'][
1013            '2004'].bed_type == u'regular_female_fr')
1014        # The payment object still shows the original payment item
1015        payment_id = self.student['payments'].keys()[0]
1016        payment = self.student['payments'][payment_id]
1017        self.assertTrue(payment.p_item == u'regular_male_fr')
1018        # Managers can relocate students if the bed's bed_type has changed
1019        bed1.bed_type = u'regular_female_fr'
1020        bed2.bed_type = u'regular_male_fr'
1021        notify(grok.ObjectModifiedEvent(bed1))
1022        notify(grok.ObjectModifiedEvent(bed2))
1023        self.browser.getLink("Relocate student").click()
1024        self.assertMatches(
1025            "...Student relocated...", self.browser.contents)
1026        self.assertMatches(
1027            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
1028        self.assertMatches(bed1.owner, self.student_id)
1029        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1030        # Managers can't relocate students if bed is reserved
1031        self.student.sex = u'm'
1032        bed1.bed_type = u'regular_female_reserved'
1033        notify(grok.ObjectModifiedEvent(bed1))
1034        self.browser.getLink("Relocate student").click()
1035        self.assertMatches(
1036            "...Students in reserved beds can't be relocated...",
1037            self.browser.contents)
1038        # Managers can relocate students if booking has been cancelled but
1039        # other bed space has been manually allocated after cancellation
1040        old_owner = bed1.releaseBed()
1041        self.assertMatches(old_owner, self.student_id)
1042        bed2.owner = self.student_id
1043        self.browser.open(self.acco_student_path + '/2004')
1044        self.assertMatches(
1045            "...booking cancelled...", self.browser.contents)
1046        self.browser.getLink("Relocate student").click()
1047        # We didn't informed the catalog therefore the new owner is not found
1048        self.assertMatches(
1049            "...There is no free bed in your category regular_male_fr...",
1050            self.browser.contents)
1051        # Now we fire the event properly
1052        notify(grok.ObjectModifiedEvent(bed2))
1053        self.browser.getLink("Relocate student").click()
1054        self.assertMatches(
1055            "...Student relocated...", self.browser.contents)
1056        self.assertMatches(
1057            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1058          # Managers can delete bed tickets
1059        self.browser.open(self.acco_student_path)
1060        ctrl = self.browser.getControl(name='val_id')
1061        value = ctrl.options[0]
1062        ctrl.getControl(value=value).selected = True
1063        self.browser.getControl("Remove selected", index=0).click()
1064        self.assertMatches('...Successfully removed...', self.browser.contents)
1065        # The bed has been properly released by the event handler
1066        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1067        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1068        return
1069
1070    def test_student_accommodation(self):
1071        # Login
1072        self.browser.open(self.login_path)
1073        self.browser.getControl(name="form.login").value = self.student_id
1074        self.browser.getControl(name="form.password").value = 'spwd'
1075        self.browser.getControl("Login").click()
1076
1077        # Students can add online booking fee payment tickets and open the
1078        # callback view (see test_manage_payments)
1079        self.browser.getLink("Payments").click()
1080        self.browser.getControl("Add online payment ticket").click()
1081        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1082        self.browser.getControl("Create ticket").click()
1083        ctrl = self.browser.getControl(name='val_id')
1084        value = ctrl.options[0]
1085        self.browser.getLink(value).click()
1086        self.browser.open(self.browser.url + '/callback')
1087        # The new HOS-0 pin has been created
1088        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1089        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1090        ac = self.app['accesscodes']['HOS-0'][pin]
1091        ac.owner = u'Anybody'
1092        parts = pin.split('-')[1:]
1093        sfeseries, sfenumber = parts
1094
1095        # Students can use HOS code and book a bed space with it
1096        self.browser.open(self.acco_student_path)
1097        self.browser.getLink("Book accommodation").click()
1098        self.assertMatches('...You are in the wrong...',
1099                           self.browser.contents)
1100        IWorkflowInfo(self.student).fireTransition('admit')
1101        self.browser.getLink("Book accommodation").click()
1102        self.assertMatches('...Activation Code:...',
1103                           self.browser.contents)
1104        self.browser.getControl(name="ac_series").value = u'nonsense'
1105        self.browser.getControl(name="ac_number").value = sfenumber
1106        self.browser.getControl("Create bed ticket").click()
1107        self.assertMatches('...Activation code is invalid...',
1108                           self.browser.contents)
1109        self.browser.getControl(name="ac_series").value = sfeseries
1110        self.browser.getControl(name="ac_number").value = sfenumber
1111        self.browser.getControl("Create bed ticket").click()
1112        self.assertMatches('...You are not the owner of this access code...',
1113                           self.browser.contents)
1114        ac.owner = self.student_id
1115        self.browser.getControl(name="ac_series").value = sfeseries
1116        self.browser.getControl(name="ac_number").value = sfenumber
1117        self.browser.getControl("Create bed ticket").click()
1118        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1119                           self.browser.contents)
1120
1121        # Bed has been allocated
1122        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
1123        self.assertTrue(bed.owner == self.student_id)
1124
1125        # BedTicketAddPage is now blocked
1126        self.browser.getLink("Book accommodation").click()
1127        self.assertMatches('...You already booked a bed space...',
1128            self.browser.contents)
1129
1130        # The bed ticket displays the data correctly
1131        self.browser.open(self.acco_student_path + '/2004')
1132        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1133                           self.browser.contents)
1134        self.assertMatches('...2004/2005...', self.browser.contents)
1135        self.assertMatches('...regular_male_fr...', self.browser.contents)
1136        self.assertMatches('...%s...' % pin, self.browser.contents)
1137
1138        # Students can open the pdf slip
1139        self.browser.open(self.browser.url + '/bed_allocation.pdf')
1140        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1141        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1142
1143        # Students can't relocate themselves
1144        self.assertFalse('Relocate' in self.browser.contents)
1145        relocate_path = self.acco_student_path + '/2004/relocate'
1146        self.assertRaises(
1147            Unauthorized, self.browser.open, relocate_path)
1148
1149        # Students can't the Remove button and check boxes
1150        self.browser.open(self.acco_student_path)
1151        self.assertFalse('Remove' in self.browser.contents)
1152        self.assertFalse('val_id' in self.browser.contents)
1153        return
Note: See TracBrowser for help on using the repository browser.