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

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

Use always view directive for action buttons.

Add test for opening the pdf course registration slip.

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