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

Last change on this file since 7020 was 7019, checked in by Henrik Bettermann, 14 years ago

Implement download page for pdf payment receipts (ExportPDFPaymentSlipPage) including student base data.

  • Property svn:keywords set to Id
File size: 49.7 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 7019 2011-11-07 13:03:42Z 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
27import cStringIO
28from datetime import datetime
29from zope.component import createObject
30from zope.component.hooks import setSite, clearSite
31from zope.security.interfaces import Unauthorized
32from zope.testbrowser.testing import Browser
33from hurry.workflow.interfaces import IWorkflowInfo
34from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
35from waeup.sirp.app import University
36from waeup.sirp.configuration import SessionConfiguration
37from waeup.sirp.students.student import Student
38from waeup.sirp.university.faculty import Faculty
39from waeup.sirp.university.department import Department
40from waeup.sirp.interfaces import IUserAccount
41from waeup.sirp.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
42
43PH_LEN = 2059  # Length of placeholder file
44
45def lookup_submit_value(name, value, browser):
46    """Find a button with a certain value."""
47    for num in range(0, 100):
48        try:
49            button = browser.getControl(name=name, index=num)
50            if button.value.endswith(value):
51                return button
52        except IndexError:
53            break
54    return None
55
56class StudentsFullSetup(FunctionalTestCase):
57    # A test case that only contains a setup and teardown
58    #
59    # Complete setup for students handlings is rather complex and
60    # requires lots of things created before we can start. This is a
61    # setup that does all this, creates a university, creates PINs,
62    # etc.  so that we do not have to bother with that in different
63    # test cases.
64
65    layer = FunctionalLayer
66
67    def setUp(self):
68        super(StudentsFullSetup, self).setUp()
69
70        # Setup a sample site for each test
71        app = University()
72        self.dc_root = tempfile.mkdtemp()
73        app['datacenter'].setStoragePath(self.dc_root)
74
75        # Prepopulate the ZODB...
76        self.getRootFolder()['app'] = app
77        # we add the site immediately after creation to the
78        # ZODB. Catalogs and other local utilities are not setup
79        # before that step.
80        self.app = self.getRootFolder()['app']
81        # Set site here. Some of the following setup code might need
82        # to access grok.getSite() and should get our new app then
83        setSite(app)
84
85        # Add student with subobjects
86        student = Student()
87        student.fullname = u'Anna Tester'
88        student.reg_number = u'123'
89        student.matric_number = u'234'
90        student.sex = u'm'
91        self.app['students'].addStudent(student)
92        self.student_id = student.student_id
93        self.student = self.app['students'][self.student_id]
94
95        # Set password
96        IUserAccount(
97            self.app['students'][self.student_id]).setPassword('spwd')
98
99        self.login_path = 'http://localhost/app/login'
100        self.container_path = 'http://localhost/app/students'
101        self.manage_container_path = self.container_path + '/@@manage'
102        self.add_student_path = self.container_path + '/addstudent'
103        self.student_path = self.container_path + '/' + self.student_id
104        self.manage_student_path = self.student_path + '/edit_base'
105        self.clearance_student_path = self.student_path + '/view_clearance'
106        self.personal_student_path = self.student_path + '/view_personal'
107        self.edit_clearance_student_path = self.student_path + '/edit_clearance'
108        self.edit_personal_student_path = self.student_path + '/edit_personal'
109        self.studycourse_student_path = self.student_path + '/studycourse'
110        self.payments_student_path = self.student_path + '/payments'
111        self.acco_student_path = self.student_path + '/accommodation'
112        self.history_student_path = self.student_path + '/history'
113
114        # Create 5 access codes with prefix'PWD'
115        pin_container = self.app['accesscodes']
116        pin_container.createBatch(
117            datetime.now(), 'some_userid', 'PWD', 9.99, 5)
118        pins = pin_container['PWD-1'].values()
119        self.pwdpins = [x.representation for x in pins]
120        self.existing_pwdpin = self.pwdpins[0]
121        parts = self.existing_pwdpin.split('-')[1:]
122        self.existing_pwdseries, self.existing_pwdnumber = parts
123        # Create 5 access codes with prefix 'CLR'
124        pin_container.createBatch(
125            datetime.now(), 'some_userid', 'CLR', 9.99, 5)
126        pins = pin_container['CLR-1'].values()
127        pins[0].owner = u'Hans Wurst'
128        self.existing_clrac = pins[0]
129        self.existing_clrpin = pins[0].representation
130        parts = self.existing_clrpin.split('-')[1:]
131        self.existing_clrseries, self.existing_clrnumber = parts
132
133        # Populate university
134        self.certificate = createObject('waeup.Certificate')
135        self.certificate.code = u'CERT1'
136        self.certificate.application_category = 'basic'
137        self.certificate.study_mode = 'ug_ft'
138        self.certificate.start_level = 100
139        self.certificate.end_level = 500
140        self.app['faculties']['fac1'] = Faculty()
141        self.app['faculties']['fac1']['dep1'] = Department()
142        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
143            self.certificate)
144        self.course = createObject('waeup.Course')
145        self.course.code = 'COURSE1'
146        self.course.semester = 1
147        self.course.credits = 10
148        self.course.passmark = 40
149        self.app['faculties']['fac1']['dep1'].courses.addCourse(
150            self.course)
151        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCourseRef(
152            self.course, level=100)
153
154        # Configure university
155        self.app['configuration'].accommodation_states = ['admitted']
156        self.app['configuration'].accommodation_session = 2004
157        configuration = SessionConfiguration()
158        configuration.academic_session = 2004
159        configuration.fee_1 = 20000
160        configuration.boocking_fee = 500
161        self.app['configuration'].addSessionConfiguration(configuration)
162
163        # Create a hostel with two beds
164        hostel = Hostel()
165        hostel.hostel_id = u'hall-1'
166        hostel.hostel_name = u'Hall 1'
167        self.app['hostels'].addHostel(hostel)
168        bed = Bed()
169        bed.bed_id = u'hall-1_A_101_A'
170        bed.bed_number = 1
171        bed.owner = NOT_OCCUPIED
172        bed.bed_type = u'regular_male_fr'
173        self.app['hostels'][hostel.hostel_id].addBed(bed)
174        bed = Bed()
175        bed.bed_id = u'hall-1_A_101_B'
176        bed.bed_number = 2
177        bed.owner = NOT_OCCUPIED
178        bed.bed_type = u'regular_female_fr'
179        self.app['hostels'][hostel.hostel_id].addBed(bed)
180
181        # Set study course attributes of test student
182        self.student['studycourse'].certificate = self.certificate
183        self.student['studycourse'].current_session = 2004
184        self.student['studycourse'].entry_session = 2004
185        self.student['studycourse'].current_verdict = 'A'
186        self.student['studycourse'].current_level = 100
187
188        # Put the prepopulated site into test ZODB and prepare test
189        # browser
190        self.browser = Browser()
191        self.browser.handleErrors = False
192
193    def tearDown(self):
194        super(StudentsFullSetup, self).tearDown()
195        clearSite()
196        shutil.rmtree(self.dc_root)
197
198
199
200class StudentsContainerUITests(StudentsFullSetup):
201    # Tests for StudentsContainer class views and pages
202
203    layer = FunctionalLayer
204
205    def test_anonymous_access(self):
206        # Anonymous users can't access students containers
207        self.assertRaises(
208            Unauthorized, self.browser.open, self.container_path)
209        self.assertRaises(
210            Unauthorized, self.browser.open, self.manage_container_path)
211        return
212
213    def test_manage_access(self):
214        # Managers can access the view page of students
215        # containers and can perform actions
216        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
217        self.browser.open(self.container_path)
218        self.assertEqual(self.browser.headers['Status'], '200 Ok')
219        self.assertEqual(self.browser.url, self.container_path)
220        self.browser.getLink("Manage student section").click()
221        self.assertEqual(self.browser.headers['Status'], '200 Ok')
222        self.assertEqual(self.browser.url, self.manage_container_path)
223        return
224
225    def test_add_search_delete_students(self):
226        # Managers can add search and remove students
227        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
228        self.browser.open(self.manage_container_path)
229        self.browser.getLink("Add student").click()
230        self.assertEqual(self.browser.headers['Status'], '200 Ok')
231        self.assertEqual(self.browser.url, self.add_student_path)
232        self.browser.getControl(name="form.fullname").value = 'Bob Tester'
233        self.browser.getControl("Create student record").click()
234        self.assertTrue('Student record created' in self.browser.contents)
235
236        # Registration and matric numbers must be unique
237        self.browser.getLink("Manage").click()
238        self.browser.getControl(name="form.reg_number").value = '123'
239        self.browser.getControl("Save").click()
240        self.assertMatches('...Registration number exists...',
241                           self.browser.contents)
242        self.browser.getControl(name="form.reg_number").value = '789'
243        self.browser.getControl(name="form.matric_number").value = '234'
244        self.browser.getControl("Save").click()
245        self.assertMatches('...Matriculation number exists...',
246                           self.browser.contents)
247
248        self.browser.open(self.container_path)
249        self.browser.getControl("Search").click()
250        self.assertTrue('Empty search string' in self.browser.contents)
251        self.browser.getControl(name="searchtype").value = ['student_id']
252        self.browser.getControl(name="searchterm").value = self.student_id
253        self.browser.getControl("Search").click()
254        self.assertTrue('Anna Tester' in self.browser.contents)
255
256        self.browser.open(self.manage_container_path)
257        self.browser.getControl("Search").click()
258        self.assertTrue('Empty search string' in self.browser.contents)
259        self.browser.getControl(name="searchtype").value = ['fullname']
260        self.browser.getControl(name="searchterm").value = 'Anna Tester'
261        self.browser.getControl("Search").click()
262        self.assertTrue('Anna Tester' in self.browser.contents)
263        # The old searchterm will be used again
264        self.browser.getControl("Search").click()
265        self.assertTrue('Anna Tester' in self.browser.contents)
266
267        ctrl = self.browser.getControl(name='entries')
268        ctrl.getControl(value=self.student_id).selected = True
269        self.browser.getControl("Remove selected", index=0).click()
270        self.assertTrue('Successfully removed' in self.browser.contents)
271        self.browser.getControl(name="searchtype").value = ['student_id']
272        self.browser.getControl(name="searchterm").value = self.student_id
273        self.browser.getControl("Search").click()
274        self.assertTrue('No student found' in self.browser.contents)
275
276        self.browser.open(self.container_path)
277        self.browser.getControl(name="searchtype").value = ['student_id']
278        self.browser.getControl(name="searchterm").value = self.student_id
279        self.browser.getControl("Search").click()
280        self.assertTrue('No student found' in self.browser.contents)
281        return
282
283class StudentUITests(StudentsFullSetup):
284    # Tests for Student class views and pages
285
286    layer = FunctionalLayer
287
288    def test_manage_access(self):
289        # Managers can access the pages of students
290        # and can perform actions
291        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
292
293        self.browser.open(self.student_path)
294        self.assertEqual(self.browser.headers['Status'], '200 Ok')
295        self.assertEqual(self.browser.url, self.student_path)
296        self.browser.getLink("Manage").click()
297        self.assertEqual(self.browser.headers['Status'], '200 Ok')
298        self.assertEqual(self.browser.url, self.manage_student_path)
299        # Managers can edit base data and fire transitions
300        self.browser.getControl(name="transition").value = ['admit']
301        self.browser.getControl(name="form.fullname").value = 'John Tester'
302        self.browser.getControl(name="form.reg_number").value = '345'
303        self.browser.getControl(name="password").value = 'secret'
304        self.browser.getControl(name="control_password").value = 'secret'
305        self.browser.getControl("Save").click()
306        self.assertMatches('...Form has been saved...',
307                           self.browser.contents)
308        self.browser.open(self.student_path)
309        self.browser.getLink("Clearance Data").click()
310        self.assertEqual(self.browser.headers['Status'], '200 Ok')
311        self.assertEqual(self.browser.url, self.clearance_student_path)
312        self.browser.getLink("Manage").click()
313        self.assertEqual(self.browser.headers['Status'], '200 Ok')
314        self.assertEqual(self.browser.url, self.edit_clearance_student_path)
315        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
316        self.browser.getControl("Save").click()
317        self.assertMatches('...Form has been saved...',
318                           self.browser.contents)
319
320        self.browser.open(self.student_path)
321        self.browser.getLink("Personal Data").click()
322        self.assertEqual(self.browser.headers['Status'], '200 Ok')
323        self.assertEqual(self.browser.url, self.personal_student_path)
324        self.browser.getLink("Manage").click()
325        self.assertEqual(self.browser.headers['Status'], '200 Ok')
326        self.assertEqual(self.browser.url, self.edit_personal_student_path)
327        self.browser.getControl("Save").click()
328        self.assertMatches('...Form has been saved...',
329                           self.browser.contents)
330
331        # Managers can browse all subobjects
332        self.browser.open(self.student_path)
333        self.browser.getLink("Payments").click()
334        self.assertEqual(self.browser.headers['Status'], '200 Ok')
335        self.assertEqual(self.browser.url, self.payments_student_path)
336        self.browser.open(self.student_path)
337        self.browser.getLink("Accommodation").click()
338        self.assertEqual(self.browser.headers['Status'], '200 Ok')
339        self.assertEqual(self.browser.url, self.acco_student_path)
340        self.browser.open(self.student_path)
341        self.browser.getLink("History").click()
342        self.assertEqual(self.browser.headers['Status'], '200 Ok')
343        self.assertEqual(self.browser.url, self.history_student_path)
344        self.assertMatches('...Student admitted by zope.mgr...',
345                           self.browser.contents)
346        return
347
348    def test_manage_course_lists(self):
349        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
350        self.browser.open(self.student_path)
351        self.browser.getLink("Study Course").click()
352        self.assertEqual(self.browser.headers['Status'], '200 Ok')
353        self.assertEqual(self.browser.url, self.studycourse_student_path)
354        self.browser.getLink("Manage").click()
355        self.assertTrue('Manage study course' in self.browser.contents)
356        # Before we can select a level, the certificate must be selected and saved
357        self.browser.getControl(name="form.certificate").value = ['CERT1']
358        self.browser.getControl(name="form.current_session").value = ['2004']
359        self.browser.getControl(name="form.current_verdict").value = ['A']
360        self.browser.getControl("Save").click()
361        # Now we can save also the current level which depends on start and end
362        # level of the certificate
363        self.browser.getControl(name="form.current_level").value = ['100']
364        self.browser.getControl("Save").click()
365        # Managers can add and remove any study level (course list)
366        self.browser.getControl(name="addlevel").value = ['100']
367        self.browser.getControl("Add study level").click()
368        self.assertMatches('...<span>100</span>...', self.browser.contents)
369        self.browser.getControl("Add study level").click()
370        self.assertMatches('...This level exists...', self.browser.contents)
371        self.browser.getControl("Remove selected").click()
372        self.assertMatches('...No study level selected...', self.browser.contents)
373        self.browser.getControl(name="val_id").value = ['100']
374        self.browser.getControl("Remove selected").click()
375        self.assertMatches('...Successfully removed...', self.browser.contents)
376        # Add level again
377        self.browser.getControl(name="addlevel").value = ['100']
378        self.browser.getControl("Add study level").click()
379        self.browser.getControl(name="addlevel").value = ['100']
380
381        # Managers can view and manage course lists
382        self.browser.getLink("100").click()
383        self.assertMatches('...: Study Level 100 (Year 1)...', self.browser.contents)
384        self.browser.getLink("Manage").click()
385        self.browser.getControl(name="form.level_session").value = ['2002']
386        self.browser.getControl("Save").click()
387        self.browser.getControl("Remove selected").click()
388        self.assertMatches('...No ticket selected...', self.browser.contents)
389        ctrl = self.browser.getControl(name='val_id')
390        ctrl.getControl(value='COURSE1').selected = True
391        self.browser.getControl("Remove selected", index=0).click()
392        self.assertTrue('Successfully removed' in self.browser.contents)
393        self.browser.getControl("Add course ticket").click()
394        self.browser.getControl(name="form.course").value = ['COURSE1']
395        self.browser.getControl("Add course ticket").click()
396        self.assertTrue('Successfully added' in self.browser.contents)
397        self.browser.getControl("Add course ticket").click()
398        self.browser.getControl(name="form.course").value = ['COURSE1']
399        self.browser.getControl("Add course ticket").click()
400        self.assertTrue('The ticket exists' in self.browser.contents)
401        self.browser.getControl("Cancel").click()
402        self.browser.getLink("COURSE1").click()
403        self.browser.getLink("Manage").click()
404        self.browser.getControl(name="form.score").value = '10'
405        self.browser.getControl("Save").click()
406        self.assertTrue('Form has been saved' in self.browser.contents)
407        return
408
409    def test_manage_workflow(self):
410        # Managers can pass through the whole workflow
411        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
412        student = self.app['students'][self.student_id]
413        self.browser.open(self.manage_student_path)
414        self.assertTrue(student.clearance_locked)
415        self.browser.getControl(name="transition").value = ['admit']
416        self.browser.getControl("Save").click()
417        self.assertTrue(student.clearance_locked)
418        self.browser.getControl(name="transition").value = ['start_clearance']
419        self.browser.getControl("Save").click()
420        self.assertFalse(student.clearance_locked)
421        self.browser.getControl(name="transition").value = ['request_clearance']
422        self.browser.getControl("Save").click()
423        self.assertTrue(student.clearance_locked)
424        self.browser.getControl(name="transition").value = ['clear']
425        self.browser.getControl("Save").click()
426        self.browser.getControl(name="transition").value = ['pay_first_school_fee']
427        self.browser.getControl("Save").click()
428        self.browser.getControl(name="transition").value = ['reset6']
429        self.browser.getControl("Save").click()
430        # In state returning the pay_school_fee transition triggers some
431        # changes of attributes
432        self.browser.getControl(name="transition").value = ['pay_school_fee']
433        self.browser.getControl("Save").click()
434        self.assertEqual(student['studycourse'].current_session, 2005) # +1
435        self.assertEqual(student['studycourse'].current_level, 200) # +100
436        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = not set
437        self.assertEqual(student['studycourse'].previous_verdict, 'A')
438        self.browser.getControl(name="transition").value = ['register_courses']
439        self.browser.getControl("Save").click()
440        self.browser.getControl(name="transition").value = ['validate_courses']
441        self.browser.getControl("Save").click()
442        self.browser.getControl(name="transition").value = ['return']
443        self.browser.getControl("Save").click()
444        return
445
446    def test_manage_import(self):
447        # Managers can import student data files
448        datacenter_path = 'http://localhost/app/datacenter'
449        # Prepare a csv file for students
450        open('students.csv', 'wb').write(
451"""firstname,lastname,fullname,reg_number,date_of_birth,matric_number
452Aaren,Pieri,Aaren Pieri,1,1990-01-02,100000
453Claus,Finau,Claus Finau,2,1990-01-03,100001
454Brit,Berson,Brit Berson,3,1990-01-04,100001
455""")
456        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
457        self.browser.open(datacenter_path)
458        self.browser.getLink('Upload CSV file').click()
459        filecontents = cStringIO.StringIO(open('students.csv', 'rb').read())
460        filewidget = self.browser.getControl(name='uploadfile:file')
461        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
462        self.browser.getControl(name='SUBMIT').click()
463        self.browser.getLink('Batch processing').click()
464        button = lookup_submit_value(
465            'select', 'students_zope.mgr.csv', self.browser)
466        button.click()
467        importerselect = self.browser.getControl(name='importer')
468        modeselect = self.browser.getControl(name='mode')
469        importerselect.getControl('Student Importer').selected = True
470        modeselect.getControl(value='create').selected = True
471        self.browser.getControl('Proceed to step 3...').click()
472        self.assertTrue('Header fields OK' in self.browser.contents)
473        self.browser.getControl('Perform import...').click()
474        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
475        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
476        self.assertTrue('Batch processing finished' in self.browser.contents)
477        open('studycourses.csv', 'wb').write(
478"""reg_number,matric_number,certificate,current_session,current_level
4791,,CERT1,2008,100
480,100001,CERT1,2008,100
481,100002,CERT1,2008,100
482""")
483        self.browser.open(datacenter_path)
484        self.browser.getLink('Upload CSV file').click()
485        filecontents = cStringIO.StringIO(open('studycourses.csv', 'rb').read())
486        filewidget = self.browser.getControl(name='uploadfile:file')
487        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
488        self.browser.getControl(name='SUBMIT').click()
489        self.browser.getLink('Batch processing').click()
490        button = lookup_submit_value(
491            'select', 'studycourses_zope.mgr.csv', self.browser)
492        button.click()
493        importerselect = self.browser.getControl(name='importer')
494        modeselect = self.browser.getControl(name='mode')
495        importerselect.getControl(
496            'StudentStudyCourse Importer (update only)').selected = True
497        modeselect.getControl(value='create').selected = True
498        self.browser.getControl('Proceed to step 3...').click()
499        self.assertTrue('Update mode only' in self.browser.contents)
500        self.browser.getControl('Proceed to step 3...').click()
501        self.assertTrue('Header fields OK' in self.browser.contents)
502        self.browser.getControl('Perform import...').click()
503        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
504        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
505        return
506
507    def test_student_change_password(self):
508        # Students can change the password
509        self.browser.open(self.login_path)
510        self.browser.getControl(name="form.login").value = self.student_id
511        self.browser.getControl(name="form.password").value = 'spwd'
512        self.browser.getControl("Login").click()
513        self.assertEqual(self.browser.url, self.student_path)
514        self.assertTrue('You logged in' in self.browser.contents)
515        # Change password
516        self.browser.getLink("Change password").click()
517        self.browser.getControl(name="form.password").value = 'new_password'
518        self.browser.getControl(
519            name="form.password_repeat").value = 'new_passssword'
520        self.browser.getControl("Save").click()
521        self.assertTrue('passwords do not match' in self.browser.contents)
522        self.browser.getControl(name="form.password").value = 'new_password'
523        self.browser.getControl(
524            name="form.password_repeat").value = 'new_password'
525        self.browser.getControl("Save").click()
526        self.assertTrue('Form has been saved' in self.browser.contents)
527        # We are still logged in. Changing the password hasn't thrown us out.
528        self.browser.getLink("My Data").click()
529        self.assertEqual(self.browser.url, self.student_path)
530        # We can logout
531        self.browser.getLink("Logout").click()
532        self.assertTrue('You have been logged out' in self.browser.contents)
533        self.assertEqual(self.browser.url, 'http://localhost/app')
534        # We can login again with the new password
535        self.browser.getLink("Login").click()
536        self.browser.open(self.login_path)
537        self.browser.getControl(name="form.login").value = self.student_id
538        self.browser.getControl(name="form.password").value = 'new_password'
539        self.browser.getControl("Login").click()
540        self.assertEqual(self.browser.url, self.student_path)
541        self.assertTrue('You logged in' in self.browser.contents)
542        return
543
544    def test_setpassword(self):
545        # Set password for first-time access
546        student = Student()
547        student.reg_number = u'123456'
548        student.fullname = u'Klaus Tester'
549        self.app['students'].addStudent(student)
550        setpassword_path = 'http://localhost/app/setpassword'
551        student_path = 'http://localhost/app/students/%s' % student.student_id
552        self.browser.open(setpassword_path)
553        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
554        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
555        self.browser.getControl(name="reg_number").value = '223456'
556        self.browser.getControl("Show").click()
557        self.assertMatches('...No student found...',
558                           self.browser.contents)
559        self.browser.getControl(name="reg_number").value = '123456'
560        self.browser.getControl(name="ac_number").value = '999999'
561        self.browser.getControl("Show").click()
562        self.assertMatches('...Access code is invalid...',
563                           self.browser.contents)
564        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
565        self.browser.getControl("Show").click()
566        self.assertMatches('...Password has been set. Your Student Id is...',
567                           self.browser.contents)
568        self.browser.getControl("Show").click()
569        self.assertMatches(
570            '...Password has already been set. Your Student Id is...',
571            self.browser.contents)
572        existing_pwdpin = self.pwdpins[1]
573        parts = existing_pwdpin.split('-')[1:]
574        existing_pwdseries, existing_pwdnumber = parts
575        self.browser.getControl(name="ac_series").value = existing_pwdseries
576        self.browser.getControl(name="ac_number").value = existing_pwdnumber
577        self.browser.getControl(name="reg_number").value = '123456'
578        self.browser.getControl("Show").click()
579        self.assertMatches(
580            '...You are using the wrong Access Code...',
581            self.browser.contents)
582        # The student can login with the new credentials
583        self.browser.open(self.login_path)
584        self.browser.getControl(name="form.login").value = student.student_id
585        self.browser.getControl(
586            name="form.password").value = self.existing_pwdnumber
587        self.browser.getControl("Login").click()
588        self.assertEqual(self.browser.url, student_path)
589        self.assertTrue('You logged in' in self.browser.contents)
590        return
591
592    def test_student_access(self):
593        # Students can access their own objects
594        # and can perform actions
595        IWorkflowInfo(self.student).fireTransition('admit')
596        self.browser.open(self.login_path)
597        self.browser.getControl(name="form.login").value = self.student_id
598        self.browser.getControl(name="form.password").value = 'spwd'
599        self.browser.getControl("Login").click()
600        # Student can view the clearance data
601        self.browser.getLink("Clearance Data").click()
602        # Student can't open clearance edit form before starting clearance
603        self.browser.open(self.student_path + '/cedit')
604        self.assertMatches('...The requested form is locked...',
605                           self.browser.contents)
606        self.browser.getLink("Clearance Data").click()
607        self.browser.getLink("Start clearance").click()
608        self.browser.getControl(name="ac_series").value = '3'
609        self.browser.getControl(name="ac_number").value = '4444444'
610        self.browser.getControl("Start clearance now").click()
611        self.assertMatches('...Activation code is invalid...',
612                           self.browser.contents)
613        self.browser.getControl(name="ac_series").value = self.existing_clrseries
614        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
615        # Owner is Hans Wurst, AC can't be invalidated
616        self.browser.getControl("Start clearance now").click()
617        self.assertMatches('...You are not the owner of this access code...',
618                           self.browser.contents)
619        # Set the correct owner
620        self.existing_clrac.owner = self.student_id
621        self.browser.getControl("Start clearance now").click()
622        self.assertMatches('...Clearance process has been started...',
623                           self.browser.contents)
624        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
625        self.browser.getControl("Save", index=0).click()
626        # Student can view the clearance data
627        self.browser.getLink("Clearance Data").click()
628        # and go back to the edit form
629        self.browser.getLink("Edit").click()
630        self.browser.getControl("Save and request clearance").click()
631        self.browser.getControl(name="ac_series").value = self.existing_clrseries
632        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
633        self.browser.getControl("Request clearance now").click()
634        self.assertMatches('...Clearance has been requested...',
635                           self.browser.contents)
636        # Student can't reopen clearance form after requesting clearance
637        self.browser.open(self.student_path + '/cedit')
638        self.assertMatches('...The requested form is locked...',
639                           self.browser.contents)
640        # Student can't add study level if not in state 'school fee paid'
641        self.browser.open(self.student_path + '/studycourse/add')
642        self.assertMatches('...The requested form is locked...',
643                           self.browser.contents)
644        # ... and must be transferred first
645        IWorkflowInfo(self.student).fireTransition('clear')
646        IWorkflowInfo(self.student).fireTransition('pay_first_school_fee')
647        # Now students can add the current study level
648        self.browser.getLink("Study Course").click()
649        self.browser.getLink("Add course list").click()
650        self.assertMatches('...Add current level 100 (Year 1)...',
651                           self.browser.contents)
652        self.browser.getControl("Create course list now").click()
653        self.browser.getLink("100").click()
654        self.browser.getLink("Add and remove courses").click()
655        self.browser.getControl("Add course ticket").click()
656        self.browser.getControl(name="form.course").value = ['COURSE1']
657        self.browser.getControl("Add course ticket").click()
658        self.assertMatches('...The ticket exists...',
659                           self.browser.contents)
660        self.student['studycourse'].current_level = 200
661        self.browser.getLink("Study Course").click()
662        self.browser.getLink("Add course list").click()
663        self.assertMatches('...Add current level 200 (Year 2)...',
664                           self.browser.contents)
665        self.browser.getControl("Create course list now").click()
666        self.browser.getLink("200").click()
667        self.browser.getLink("Add and remove courses").click()
668        self.browser.getControl("Add course ticket").click()
669        self.browser.getControl(name="form.course").value = ['COURSE1']
670        self.browser.getControl("Add course ticket").click()
671        self.assertMatches('...Successfully added COURSE1...',
672                           self.browser.contents)
673        self.browser.getControl("Remove selected", index=0).click()
674        self.assertTrue('No ticket selected' in self.browser.contents)
675        ctrl = self.browser.getControl(name='val_id')
676        ctrl.getControl(value='COURSE1').selected = True
677        self.browser.getControl("Remove selected", index=0).click()
678        self.assertTrue('Successfully removed' in self.browser.contents)
679        self.browser.getControl("Register course list").click()
680        self.assertTrue('Course list has been registered' in self.browser.contents)
681        self.assertEqual(self.student.state, 'courses registered')
682        return
683
684    def test_manage_payments(self):
685        # Managers can add online school fee payment tickets
686        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
687        self.browser.open(self.payments_student_path)
688        self.browser.getControl("Add online payment ticket").click()
689        self.browser.getControl(name="form.p_category").value = ['schoolfee']
690        self.browser.getControl("Create ticket").click()
691        self.assertMatches('...ticket created...',
692                           self.browser.contents)
693        ctrl = self.browser.getControl(name='val_id')
694        value = ctrl.options[0]
695        self.browser.getLink(value).click()
696        self.assertMatches('...Amount Authorized...',
697                           self.browser.contents)
698        payment_url = self.browser.url
699        # Managers can open the callback view which simulates a valid callback
700        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
701        self.browser.open(payment_url + '/callback')
702        self.assertMatches('...Valid callback received...',
703                          self.browser.contents)
704
705        # Managers can open the pdf payment receipt
706        self.browser.open(payment_url + '/payment_receipt.pdf')
707        self.assertEqual(self.browser.headers['Status'], '200 Ok')
708        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
709
710        # Managers can remove online school fee payment tickets
711        self.browser.open(self.payments_student_path)
712        self.browser.getControl("Remove selected").click()
713        self.assertMatches('...No payment selected...', self.browser.contents)
714        ctrl = self.browser.getControl(name='val_id')
715        value = ctrl.options[0]
716        ctrl.getControl(value=value).selected = True
717        self.browser.getControl("Remove selected", index=0).click()
718        self.assertTrue('Successfully removed' in self.browser.contents)
719
720        # Managers can add online clearance payment tickets
721        self.browser.open(self.payments_student_path + '/addop')
722        self.browser.getControl(name="form.p_category").value = ['clearance']
723        self.browser.getControl("Create ticket").click()
724        self.assertMatches('...ticket created...',
725                           self.browser.contents)
726
727        # Managers can open the callback view which simulates a valid callback
728        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
729        ctrl = self.browser.getControl(name='val_id')
730        value = ctrl.options[0]
731        self.browser.getLink(value).click()
732        self.browser.open(self.browser.url + '/callback')
733        self.assertMatches('...Valid callback received...',
734                          self.browser.contents)
735        expected = '''...
736        <td>
737          Paid
738        </td>...'''
739        self.assertMatches(expected,self.browser.contents)
740        # The new CLR-0 pin has been created
741        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
742        pin = self.app['accesscodes']['CLR-0'].keys()[0]
743        ac = self.app['accesscodes']['CLR-0'][pin]
744        ac.owner = self.student_id
745        # The new CLR-0 pin can be used for starting clearance
746        IWorkflowInfo(self.student).fireTransition('admit')
747        self.browser.open(self.student_path + '/start_clearance')
748        parts = pin.split('-')[1:]
749        clrseries, clrnumber = parts
750        self.browser.getControl(name="ac_series").value = clrseries
751        self.browser.getControl(name="ac_number").value = clrnumber
752        self.browser.getControl("Start clearance now").click()
753        self.assertMatches('...Clearance process has been started...',
754                           self.browser.contents)
755        return
756
757    def test_student_payments(self):
758        # Login
759        self.browser.open(self.login_path)
760        self.browser.getControl(name="form.login").value = self.student_id
761        self.browser.getControl(name="form.password").value = 'spwd'
762        self.browser.getControl("Login").click()
763
764        # Students can add online clearance payment tickets
765        self.browser.open(self.payments_student_path + '/addop')
766        self.browser.getControl(name="form.p_category").value = ['clearance']
767        self.browser.getControl("Create ticket").click()
768        self.assertMatches('...ticket created...',
769                           self.browser.contents)
770
771        # Students can open the callback view which simulates a valid callback
772        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
773        ctrl = self.browser.getControl(name='val_id')
774        value = ctrl.options[0]
775        self.browser.getLink(value).click()
776        payment_url = self.browser.url
777        self.browser.open(payment_url + '/callback')
778        self.assertMatches('...Valid callback received...',
779                          self.browser.contents)
780        expected = '''...
781        <td>
782          Paid
783        </td>...'''
784        self.assertMatches(expected,self.browser.contents)
785        # The new CLR-0 pin has been created
786        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
787        pin = self.app['accesscodes']['CLR-0'].keys()[0]
788        ac = self.app['accesscodes']['CLR-0'][pin]
789        ac.owner = self.student_id
790
791        # Students can open the pdf payment receipt
792        self.browser.open(payment_url + '/payment_receipt.pdf')
793        self.assertEqual(self.browser.headers['Status'], '200 Ok')
794        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
795
796        # The new CLR-0 pin can be used for starting clearance
797        IWorkflowInfo(self.student).fireTransition('admit')
798        self.browser.open(self.student_path + '/start_clearance')
799        parts = pin.split('-')[1:]
800        clrseries, clrnumber = parts
801        self.browser.getControl(name="ac_series").value = clrseries
802        self.browser.getControl(name="ac_number").value = clrnumber
803        self.browser.getControl("Start clearance now").click()
804        self.assertMatches('...Clearance process has been started...',
805                           self.browser.contents)
806
807        # Students can add online school fee payment tickets
808        self.browser.open(self.payments_student_path)
809        self.browser.getControl("Add online payment ticket").click()
810        self.browser.getControl(name="form.p_category").value = ['schoolfee']
811        self.browser.getControl("Create ticket").click()
812        self.assertMatches('...ticket created...',
813                           self.browser.contents)
814        ctrl = self.browser.getControl(name='val_id')
815        value = ctrl.options[0]
816        self.browser.getLink(value).click()
817        self.assertMatches('...Amount Authorized...',
818                           self.browser.contents)
819
820        # Students can open the callback view which simulates a valid callback
821        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
822        self.browser.open(self.browser.url + '/callback')
823        self.assertMatches('...Valid callback received...',
824                          self.browser.contents)
825
826        # Students can remove only online payment tickets which have
827        # not received a valid callback
828        self.browser.open(self.payments_student_path)
829        self.assertRaises(
830            LookupError, self.browser.getControl, name='val_id')
831        self.browser.open(self.payments_student_path + '/addop')
832        self.browser.getControl(name="form.p_category").value = ['gown']
833        self.browser.getControl("Create ticket").click()
834        self.browser.open(self.payments_student_path)
835        ctrl = self.browser.getControl(name='val_id')
836        value = ctrl.options[0]
837        ctrl.getControl(value=value).selected = True
838        self.browser.getControl("Remove selected", index=0).click()
839        self.assertTrue('Successfully removed' in self.browser.contents)
840
841        # The new SFE-0 pin can be used for starting course registration
842        IWorkflowInfo(self.student).fireTransition('request_clearance')
843        IWorkflowInfo(self.student).fireTransition('clear')
844        self.browser.open(self.studycourse_student_path)
845        self.browser.getLink('Start course registration').click()
846        pin = self.app['accesscodes']['SFE-0'].keys()[0]
847        parts = pin.split('-')[1:]
848        sfeseries, sfenumber = parts
849        self.browser.getControl(name="ac_series").value = sfeseries
850        self.browser.getControl(name="ac_number").value = sfenumber
851        self.browser.getControl("Start course registration now").click()
852        self.assertMatches('...Course registration has been started...',
853                           self.browser.contents)
854        self.assertTrue(self.student.state == 'school fee paid')
855        return
856
857    def test_manage_accommodation(self):
858        # Managers can add online booking fee payment tickets and open the
859        # callback view (see test_manage_payments)
860        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
861        self.browser.open(self.payments_student_path)
862        self.browser.getControl("Add online payment ticket").click()
863        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
864        self.browser.getControl("Create ticket").click()
865        ctrl = self.browser.getControl(name='val_id')
866        value = ctrl.options[0]
867        self.browser.getLink(value).click()
868        self.browser.open(self.browser.url + '/callback')
869        # The new HOS-0 pin has been created
870        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
871        pin = self.app['accesscodes']['HOS-0'].keys()[0]
872        ac = self.app['accesscodes']['HOS-0'][pin]
873        ac.owner = self.student_id
874        parts = pin.split('-')[1:]
875        sfeseries, sfenumber = parts
876        # Managers can use HOS code and book a bed space with it
877        self.browser.open(self.acco_student_path)
878        self.browser.getLink("Book accommodation").click()
879        self.assertMatches('...You are in the wrong...',
880                           self.browser.contents)
881        IWorkflowInfo(self.student).fireTransition('admit')
882        self.browser.getLink("Book accommodation").click()
883        self.assertMatches('...Activation Code:...',
884                           self.browser.contents)
885        self.browser.getControl(name="ac_series").value = sfeseries
886        self.browser.getControl(name="ac_number").value = sfenumber
887        self.browser.getControl("Create bed ticket").click()
888        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
889                           self.browser.contents)
890        # Bed has been allocated
891        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
892        self.assertTrue(bed.owner == self.student_id)
893        # BedTicketAddPage is now blocked
894        self.browser.getLink("Book accommodation").click()
895        self.assertMatches('...You already booked a bed space...',
896            self.browser.contents)
897        # The bed ticket displays the data correctly
898        self.browser.open(self.acco_student_path + '/2004')
899        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
900                           self.browser.contents)
901        self.assertMatches('...2004/2005...', self.browser.contents)
902        self.assertMatches('...regular_male_fr...', self.browser.contents)
903        self.assertMatches('...%s...' % pin, self.browser.contents)
904        # Managers can relocate students
905        self.browser.getLink("Relocate student").click()
906        self.assertMatches(
907            "...Bed category hasn't changed...", self.browser.contents)
908        self.student.sex = u'f'
909        self.browser.getLink("Relocate student").click()
910        self.assertMatches(
911            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
912        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
913        self.assertTrue(bed1.owner == NOT_OCCUPIED)
914        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
915        self.assertTrue(bed2.owner == self.student_id)
916        self.assertTrue(self.student['accommodation'][
917            '2004'].bed_type == u'regular_female_fr')
918        # The payment object still shows the original payment item
919        payment_id = self.student['payments'].keys()[0]
920        payment = self.student['payments'][payment_id]
921        self.assertTrue(payment.p_item == u'regular_male_fr')
922        # Managers can delete bed tickets
923        self.browser.open(self.acco_student_path)
924        ctrl = self.browser.getControl(name='val_id')
925        value = ctrl.options[0]
926        ctrl.getControl(value=value).selected = True
927        self.browser.getControl("Remove selected", index=0).click()
928        self.assertMatches('...Successfully removed...', self.browser.contents)
929        # The bed has been released properly by the event handler
930        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
931        self.assertTrue(bed.owner == NOT_OCCUPIED)
932        return
933
934    def test_student_accommodation(self):
935        # Login
936        self.browser.open(self.login_path)
937        self.browser.getControl(name="form.login").value = self.student_id
938        self.browser.getControl(name="form.password").value = 'spwd'
939        self.browser.getControl("Login").click()
940
941        # Students can add online booking fee payment tickets and open the
942        # callback view (see test_manage_payments)
943        self.browser.getLink("Payments").click()
944        self.browser.getControl("Add online payment ticket").click()
945        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
946        self.browser.getControl("Create ticket").click()
947        ctrl = self.browser.getControl(name='val_id')
948        value = ctrl.options[0]
949        self.browser.getLink(value).click()
950        self.browser.open(self.browser.url + '/callback')
951        # The new HOS-0 pin has been created
952        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
953        pin = self.app['accesscodes']['HOS-0'].keys()[0]
954        ac = self.app['accesscodes']['HOS-0'][pin]
955        ac.owner = self.student_id
956        parts = pin.split('-')[1:]
957        sfeseries, sfenumber = parts
958        # Students can use HOS code and book a bed space with it
959        self.browser.open(self.acco_student_path)
960        self.browser.getLink("Book accommodation").click()
961        self.assertMatches('...You are in the wrong...',
962                           self.browser.contents)
963        IWorkflowInfo(self.student).fireTransition('admit')
964        self.browser.getLink("Book accommodation").click()
965        self.assertMatches('...Activation Code:...',
966                           self.browser.contents)
967        self.browser.getControl(name="ac_series").value = sfeseries
968        self.browser.getControl(name="ac_number").value = sfenumber
969        self.browser.getControl("Create bed ticket").click()
970        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
971                           self.browser.contents)
972        # Bed has been allocated
973        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
974        self.assertTrue(bed.owner == self.student_id)
975        # BedTicketAddPage is now blocked
976        self.browser.getLink("Book accommodation").click()
977        self.assertMatches('...You already booked a bed space...',
978            self.browser.contents)
979        # The bed ticket displays the data correctly
980        self.browser.open(self.acco_student_path + '/2004')
981        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
982                           self.browser.contents)
983        self.assertMatches('...2004/2005...', self.browser.contents)
984        self.assertMatches('...regular_male_fr...', self.browser.contents)
985        self.assertMatches('...%s...' % pin, self.browser.contents)
986        # Students can't relocate themselves
987        self.assertFalse('Relocate' in self.browser.contents)
988        relocate_path = self.acco_student_path + '/2004/relocate'
989        self.assertRaises(
990            Unauthorized, self.browser.open, relocate_path)
991        # Students can't the Remove button and check boxes
992        self.browser.open(self.acco_student_path)
993        self.assertFalse('Remove' in self.browser.contents)
994        self.assertFalse('val_id' in self.browser.contents)
995        return
Note: See TracBrowser for help on using the repository browser.