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

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

Fix test.

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