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

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

Implement ExportPDFBedTicketSlipPage.

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