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

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

Add test for file uploader in students.

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