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

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

Implement ExportPDFCourseRegistrationSlipPage (work in progess).

PDF payment receipt mustn't be available if ticket has not yet been paid.

  • Property svn:keywords set to Id
File size: 51.5 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 7028 2011-11-08 07:21:58Z 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        self.browser.getControl("Remove selected", index=0).click()
675        self.assertTrue('No ticket selected' in self.browser.contents)
676        ctrl = self.browser.getControl(name='val_id')
677        ctrl.getControl(value='COURSE1').selected = True
678        self.browser.getControl("Remove selected", index=0).click()
679        self.assertTrue('Successfully removed' in self.browser.contents)
680        self.browser.getControl("Register course list").click()
681        self.assertTrue('Course list has been registered' in self.browser.contents)
682        self.assertEqual(self.student.state, 'courses registered')
683        return
684
685    def test_manage_payments(self):
686        # Managers can add online school fee payment tickets
687        # if certain requirements are met
688        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
689        self.browser.open(self.payments_student_path)
690        self.browser.getControl("Add online payment ticket").click()
691        self.browser.getControl(name="form.p_category").value = ['schoolfee']
692        self.browser.getControl("Create ticket").click()
693        self.assertMatches('...ticket created...',
694                           self.browser.contents)
695        ctrl = self.browser.getControl(name='val_id')
696        value = ctrl.options[0]
697        self.browser.getLink(value).click()
698        self.assertMatches('...Amount Authorized...',
699                           self.browser.contents)
700        payment_url = self.browser.url
701
702        # The pdf payment receipt can't yet be opened
703        self.browser.open(payment_url + '/payment_receipt.pdf')
704        self.assertMatches('...Ticket not yet paid...',
705                           self.browser.contents)
706
707        # The same payment ticket (with same p_item, p_session and p_category)
708        # can't be added twice.
709        self.browser.open(self.payments_student_path)
710        self.browser.getControl("Add online payment ticket").click()
711        self.browser.getControl(name="form.p_category").value = ['schoolfee']
712        self.browser.getControl("Create ticket").click()
713        self.assertMatches('...This payment ticket already exists...',
714                           self.browser.contents)
715
716        # Managers can open the callback view which simulates a valid callback
717        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
718        self.browser.open(payment_url + '/callback')
719        self.assertMatches('...Valid callback received...',
720                          self.browser.contents)
721
722        # Callback can't be applied twice
723        self.browser.open(payment_url + '/callback')
724        self.assertMatches('...This ticket has already been paid...',
725                          self.browser.contents)
726
727        # Managers can open the pdf payment receipt
728        self.browser.getLink("Download payment receipt").click()
729        self.assertEqual(self.browser.headers['Status'], '200 Ok')
730        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
731
732        # Managers can remove online school fee payment tickets
733        self.browser.open(self.payments_student_path)
734        self.browser.getControl("Remove selected").click()
735        self.assertMatches('...No payment selected...', self.browser.contents)
736        ctrl = self.browser.getControl(name='val_id')
737        value = ctrl.options[0]
738        ctrl.getControl(value=value).selected = True
739        self.browser.getControl("Remove selected", index=0).click()
740        self.assertTrue('Successfully removed' in self.browser.contents)
741
742        # Managers can add online clearance payment tickets
743        self.browser.open(self.payments_student_path + '/addop')
744        self.browser.getControl(name="form.p_category").value = ['clearance']
745        self.browser.getControl("Create ticket").click()
746        self.assertMatches('...ticket created...',
747                           self.browser.contents)
748
749        # Managers can open the callback view which simulates a valid callback
750        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
751        ctrl = self.browser.getControl(name='val_id')
752        value = ctrl.options[0]
753        self.browser.getLink(value).click()
754        self.browser.open(self.browser.url + '/callback')
755        self.assertMatches('...Valid callback received...',
756                          self.browser.contents)
757        expected = '''...
758        <td>
759          Paid
760        </td>...'''
761        self.assertMatches(expected,self.browser.contents)
762        # The new CLR-0 pin has been created
763        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
764        pin = self.app['accesscodes']['CLR-0'].keys()[0]
765        ac = self.app['accesscodes']['CLR-0'][pin]
766        ac.owner = self.student_id
767        # The new CLR-0 pin can be used for starting clearance
768        IWorkflowInfo(self.student).fireTransition('admit')
769        self.browser.open(self.student_path + '/start_clearance')
770        parts = pin.split('-')[1:]
771        clrseries, clrnumber = parts
772        self.browser.getControl(name="ac_series").value = clrseries
773        self.browser.getControl(name="ac_number").value = clrnumber
774        self.browser.getControl("Start clearance now").click()
775        self.assertMatches('...Clearance process has been started...',
776                           self.browser.contents)
777        return
778
779    def test_student_payments(self):
780        # Login
781        self.browser.open(self.login_path)
782        self.browser.getControl(name="form.login").value = self.student_id
783        self.browser.getControl(name="form.password").value = 'spwd'
784        self.browser.getControl("Login").click()
785
786        # Students can add online clearance payment tickets
787        self.browser.open(self.payments_student_path + '/addop')
788        self.browser.getControl(name="form.p_category").value = ['clearance']
789        self.browser.getControl("Create ticket").click()
790        self.assertMatches('...ticket created...',
791                           self.browser.contents)
792
793        # Students can open the callback view which simulates a valid callback
794        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
795        ctrl = self.browser.getControl(name='val_id')
796        value = ctrl.options[0]
797        self.browser.getLink(value).click()
798        payment_url = self.browser.url
799        self.browser.open(payment_url + '/callback')
800        self.assertMatches('...Valid callback received...',
801                          self.browser.contents)
802        expected = '''...
803        <td>
804          Paid
805        </td>...'''
806        self.assertMatches(expected,self.browser.contents)
807        # The new CLR-0 pin has been created
808        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
809        pin = self.app['accesscodes']['CLR-0'].keys()[0]
810        ac = self.app['accesscodes']['CLR-0'][pin]
811        ac.owner = self.student_id
812
813        # Students can open the pdf payment receipt
814        self.browser.open(payment_url + '/payment_receipt.pdf')
815        self.assertEqual(self.browser.headers['Status'], '200 Ok')
816        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
817
818        # The new CLR-0 pin can be used for starting clearance
819        IWorkflowInfo(self.student).fireTransition('admit')
820        self.browser.open(self.student_path + '/start_clearance')
821        parts = pin.split('-')[1:]
822        clrseries, clrnumber = parts
823        self.browser.getControl(name="ac_series").value = clrseries
824        self.browser.getControl(name="ac_number").value = clrnumber
825        self.browser.getControl("Start clearance now").click()
826        self.assertMatches('...Clearance process has been started...',
827                           self.browser.contents)
828
829        # Students can add online school fee payment tickets
830        self.browser.open(self.payments_student_path)
831        self.browser.getControl("Add online payment ticket").click()
832        self.browser.getControl(name="form.p_category").value = ['schoolfee']
833        self.browser.getControl("Create ticket").click()
834        self.assertMatches('...ticket created...',
835                           self.browser.contents)
836        ctrl = self.browser.getControl(name='val_id')
837        value = ctrl.options[0]
838        self.browser.getLink(value).click()
839        self.assertMatches('...Amount Authorized...',
840                           self.browser.contents)
841
842        # Students can open the callback view which simulates a valid callback
843        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
844        self.browser.open(self.browser.url + '/callback')
845        self.assertMatches('...Valid callback received...',
846                          self.browser.contents)
847
848        # Students can remove only online payment tickets which have
849        # not received a valid callback
850        self.browser.open(self.payments_student_path)
851        self.assertRaises(
852            LookupError, self.browser.getControl, name='val_id')
853        self.browser.open(self.payments_student_path + '/addop')
854        self.browser.getControl(name="form.p_category").value = ['gown']
855        self.browser.getControl("Create ticket").click()
856        self.browser.open(self.payments_student_path)
857        ctrl = self.browser.getControl(name='val_id')
858        value = ctrl.options[0]
859        ctrl.getControl(value=value).selected = True
860        self.browser.getControl("Remove selected", index=0).click()
861        self.assertTrue('Successfully removed' in self.browser.contents)
862
863        # The new SFE-0 pin can be used for starting course registration
864        IWorkflowInfo(self.student).fireTransition('request_clearance')
865        IWorkflowInfo(self.student).fireTransition('clear')
866        self.browser.open(self.studycourse_student_path)
867        self.browser.getLink('Start course registration').click()
868        pin = self.app['accesscodes']['SFE-0'].keys()[0]
869        parts = pin.split('-')[1:]
870        sfeseries, sfenumber = parts
871        self.browser.getControl(name="ac_series").value = sfeseries
872        self.browser.getControl(name="ac_number").value = sfenumber
873        self.browser.getControl("Start course registration now").click()
874        self.assertMatches('...Course registration has been started...',
875                           self.browser.contents)
876        self.assertTrue(self.student.state == 'school fee paid')
877        return
878
879    def test_manage_accommodation(self):
880        # Managers can add online booking fee payment tickets and open the
881        # callback view (see test_manage_payments)
882        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
883        self.browser.open(self.payments_student_path)
884        self.browser.getControl("Add online payment ticket").click()
885        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
886        # If student is not in accommodation session, payment cannot be processed
887        self.app['configuration'].accommodation_session = 2011
888        self.browser.getControl("Create ticket").click()
889        self.assertMatches('...Your current session does not match...',
890                           self.browser.contents)
891        self.app['configuration'].accommodation_session = 2004
892        self.browser.getControl("Add online payment ticket").click()
893        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
894        self.browser.getControl("Create ticket").click()
895        ctrl = self.browser.getControl(name='val_id')
896        value = ctrl.options[0]
897        self.browser.getLink(value).click()
898        self.browser.open(self.browser.url + '/callback')
899        # The new HOS-0 pin has been created
900        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
901        pin = self.app['accesscodes']['HOS-0'].keys()[0]
902        ac = self.app['accesscodes']['HOS-0'][pin]
903        ac.owner = self.student_id
904        parts = pin.split('-')[1:]
905        sfeseries, sfenumber = parts
906        # Managers can use HOS code and book a bed space with it
907        self.browser.open(self.acco_student_path)
908        self.browser.getLink("Book accommodation").click()
909        self.assertMatches('...You are in the wrong...',
910                           self.browser.contents)
911        IWorkflowInfo(self.student).fireTransition('admit')
912        self.browser.getLink("Book accommodation").click()
913        self.assertMatches('...Activation Code:...',
914                           self.browser.contents)
915        self.browser.getControl(name="ac_series").value = sfeseries
916        self.browser.getControl(name="ac_number").value = sfenumber
917        self.browser.getControl("Create bed ticket").click()
918        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
919                           self.browser.contents)
920        # Bed has been allocated
921        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
922        self.assertTrue(bed.owner == self.student_id)
923        # BedTicketAddPage is now blocked
924        self.browser.getLink("Book accommodation").click()
925        self.assertMatches('...You already booked a bed space...',
926            self.browser.contents)
927        # The bed ticket displays the data correctly
928        self.browser.open(self.acco_student_path + '/2004')
929        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
930                           self.browser.contents)
931        self.assertMatches('...2004/2005...', self.browser.contents)
932        self.assertMatches('...regular_male_fr...', self.browser.contents)
933        self.assertMatches('...%s...' % pin, self.browser.contents)
934        # Managers can relocate students
935        self.browser.getLink("Relocate student").click()
936        self.assertMatches(
937            "...Bed category hasn't changed...", self.browser.contents)
938        self.student.sex = u'f'
939        self.browser.getLink("Relocate student").click()
940        self.assertMatches(
941            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
942        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
943        self.assertTrue(bed1.owner == NOT_OCCUPIED)
944        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
945        self.assertTrue(bed2.owner == self.student_id)
946        self.assertTrue(self.student['accommodation'][
947            '2004'].bed_type == u'regular_female_fr')
948        # The payment object still shows the original payment item
949        payment_id = self.student['payments'].keys()[0]
950        payment = self.student['payments'][payment_id]
951        self.assertTrue(payment.p_item == u'regular_male_fr')
952        # Managers can delete bed tickets
953        self.browser.open(self.acco_student_path)
954        ctrl = self.browser.getControl(name='val_id')
955        value = ctrl.options[0]
956        ctrl.getControl(value=value).selected = True
957        self.browser.getControl("Remove selected", index=0).click()
958        self.assertMatches('...Successfully removed...', self.browser.contents)
959        # The bed has been released properly by the event handler
960        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
961        self.assertTrue(bed.owner == NOT_OCCUPIED)
962        return
963
964    def test_student_accommodation(self):
965        # Login
966        self.browser.open(self.login_path)
967        self.browser.getControl(name="form.login").value = self.student_id
968        self.browser.getControl(name="form.password").value = 'spwd'
969        self.browser.getControl("Login").click()
970
971        # Students can add online booking fee payment tickets and open the
972        # callback view (see test_manage_payments)
973        self.browser.getLink("Payments").click()
974        self.browser.getControl("Add online payment ticket").click()
975        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
976        self.browser.getControl("Create ticket").click()
977        ctrl = self.browser.getControl(name='val_id')
978        value = ctrl.options[0]
979        self.browser.getLink(value).click()
980        self.browser.open(self.browser.url + '/callback')
981        # The new HOS-0 pin has been created
982        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
983        pin = self.app['accesscodes']['HOS-0'].keys()[0]
984        ac = self.app['accesscodes']['HOS-0'][pin]
985        ac.owner = self.student_id
986        parts = pin.split('-')[1:]
987        sfeseries, sfenumber = parts
988
989        # Students can use HOS code and book a bed space with it
990        self.browser.open(self.acco_student_path)
991        self.browser.getLink("Book accommodation").click()
992        self.assertMatches('...You are in the wrong...',
993                           self.browser.contents)
994        IWorkflowInfo(self.student).fireTransition('admit')
995        self.browser.getLink("Book accommodation").click()
996        self.assertMatches('...Activation Code:...',
997                           self.browser.contents)
998        self.browser.getControl(name="ac_series").value = sfeseries
999        self.browser.getControl(name="ac_number").value = sfenumber
1000        self.browser.getControl("Create bed ticket").click()
1001        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1002                           self.browser.contents)
1003
1004        # Bed has been allocated
1005        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
1006        self.assertTrue(bed.owner == self.student_id)
1007
1008        # BedTicketAddPage is now blocked
1009        self.browser.getLink("Book accommodation").click()
1010        self.assertMatches('...You already booked a bed space...',
1011            self.browser.contents)
1012
1013        # The bed ticket displays the data correctly
1014        self.browser.open(self.acco_student_path + '/2004')
1015        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1016                           self.browser.contents)
1017        self.assertMatches('...2004/2005...', self.browser.contents)
1018        self.assertMatches('...regular_male_fr...', self.browser.contents)
1019        self.assertMatches('...%s...' % pin, self.browser.contents)
1020
1021        # Students can open the pdf slip
1022        self.browser.open(self.browser.url + '/bed_allocation.pdf')
1023        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1024        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1025
1026        # Students can't relocate themselves
1027        self.assertFalse('Relocate' in self.browser.contents)
1028        relocate_path = self.acco_student_path + '/2004/relocate'
1029        self.assertRaises(
1030            Unauthorized, self.browser.open, relocate_path)
1031
1032        # Students can't the Remove button and check boxes
1033        self.browser.open(self.acco_student_path)
1034        self.assertFalse('Remove' in self.browser.contents)
1035        self.assertFalse('val_id' in self.browser.contents)
1036        return
Note: See TracBrowser for help on using the repository browser.