source: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py @ 9521

Last change on this file since 9521 was 9521, checked in by Henrik Bettermann, 12 years ago

Rename is_fresh method. Actually we need to filter students who have not yet paid school fee.

  • Property svn:keywords set to Id
File size: 129.2 KB
Line 
1## $Id: test_browser.py 9521 2012-11-04 08:04:15Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Test the student-related UI components.
20"""
21import shutil
22import tempfile
23import pytz
24from datetime import datetime, timedelta
25from StringIO import StringIO
26import os
27import grok
28from zope.event import notify
29from zope.component import createObject, queryUtility
30from zope.component.hooks import setSite, clearSite
31from zope.catalog.interfaces import ICatalog
32from zope.security.interfaces import Unauthorized
33from zope.securitypolicy.interfaces import IPrincipalRoleManager
34from zope.testbrowser.testing import Browser
35from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
36from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
37from waeup.kofa.app import University
38from waeup.kofa.payments.interfaces import IPaymentWebservice
39from waeup.kofa.students.student import Student
40from waeup.kofa.students.studylevel import StudentStudyLevel
41from waeup.kofa.university.faculty import Faculty
42from waeup.kofa.university.department import Department
43from waeup.kofa.interfaces import IUserAccount
44from waeup.kofa.authentication import LocalRoleSetEvent
45from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
46
47PH_LEN = 2059  # Length of placeholder file
48
49SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
50SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
51
52def lookup_submit_value(name, value, browser):
53    """Find a button with a certain value."""
54    for num in range(0, 100):
55        try:
56            button = browser.getControl(name=name, index=num)
57            if button.value.endswith(value):
58                return button
59        except IndexError:
60            break
61    return None
62
63class StudentsFullSetup(FunctionalTestCase):
64    # A test case that only contains a setup and teardown
65    #
66    # Complete setup for students handlings is rather complex and
67    # requires lots of things created before we can start. This is a
68    # setup that does all this, creates a university, creates PINs,
69    # etc.  so that we do not have to bother with that in different
70    # test cases.
71
72    layer = FunctionalLayer
73
74    def setUp(self):
75        super(StudentsFullSetup, self).setUp()
76
77        # Setup a sample site for each test
78        app = University()
79        self.dc_root = tempfile.mkdtemp()
80        app['datacenter'].setStoragePath(self.dc_root)
81
82        # Prepopulate the ZODB...
83        self.getRootFolder()['app'] = app
84        # we add the site immediately after creation to the
85        # ZODB. Catalogs and other local utilities are not setup
86        # before that step.
87        self.app = self.getRootFolder()['app']
88        # Set site here. Some of the following setup code might need
89        # to access grok.getSite() and should get our new app then
90        setSite(app)
91
92        # Add student with subobjects
93        student = createObject('waeup.Student')
94        student.firstname = u'Anna'
95        student.lastname = u'Tester'
96        student.reg_number = u'123'
97        student.matric_number = u'234'
98        student.sex = u'm'
99        student.email = 'aa@aa.ng'
100        student.phone = u'1234'
101        self.app['students'].addStudent(student)
102        self.student_id = student.student_id
103        self.student = self.app['students'][self.student_id]
104
105        # Set password
106        IUserAccount(
107            self.app['students'][self.student_id]).setPassword('spwd')
108
109        self.login_path = 'http://localhost/app/login'
110        self.container_path = 'http://localhost/app/students'
111        self.manage_container_path = self.container_path + '/@@manage'
112        self.add_student_path = self.container_path + '/addstudent'
113        self.student_path = self.container_path + '/' + self.student_id
114        self.manage_student_path = self.student_path + '/manage_base'
115        self.trigtrans_path = self.student_path + '/trigtrans'
116        self.clearance_path = self.student_path + '/view_clearance'
117        self.personal_path = self.student_path + '/view_personal'
118        self.edit_clearance_path = self.student_path + '/cedit'
119        self.manage_clearance_path = self.student_path + '/manage_clearance'
120        self.edit_personal_path = self.student_path + '/edit_personal'
121        self.manage_personal_path = self.student_path + '/manage_personal'
122        self.studycourse_path = self.student_path + '/studycourse'
123        self.payments_path = self.student_path + '/payments'
124        self.acco_path = self.student_path + '/accommodation'
125        self.history_path = self.student_path + '/history'
126
127        # Create 5 access codes with prefix'PWD'
128        pin_container = self.app['accesscodes']
129        pin_container.createBatch(
130            datetime.utcnow(), 'some_userid', 'PWD', 9.99, 5)
131        pins = pin_container['PWD-1'].values()
132        self.pwdpins = [x.representation for x in pins]
133        self.existing_pwdpin = self.pwdpins[0]
134        parts = self.existing_pwdpin.split('-')[1:]
135        self.existing_pwdseries, self.existing_pwdnumber = parts
136        # Create 5 access codes with prefix 'CLR'
137        pin_container.createBatch(
138            datetime.now(), 'some_userid', 'CLR', 9.99, 5)
139        pins = pin_container['CLR-1'].values()
140        pins[0].owner = u'Hans Wurst'
141        self.existing_clrac = pins[0]
142        self.existing_clrpin = pins[0].representation
143        parts = self.existing_clrpin.split('-')[1:]
144        self.existing_clrseries, self.existing_clrnumber = parts
145        # Create 2 access codes with prefix 'HOS'
146        pin_container.createBatch(
147            datetime.now(), 'some_userid', 'HOS', 9.99, 2)
148        pins = pin_container['HOS-1'].values()
149        self.existing_hosac = pins[0]
150        self.existing_hospin = pins[0].representation
151        parts = self.existing_hospin.split('-')[1:]
152        self.existing_hosseries, self.existing_hosnumber = parts
153
154        # Populate university
155        self.certificate = createObject('waeup.Certificate')
156        self.certificate.code = u'CERT1'
157        self.certificate.application_category = 'basic'
158        self.certificate.study_mode = 'ug_ft'
159        self.certificate.start_level = 100
160        self.certificate.end_level = 500
161        self.certificate.school_fee_1 = 40000.0
162        self.certificate.school_fee_2 = 20000.0
163        self.app['faculties']['fac1'] = Faculty(code=u'fac1')
164        self.app['faculties']['fac1']['dep1'] = Department(code=u'dep1')
165        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
166            self.certificate)
167        self.course = createObject('waeup.Course')
168        self.course.code = 'COURSE1'
169        self.course.semester = 1
170        self.course.credits = 10
171        self.course.passmark = 40
172        self.app['faculties']['fac1']['dep1'].courses.addCourse(
173            self.course)
174        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
175            self.course, level=100)
176
177        # Configure university and hostels
178        self.app['hostels'].accommodation_states = ['admitted']
179        self.app['hostels'].accommodation_session = 2004
180        delta = timedelta(days=10)
181        self.app['hostels'].startdate = datetime.now(pytz.utc) - delta
182        self.app['hostels'].enddate = datetime.now(pytz.utc) + delta
183        self.app['configuration'].carry_over = True
184        configuration = createObject('waeup.SessionConfiguration')
185        configuration.academic_session = 2004
186        configuration.clearance_fee = 3456.0
187        configuration.booking_fee = 123.4
188        configuration.maint_fee = 987.0
189        self.app['configuration'].addSessionConfiguration(configuration)
190
191        # Create a hostel with two beds
192        hostel = Hostel()
193        hostel.hostel_id = u'hall-1'
194        hostel.hostel_name = u'Hall 1'
195        self.app['hostels'].addHostel(hostel)
196        bed = Bed()
197        bed.bed_id = u'hall-1_A_101_A'
198        bed.bed_number = 1
199        bed.owner = NOT_OCCUPIED
200        bed.bed_type = u'regular_male_fr'
201        self.app['hostels'][hostel.hostel_id].addBed(bed)
202        bed = Bed()
203        bed.bed_id = u'hall-1_A_101_B'
204        bed.bed_number = 2
205        bed.owner = NOT_OCCUPIED
206        bed.bed_type = u'regular_female_fr'
207        self.app['hostels'][hostel.hostel_id].addBed(bed)
208
209        # Set study course attributes of test student
210        self.student['studycourse'].certificate = self.certificate
211        self.student['studycourse'].current_session = 2004
212        self.student['studycourse'].entry_session = 2004
213        self.student['studycourse'].current_verdict = 'A'
214        self.student['studycourse'].current_level = 100
215        # Update the catalog
216        notify(grok.ObjectModifiedEvent(self.student))
217
218        # Put the prepopulated site into test ZODB and prepare test
219        # browser
220        self.browser = Browser()
221        self.browser.handleErrors = False
222
223    def tearDown(self):
224        super(StudentsFullSetup, self).tearDown()
225        clearSite()
226        shutil.rmtree(self.dc_root)
227
228
229
230class StudentsContainerUITests(StudentsFullSetup):
231    # Tests for StudentsContainer class views and pages
232
233    layer = FunctionalLayer
234
235    def test_anonymous_access(self):
236        # Anonymous users can't access students containers
237        self.assertRaises(
238            Unauthorized, self.browser.open, self.container_path)
239        self.assertRaises(
240            Unauthorized, self.browser.open, self.manage_container_path)
241        return
242
243    def test_manage_access(self):
244        # Managers can access the view page of students
245        # containers and can perform actions
246        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
247        self.browser.open(self.container_path)
248        self.assertEqual(self.browser.headers['Status'], '200 Ok')
249        self.assertEqual(self.browser.url, self.container_path)
250        self.browser.getLink("Manage student section").click()
251        self.assertEqual(self.browser.headers['Status'], '200 Ok')
252        self.assertEqual(self.browser.url, self.manage_container_path)
253        return
254
255    def test_add_search_delete_students(self):
256        # Managers can add search and remove students
257        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
258        self.browser.open(self.manage_container_path)
259        self.browser.getLink("Add student").click()
260        self.assertEqual(self.browser.headers['Status'], '200 Ok')
261        self.assertEqual(self.browser.url, self.add_student_path)
262        self.browser.getControl(name="form.firstname").value = 'Bob'
263        self.browser.getControl(name="form.lastname").value = 'Tester'
264        self.browser.getControl(name="form.reg_number").value = '123'
265        self.browser.getControl("Create student record").click()
266        self.assertTrue('Registration number exists already'
267            in self.browser.contents)
268        self.browser.getControl(name="form.reg_number").value = '1234'
269        self.browser.getControl("Create student record").click()
270        self.assertTrue('Student record created' in self.browser.contents)
271
272        # Registration and matric numbers must be unique
273        self.browser.getLink("Manage").click()
274        self.browser.getControl(name="form.reg_number").value = '123'
275        self.browser.getControl("Save").click()
276        self.assertMatches('...Registration number exists...',
277                           self.browser.contents)
278        self.browser.getControl(name="form.reg_number").value = '789'
279        self.browser.getControl(name="form.matric_number").value = '234'
280        self.browser.getControl("Save").click()
281        self.assertMatches('...Matriculation number exists...',
282                           self.browser.contents)
283
284        # We can find a student with a certain student_id
285        self.browser.open(self.container_path)
286        self.browser.getControl("Search").click()
287        self.assertTrue('Empty search string' in self.browser.contents)
288        self.browser.getControl(name="searchtype").value = ['student_id']
289        self.browser.getControl(name="searchterm").value = self.student_id
290        self.browser.getControl("Search").click()
291        self.assertTrue('Anna Tester' in self.browser.contents)
292
293        # We can find a student in a certain session
294        self.browser.open(self.container_path)
295        self.browser.getControl(name="searchtype").value = ['current_session']
296        self.browser.getControl(name="searchterm").value = '2004'
297        self.browser.getControl("Search").click()
298        self.assertTrue('Anna Tester' in self.browser.contents)
299        # Session fileds require integer values
300        self.browser.open(self.container_path)
301        self.browser.getControl(name="searchtype").value = ['current_session']
302        self.browser.getControl(name="searchterm").value = '2004/2005'
303        self.browser.getControl("Search").click()
304        self.assertTrue('Only year dates allowed' in self.browser.contents)
305        self.browser.open(self.manage_container_path)
306        self.browser.getControl(name="searchtype").value = ['current_session']
307        self.browser.getControl(name="searchterm").value = '2004/2005'
308        self.browser.getControl("Search").click()
309        self.assertTrue('Only year dates allowed' in self.browser.contents)
310
311        # We can find a student in a certain study_mode
312        self.browser.open(self.container_path)
313        self.browser.getControl(name="searchtype").value = ['current_mode']
314        self.browser.getControl(name="searchterm").value = 'ug_ft'
315        self.browser.getControl("Search").click()
316        self.assertTrue('Anna Tester' in self.browser.contents)
317
318        # We can find a student in a certain department
319        self.browser.open(self.container_path)
320        self.browser.getControl(name="searchtype").value = ['depcode']
321        self.browser.getControl(name="searchterm").value = 'dep1'
322        self.browser.getControl("Search").click()
323        self.assertTrue('Anna Tester' in self.browser.contents)
324
325        # We can find a student by searching for all kind of name parts
326        self.browser.open(self.manage_container_path)
327        self.browser.getControl("Search").click()
328        self.assertTrue('Empty search string' in self.browser.contents)
329        self.browser.getControl(name="searchtype").value = ['fullname']
330        self.browser.getControl(name="searchterm").value = 'Anna Tester'
331        self.browser.getControl("Search").click()
332        self.assertTrue('Anna Tester' in self.browser.contents)
333        self.browser.open(self.manage_container_path)
334        self.browser.getControl(name="searchtype").value = ['fullname']
335        self.browser.getControl(name="searchterm").value = 'Anna'
336        self.browser.getControl("Search").click()
337        self.assertTrue('Anna Tester' in self.browser.contents)
338        self.browser.open(self.manage_container_path)
339        self.browser.getControl(name="searchtype").value = ['fullname']
340        self.browser.getControl(name="searchterm").value = 'Tester'
341        self.browser.getControl("Search").click()
342        self.assertTrue('Anna Tester' in self.browser.contents)
343        self.browser.open(self.manage_container_path)
344        self.browser.getControl(name="searchtype").value = ['fullname']
345        self.browser.getControl(name="searchterm").value = 'An'
346        self.browser.getControl("Search").click()
347        self.assertFalse('Anna Tester' in self.browser.contents)
348        self.browser.open(self.manage_container_path)
349        self.browser.getControl(name="searchtype").value = ['fullname']
350        self.browser.getControl(name="searchterm").value = 'An*'
351        self.browser.getControl("Search").click()
352        self.assertTrue('Anna Tester' in self.browser.contents)
353        self.browser.open(self.manage_container_path)
354        self.browser.getControl(name="searchtype").value = ['fullname']
355        self.browser.getControl(name="searchterm").value = 'tester'
356        self.browser.getControl("Search").click()
357        self.assertTrue('Anna Tester' in self.browser.contents)
358        self.browser.open(self.manage_container_path)
359        self.browser.getControl(name="searchtype").value = ['fullname']
360        self.browser.getControl(name="searchterm").value = 'Tester Ana'
361        self.browser.getControl("Search").click()
362        self.assertFalse('Anna Tester' in self.browser.contents)
363        self.browser.open(self.manage_container_path)
364        self.browser.getControl(name="searchtype").value = ['fullname']
365        self.browser.getControl(name="searchterm").value = 'Tester Anna'
366        self.browser.getControl("Search").click()
367        self.assertTrue('Anna Tester' in self.browser.contents)
368        # The old searchterm will be used again
369        self.browser.getControl("Search").click()
370        self.assertTrue('Anna Tester' in self.browser.contents)
371
372        # The catalog is informed when studycourse objects have been
373        # edited
374        self.browser.open(self.studycourse_path + '/manage')
375        self.browser.getControl(name="form.current_session").value = ['2010']
376        self.browser.getControl(name="form.entry_session").value = ['2010']
377        self.browser.getControl(name="form.entry_mode").value = ['ug_ft']
378        self.browser.getControl("Save").click()
379
380        # We can find the student in the new session
381        self.browser.open(self.manage_container_path)
382        self.browser.getControl(name="searchtype").value = ['current_session']
383        self.browser.getControl(name="searchterm").value = '2010'
384        self.browser.getControl("Search").click()
385        self.assertTrue('Anna Tester' in self.browser.contents)
386
387        ctrl = self.browser.getControl(name='entries')
388        ctrl.getControl(value=self.student_id).selected = True
389        self.browser.getControl("Remove selected", index=0).click()
390        self.assertTrue('Successfully removed' in self.browser.contents)
391        self.browser.getControl(name="searchtype").value = ['student_id']
392        self.browser.getControl(name="searchterm").value = self.student_id
393        self.browser.getControl("Search").click()
394        self.assertTrue('No student found' in self.browser.contents)
395
396        self.browser.open(self.container_path)
397        self.browser.getControl(name="searchtype").value = ['student_id']
398        self.browser.getControl(name="searchterm").value = self.student_id
399        self.browser.getControl("Search").click()
400        self.assertTrue('No student found' in self.browser.contents)
401        return
402
403class OfficerUITests(StudentsFullSetup):
404    # Tests for Student class views and pages
405
406    def test_student_properties(self):
407        self.student['studycourse'].current_level = 100
408        self.assertEqual(self.student.current_level, 100)
409        self.student['studycourse'].current_session = 2011
410        self.assertEqual(self.student.current_session, 2011)
411        self.student['studycourse'].current_verdict = 'A'
412        self.assertEqual(self.student.current_verdict, 'A')
413        return
414
415    def test_studylevelmanagepage(self):
416        studylevel = StudentStudyLevel()
417        studylevel.level = 100
418        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
419        self.student['studycourse'].addStudentStudyLevel(
420            cert,studylevel)
421        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
422        self.browser.open(self.studycourse_path + '/100/manage')
423        self.assertEqual(self.browser.url, self.studycourse_path + '/100/manage')
424        self.assertEqual(self.browser.headers['Status'], '200 Ok')
425
426    def test_basic_auth(self):
427        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
428        self.browser.open('http://localhost/app')
429        self.browser.getLink("Logout").click()
430        self.assertTrue('You have been logged out' in self.browser.contents)
431        # But we are still logged in since we've used basic authentication here.
432        # Wikipedia says: Existing browsers retain authentication information
433        # until the tab or browser is closed or the user clears the history.
434        # HTTP does not provide a method for a server to direct clients to
435        # discard these cached credentials. This means that there is no
436        # effective way for a server to "log out" the user without closing
437        # the browser. This is a significant defect that requires browser
438        # manufacturers to support a "logout" user interface element ...
439        self.assertTrue('Manager' in self.browser.contents)
440
441    def test_manage_access(self):
442        # Managers can access the pages of students
443        # and can perform actions
444        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
445        self.browser.open(self.student_path)
446        self.assertEqual(self.browser.headers['Status'], '200 Ok')
447        self.assertEqual(self.browser.url, self.student_path)
448        self.browser.getLink("Trigger").click()
449        self.assertEqual(self.browser.headers['Status'], '200 Ok')
450        # Managers can trigger transitions
451        self.browser.getControl(name="transition").value = ['admit']
452        self.browser.getControl("Save").click()
453        # Managers can edit base
454        self.browser.open(self.student_path)
455        self.browser.getLink("Manage").click()
456        self.assertEqual(self.browser.url, self.manage_student_path)
457        self.assertEqual(self.browser.headers['Status'], '200 Ok')
458        self.browser.getControl(name="form.firstname").value = 'John'
459        self.browser.getControl(name="form.lastname").value = 'Tester'
460        self.browser.getControl(name="form.reg_number").value = '345'
461        self.browser.getControl(name="password").value = 'secret'
462        self.browser.getControl(name="control_password").value = 'secret'
463        self.browser.getControl("Save").click()
464        self.assertMatches('...Form has been saved...',
465                           self.browser.contents)
466        self.browser.open(self.student_path)
467        self.browser.getLink("Clearance Data").click()
468        self.assertEqual(self.browser.headers['Status'], '200 Ok')
469        self.assertEqual(self.browser.url, self.clearance_path)
470        self.browser.getLink("Manage").click()
471        self.assertEqual(self.browser.headers['Status'], '200 Ok')
472        self.assertEqual(self.browser.url, self.manage_clearance_path)
473        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
474        self.browser.getControl("Save").click()
475        self.assertMatches('...Form has been saved...',
476                           self.browser.contents)
477
478        self.browser.open(self.student_path)
479        self.browser.getLink("Personal Data").click()
480        self.assertEqual(self.browser.headers['Status'], '200 Ok')
481        self.assertEqual(self.browser.url, self.personal_path)
482        self.browser.getLink("Manage").click()
483        self.assertEqual(self.browser.headers['Status'], '200 Ok')
484        self.assertEqual(self.browser.url, self.manage_personal_path)
485        self.browser.open(self.personal_path)
486        self.browser.getLink("Edit").click()
487        self.assertEqual(self.browser.headers['Status'], '200 Ok')
488        self.assertEqual(self.browser.url, self.edit_personal_path)
489        self.browser.getControl("Save").click()
490        self.assertMatches('...Form has been saved...',
491                           self.browser.contents)
492
493        # Managers can browse all subobjects
494        self.browser.open(self.student_path)
495        self.browser.getLink("Payments").click()
496        self.assertEqual(self.browser.headers['Status'], '200 Ok')
497        self.assertEqual(self.browser.url, self.payments_path)
498        self.browser.open(self.student_path)
499        self.browser.getLink("Accommodation").click()
500        self.assertEqual(self.browser.headers['Status'], '200 Ok')
501        self.assertEqual(self.browser.url, self.acco_path)
502        self.browser.open(self.student_path)
503        self.browser.getLink("History").click()
504        self.assertEqual(self.browser.headers['Status'], '200 Ok')
505        self.assertEqual(self.browser.url, self.history_path)
506        self.assertMatches('...Admitted by Manager...',
507                           self.browser.contents)
508        # Only the Application Slip does not exist
509        self.assertFalse('Application Slip' in self.browser.contents)
510        return
511
512    def test_manage_contact_student(self):
513        # Managers can contact student
514        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
515        self.student.email = None
516        self.browser.open(self.student_path)
517        self.browser.getLink("Send email").click()
518        self.browser.getControl(name="form.subject").value = 'Important subject'
519        self.browser.getControl(name="form.body").value = 'Hello!'
520        self.browser.getControl("Send message now").click()
521        self.assertTrue('An smtp server error occurred' in self.browser.contents)
522        self.student.email = 'xx@yy.zz'
523        self.browser.getControl("Send message now").click()
524        self.assertTrue('Your message has been sent' in self.browser.contents)
525        return
526
527    def test_manage_remove_department(self):
528        # Lazy student is studying CERT1
529        lazystudent = Student()
530        lazystudent.firstname = u'Lazy'
531        lazystudent.lastname = u'Student'
532        self.app['students'].addStudent(lazystudent)
533        student_id = lazystudent.student_id
534        student_path = self.container_path + '/' + student_id
535        lazystudent['studycourse'].certificate = self.certificate
536        notify(grok.ObjectModifiedEvent(lazystudent))
537        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
538        self.browser.open(student_path + '/studycourse')
539        self.assertTrue('CERT1' in self.browser.contents)
540        # After some years the department is removed
541        del self.app['faculties']['fac1']['dep1']
542        # So CERT1 does no longer exist and lazy student's
543        # certificate reference is removed too
544        self.browser.open(student_path + '/studycourse')
545        self.assertEqual(self.browser.headers['Status'], '200 Ok')
546        self.assertEqual(self.browser.url, student_path + '/studycourse')
547        self.assertFalse('CERT1' in self.browser.contents)
548        self.assertMatches('...<div>--</div>...',
549                           self.browser.contents)
550
551    def test_manage_upload_file(self):
552        # Managers can upload a file via the StudentClearanceManageFormPage
553        # The image is stored even if form has errors
554        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
555        self.browser.open(self.manage_clearance_path)
556        # No birth certificate has been uploaded yet
557        # Browsing the link shows a placerholder image
558        self.browser.open('birth_certificate')
559        self.assertEqual(
560            self.browser.headers['content-type'], 'image/jpeg')
561        self.assertEqual(len(self.browser.contents), PH_LEN)
562        # Create a pseudo image file and select it to be uploaded in form
563        # as birth certificate
564        self.browser.open(self.manage_clearance_path)
565        image = open(SAMPLE_IMAGE, 'rb')
566        ctrl = self.browser.getControl(name='birthcertificateupload')
567        file_ctrl = ctrl.mech_control
568        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
569        # The Save action does not upload files
570        self.browser.getControl("Save").click() # submit form
571        self.assertFalse(
572            '<a target="image" href="birth_certificate">'
573            in self.browser.contents)
574        # ... but the correct upload submit button does
575        image = open(SAMPLE_IMAGE)
576        ctrl = self.browser.getControl(name='birthcertificateupload')
577        file_ctrl = ctrl.mech_control
578        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
579        self.browser.getControl(
580            name='upload_birthcertificateupload').click()
581        # There is a correct <img> link included
582        self.assertTrue(
583            '<a target="image" href="birth_certificate">'
584            in self.browser.contents)
585        # Browsing the link shows a real image
586        self.browser.open('birth_certificate')
587        self.assertEqual(
588            self.browser.headers['content-type'], 'image/jpeg')
589        self.assertEqual(len(self.browser.contents), 2787)
590        # Reuploading a file which is bigger than 150k will raise an error
591        self.browser.open(self.manage_clearance_path)
592        # An image > 150K
593        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
594        ctrl = self.browser.getControl(name='birthcertificateupload')
595        file_ctrl = ctrl.mech_control
596        file_ctrl.add_file(big_image, filename='my_birth_certificate.jpg')
597        self.browser.getControl(
598            name='upload_birthcertificateupload').click()
599        self.assertTrue(
600            'Uploaded file is too big' in self.browser.contents)
601        # we do not rely on filename extensions given by uploaders
602        image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
603        ctrl = self.browser.getControl(name='birthcertificateupload')
604        file_ctrl = ctrl.mech_control
605        # tell uploaded file is bmp
606        file_ctrl.add_file(image, filename='my_birth_certificate.bmp')
607        self.browser.getControl(
608            name='upload_birthcertificateupload').click()
609        self.assertTrue(
610            # jpg file was recognized
611            'File birth_certificate.jpg uploaded.' in self.browser.contents)
612        # File names must meet several conditions
613        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
614        ctrl = self.browser.getControl(name='birthcertificateupload')
615        file_ctrl = ctrl.mech_control
616        file_ctrl.add_file(bmp_image, filename='my_birth_certificate.bmp')
617        self.browser.getControl(
618            name='upload_birthcertificateupload').click()
619        self.assertTrue('Only the following extensions are allowed'
620            in self.browser.contents)
621        # Managers can delete files
622        self.browser.getControl(name='delete_birthcertificateupload').click()
623        self.assertTrue(
624            'birth_certificate deleted' in self.browser.contents)
625
626        # Managers can upload a file via the StudentBaseManageFormPage
627        self.browser.open(self.manage_student_path)
628        image = open(SAMPLE_IMAGE_BMP, 'rb')
629        ctrl = self.browser.getControl(name='passportuploadmanage')
630        file_ctrl = ctrl.mech_control
631        file_ctrl.add_file(image, filename='my_photo.bmp')
632        self.browser.getControl(
633            name='upload_passportuploadmanage').click()
634        self.assertTrue('jpg file extension expected'
635            in self.browser.contents)
636        ctrl = self.browser.getControl(name='passportuploadmanage')
637        file_ctrl = ctrl.mech_control
638        image = open(SAMPLE_IMAGE, 'rb')
639        file_ctrl.add_file(image, filename='my_photo.jpg')
640        self.browser.getControl(
641            name='upload_passportuploadmanage').click()
642        self.assertTrue(
643            '<img align="middle" height="125px" src="passport.jpg" />'
644            in self.browser.contents)
645        # We remove the passport file again
646        self.browser.open(self.manage_student_path)
647        self.browser.getControl('Delete').click()
648        self.browser.open(self.student_path + '/clearance_slip.pdf')
649        self.assertEqual(self.browser.headers['Status'], '200 Ok')
650        self.assertEqual(self.browser.headers['Content-Type'],
651                         'application/pdf')
652
653    def test_manage_course_lists(self):
654        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
655        self.browser.open(self.student_path)
656        self.browser.getLink("Study Course").click()
657        self.assertEqual(self.browser.headers['Status'], '200 Ok')
658        self.assertEqual(self.browser.url, self.studycourse_path)
659        self.assertTrue('Undergraduate Full-Time' in self.browser.contents)
660        self.browser.getLink("Manage").click()
661        self.assertTrue('Manage study course' in self.browser.contents)
662        # Before we can select a level, the certificate must
663        # be selected and saved
664        self.browser.getControl(name="form.certificate").value = ['CERT1']
665        self.browser.getControl(name="form.current_session").value = ['2004']
666        self.browser.getControl(name="form.current_verdict").value = ['A']
667        self.browser.getControl(name="form.entry_mode").value = ['ug_ft']
668        self.browser.getControl("Save").click()
669        # Now we can save also the current level which depends on start and end
670        # level of the certificate
671        self.browser.getControl(name="form.current_level").value = ['100']
672        self.browser.getControl("Save").click()
673        # Managers can add and remove any study level (course list)
674        self.browser.getControl(name="addlevel").value = ['100']
675        self.browser.getControl("Add study level").click()
676        self.assertMatches(
677            '...You must select a session...', self.browser.contents)
678        self.browser.getControl(name="addlevel").value = ['100']
679        self.browser.getControl(name="level_session").value = ['2004']
680        self.browser.getControl("Add study level").click()
681        self.assertMatches('...<span>100</span>...', self.browser.contents)
682        self.assertEqual(self.student['studycourse']['100'].level, 100)
683        self.assertEqual(self.student['studycourse']['100'].level_session, 2004)
684        self.browser.getControl(name="addlevel").value = ['100']
685        self.browser.getControl(name="level_session").value = ['2004']
686        self.browser.getControl("Add study level").click()
687        self.assertMatches('...This level exists...', self.browser.contents)
688        self.browser.getControl("Remove selected").click()
689        self.assertMatches(
690            '...No study level selected...', self.browser.contents)
691        self.browser.getControl(name="val_id").value = ['100']
692        self.browser.getControl(name="level_session").value = ['2004']
693        self.browser.getControl("Remove selected").click()
694        self.assertMatches('...Successfully removed...', self.browser.contents)
695        # Removing levels is properly logged
696        logfile = os.path.join(
697            self.app['datacenter'].storage, 'logs', 'students.log')
698        logcontent = open(logfile).read()
699        self.assertTrue('zope.mgr - students.browser.StudyCourseManageFormPage '
700                        '- K1000000 - removed: 100' in logcontent)
701        # Add level again
702        self.browser.getControl(name="addlevel").value = ['100']
703        self.browser.getControl(name="level_session").value = ['2004']
704        self.browser.getControl("Add study level").click()
705
706        # Managers can view and manage course lists
707        self.browser.getLink("100").click()
708        self.assertMatches(
709            '...: Study Level 100 (Year 1)...', self.browser.contents)
710        self.browser.getLink("Manage").click()
711        self.browser.getControl(name="form.level_session").value = ['2002']
712        self.browser.getControl("Save").click()
713        self.browser.getControl("Remove selected").click()
714        self.assertMatches('...No ticket selected...', self.browser.contents)
715        ctrl = self.browser.getControl(name='val_id')
716        ctrl.getControl(value='COURSE1').selected = True
717        self.browser.getControl("Remove selected", index=0).click()
718        self.assertTrue('Successfully removed' in self.browser.contents)
719        # Removing course tickets is properly logged
720        logfile = os.path.join(
721            self.app['datacenter'].storage, 'logs', 'students.log')
722        logcontent = open(logfile).read()
723        self.assertTrue('zope.mgr - students.browser.StudyLevelManageFormPage '
724        '- K1000000 - removed: COURSE1' in logcontent)
725        self.browser.getControl("Add course ticket").click()
726        self.browser.getControl(name="form.course").value = ['COURSE1']
727        self.browser.getControl("Add course ticket").click()
728        self.assertTrue('Successfully added' in self.browser.contents)
729        self.browser.getControl("Add course ticket").click()
730        self.browser.getControl(name="form.course").value = ['COURSE1']
731        self.browser.getControl("Add course ticket").click()
732        self.assertTrue('The ticket exists' in self.browser.contents)
733        self.browser.getControl("Cancel").click()
734        self.browser.getLink("COURSE1").click()
735        self.browser.getLink("Manage").click()
736        self.browser.getControl(name="form.score").value = '10'
737        self.browser.getControl("Save").click()
738        self.assertTrue('Form has been saved' in self.browser.contents)
739        # Carry-over courses will be collected when next level is created
740        self.browser.open(self.student_path + '/studycourse/manage')
741        # Add next level
742        self.browser.getControl(name="addlevel").value = ['200']
743        self.browser.getControl(name="level_session").value = ['2005']
744        self.browser.getControl("Add study level").click()
745        self.browser.getLink("200").click()
746        self.assertMatches(
747            '...: Study Level 200 (Year 2)...', self.browser.contents)
748        # COURSE1 has score 0 and thus will become a carry-over course
749        # in level 200
750        self.assertEqual(
751            sorted(self.student['studycourse']['200'].keys()), [u'COURSE1'])
752        self.assertTrue(
753            self.student['studycourse']['200']['COURSE1'].carry_over)
754        return
755
756    def test_manage_payments(self):
757        # Managers can add online school fee payment tickets
758        # if certain requirements are met
759        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
760        self.browser.open(self.payments_path)
761        IWorkflowState(self.student).setState('cleared')
762        self.browser.getLink("Add current session payment ticket").click()
763        self.browser.getControl(name="form.p_category").value = ['schoolfee']
764        self.browser.getControl("Create ticket").click()
765        self.assertMatches('...ticket created...',
766                           self.browser.contents)
767        ctrl = self.browser.getControl(name='val_id')
768        value = ctrl.options[0]
769        self.browser.getLink(value).click()
770        self.assertMatches('...Amount Authorized...',
771                           self.browser.contents)
772        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
773        payment_url = self.browser.url
774
775        # The pdf payment slip can't yet be opened
776        #self.browser.open(payment_url + '/payment_slip.pdf')
777        #self.assertMatches('...Ticket not yet paid...',
778        #                   self.browser.contents)
779
780        # The same payment (with same p_item, p_session and p_category)
781        # can be initialized a second time if the former ticket is not yet paid.
782        self.browser.open(self.payments_path)
783        self.browser.getLink("Add current session payment ticket").click()
784        self.browser.getControl(name="form.p_category").value = ['schoolfee']
785        self.browser.getControl("Create ticket").click()
786        self.assertMatches('...Payment ticket created...',
787                           self.browser.contents)
788
789        # The ticket can be found in the payments_catalog
790        cat = queryUtility(ICatalog, name='payments_catalog')
791        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
792        self.assertTrue(len(results), 1)
793        self.assertTrue(results[0] is self.student['payments'][value])
794
795        # Managers can approve the payment
796        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
797        self.browser.open(payment_url)
798        self.browser.getLink("Approve payment").click()
799        self.assertMatches('...Payment approved...',
800                          self.browser.contents)
801        # Approval is logged
802        logfile = os.path.join(
803            self.app['datacenter'].storage, 'logs', 'students.log')
804        logcontent = open(logfile).read()
805        self.assertTrue(
806            'zope.mgr - students.browser.OnlinePaymentApprovePage '
807            '- K1000000 - schoolfee payment approved'
808            in logcontent)
809
810        # The authorized amount has been stored in the access code
811        self.assertEqual(
812            self.app['accesscodes']['SFE-0'].values()[0].cost,40000.0)
813
814        # The catalog has been updated
815        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
816        self.assertTrue(len(results), 0)
817        results = list(cat.searchResults(p_state=('paid', 'paid')))
818        self.assertTrue(len(results), 1)
819        self.assertTrue(results[0] is self.student['payments'][value])
820
821        # Payments can't be approved twice
822        self.browser.open(payment_url + '/approve')
823        self.assertMatches('...This ticket has already been paid...',
824                          self.browser.contents)
825
826        # Now the first ticket is paid and no more ticket of same type
827        # (with same p_item, p_session and p_category) can be added
828        self.browser.open(self.payments_path)
829        self.browser.getLink("Add current session payment ticket").click()
830        self.browser.getControl(name="form.p_category").value = ['schoolfee']
831        self.browser.getControl("Create ticket").click()
832        self.assertMatches(
833            '...This type of payment has already been made...',
834            self.browser.contents)
835
836        # Managers can open the pdf payment slip
837        self.browser.open(payment_url)
838        self.browser.getLink("Download payment slip").click()
839        self.assertEqual(self.browser.headers['Status'], '200 Ok')
840        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
841
842        # Managers can remove online school fee payment tickets
843        self.browser.open(self.payments_path)
844        self.browser.getControl("Remove selected").click()
845        self.assertMatches('...No payment selected...', self.browser.contents)
846        ctrl = self.browser.getControl(name='val_id')
847        value = ctrl.options[0]
848        ctrl.getControl(value=value).selected = True
849        self.browser.getControl("Remove selected", index=0).click()
850        self.assertTrue('Successfully removed' in self.browser.contents)
851
852        # Managers can add online clearance payment tickets
853        self.browser.open(self.payments_path + '/addop')
854        self.browser.getControl(name="form.p_category").value = ['clearance']
855        self.browser.getControl("Create ticket").click()
856        self.assertMatches('...ticket created...',
857                           self.browser.contents)
858
859        # Managers can approve the payment
860        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
861        ctrl = self.browser.getControl(name='val_id')
862        value = ctrl.options[1] # The clearance payment is the second in the table
863        self.browser.getLink(value).click()
864        self.browser.open(self.browser.url + '/approve')
865        self.assertMatches('...Payment approved...',
866                          self.browser.contents)
867        expected = '''...
868        <td>
869          <span>Paid</span>
870        </td>...'''
871        self.assertMatches(expected,self.browser.contents)
872        # The new CLR-0 pin has been created
873        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
874        pin = self.app['accesscodes']['CLR-0'].keys()[0]
875        ac = self.app['accesscodes']['CLR-0'][pin]
876        self.assertEqual(ac.owner, self.student_id)
877        self.assertEqual(ac.cost, 3456.0)
878        return
879
880    def test_manage_accommodation(self):
881        logfile = os.path.join(
882            self.app['datacenter'].storage, 'logs', 'students.log')
883        # Managers can add online booking fee payment tickets and open the
884        # callback view (see test_manage_payments)
885        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
886        self.browser.open(self.payments_path)
887        self.browser.getLink("Add current session payment ticket").click()
888        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
889        # If student is not in accommodation session, payment cannot be processed
890        self.app['hostels'].accommodation_session = 2011
891        self.browser.getControl("Create ticket").click()
892        self.assertMatches('...Your current session does not match...',
893                           self.browser.contents)
894        self.app['hostels'].accommodation_session = 2004
895        self.browser.getLink("Add current session payment ticket").click()
896        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
897        self.browser.getControl("Create ticket").click()
898        ctrl = self.browser.getControl(name='val_id')
899        value = ctrl.options[0]
900        self.browser.getLink(value).click()
901        self.browser.open(self.browser.url + '/approve')
902        # The new HOS-0 pin has been created
903        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
904        pin = self.app['accesscodes']['HOS-0'].keys()[0]
905        ac = self.app['accesscodes']['HOS-0'][pin]
906        self.assertEqual(ac.owner, self.student_id)
907        parts = pin.split('-')[1:]
908        sfeseries, sfenumber = parts
909        # Managers can use HOS code and book a bed space with it
910        self.browser.open(self.acco_path)
911        self.browser.getLink("Book accommodation").click()
912        self.assertMatches('...You are in the wrong...',
913                           self.browser.contents)
914        IWorkflowInfo(self.student).fireTransition('admit')
915        # An existing HOS code can only be used if students
916        # are in accommodation session
917        self.student['studycourse'].current_session = 2003
918        self.browser.getLink("Book accommodation").click()
919        self.assertMatches('...Your current session does not match...',
920                           self.browser.contents)
921        self.student['studycourse'].current_session = 2004
922        # All requirements are met and ticket can be created
923        self.browser.getLink("Book accommodation").click()
924        self.assertMatches('...Activation Code:...',
925                           self.browser.contents)
926        self.browser.getControl(name="ac_series").value = sfeseries
927        self.browser.getControl(name="ac_number").value = sfenumber
928        self.browser.getControl("Create bed ticket").click()
929        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
930                           self.browser.contents)
931        # Bed has been allocated
932        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
933        self.assertTrue(bed1.owner == self.student_id)
934        # BedTicketAddPage is now blocked
935        self.browser.getLink("Book accommodation").click()
936        self.assertMatches('...You already booked a bed space...',
937            self.browser.contents)
938        # The bed ticket displays the data correctly
939        self.browser.open(self.acco_path + '/2004')
940        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
941                           self.browser.contents)
942        self.assertMatches('...2004/2005...', self.browser.contents)
943        self.assertMatches('...regular_male_fr...', self.browser.contents)
944        self.assertMatches('...%s...' % pin, self.browser.contents)
945        # Booking is properly logged
946        logcontent = open(logfile).read()
947        self.assertTrue('zope.mgr - students.browser.BedTicketAddPage '
948            '- K1000000 - booked: hall-1_A_101_A' in logcontent)
949        # Managers can relocate students if the student's bed_type has changed
950        self.browser.getLink("Relocate student").click()
951        self.assertMatches(
952            "...Student can't be relocated...", self.browser.contents)
953        self.student.sex = u'f'
954        self.browser.getLink("Relocate student").click()
955        self.assertMatches(
956            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
957        self.assertTrue(bed1.owner == NOT_OCCUPIED)
958        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
959        self.assertTrue(bed2.owner == self.student_id)
960        self.assertTrue(self.student['accommodation'][
961            '2004'].bed_type == u'regular_female_fr')
962        # Relocation is properly logged
963        logcontent = open(logfile).read()
964        self.assertTrue('zope.mgr - students.browser.BedTicketRelocationPage '
965            '- K1000000 - relocated: hall-1_A_101_B' in logcontent)
966        # The payment object still shows the original payment item
967        payment_id = self.student['payments'].keys()[0]
968        payment = self.student['payments'][payment_id]
969        self.assertTrue(payment.p_item == u'regular_male_fr')
970        # Managers can relocate students if the bed's bed_type has changed
971        bed1.bed_type = u'regular_female_fr'
972        bed2.bed_type = u'regular_male_fr'
973        notify(grok.ObjectModifiedEvent(bed1))
974        notify(grok.ObjectModifiedEvent(bed2))
975        self.browser.getLink("Relocate student").click()
976        self.assertMatches(
977            "...Student relocated...", self.browser.contents)
978        self.assertMatches(
979            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
980        self.assertMatches(bed1.owner, self.student_id)
981        self.assertMatches(bed2.owner, NOT_OCCUPIED)
982        # Managers can't relocate students if bed is reserved
983        self.student.sex = u'm'
984        bed1.bed_type = u'regular_female_reserved'
985        notify(grok.ObjectModifiedEvent(bed1))
986        self.browser.getLink("Relocate student").click()
987        self.assertMatches(
988            "...Students in reserved beds can't be relocated...",
989            self.browser.contents)
990        # Managers can relocate students if booking has been cancelled but
991        # other bed space has been manually allocated after cancellation
992        old_owner = bed1.releaseBed()
993        self.assertMatches(old_owner, self.student_id)
994        bed2.owner = self.student_id
995        self.browser.open(self.acco_path + '/2004')
996        self.assertMatches(
997            "...booking cancelled...", self.browser.contents)
998        self.browser.getLink("Relocate student").click()
999        # We didn't informed the catalog therefore the new owner is not found
1000        self.assertMatches(
1001            "...There is no free bed in your category regular_male_fr...",
1002            self.browser.contents)
1003        # Now we fire the event properly
1004        notify(grok.ObjectModifiedEvent(bed2))
1005        self.browser.getLink("Relocate student").click()
1006        self.assertMatches(
1007            "...Student relocated...", self.browser.contents)
1008        self.assertMatches(
1009            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1010          # Managers can delete bed tickets
1011        self.browser.open(self.acco_path)
1012        ctrl = self.browser.getControl(name='val_id')
1013        value = ctrl.options[0]
1014        ctrl.getControl(value=value).selected = True
1015        self.browser.getControl("Remove selected", index=0).click()
1016        self.assertMatches('...Successfully removed...', self.browser.contents)
1017        # The bed has been properly released by the event handler
1018        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1019        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1020        return
1021
1022    def test_manage_workflow(self):
1023        # Managers can pass through the whole workflow
1024        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1025        student = self.app['students'][self.student_id]
1026        self.browser.open(self.trigtrans_path)
1027        self.assertTrue(student.clearance_locked)
1028        self.browser.getControl(name="transition").value = ['admit']
1029        self.browser.getControl("Save").click()
1030        self.assertTrue(student.clearance_locked)
1031        self.browser.getControl(name="transition").value = ['start_clearance']
1032        self.browser.getControl("Save").click()
1033        self.assertFalse(student.clearance_locked)
1034        self.browser.getControl(name="transition").value = ['request_clearance']
1035        self.browser.getControl("Save").click()
1036        self.assertTrue(student.clearance_locked)
1037        self.browser.getControl(name="transition").value = ['clear']
1038        self.browser.getControl("Save").click()
1039        # Managers approve payment, they do not pay
1040        self.assertFalse('pay_first_school_fee' in self.browser.contents)
1041        self.browser.getControl(
1042            name="transition").value = ['approve_first_school_fee']
1043        self.browser.getControl("Save").click()
1044        self.browser.getControl(name="transition").value = ['reset6']
1045        self.browser.getControl("Save").click()
1046        # In state returning the pay_school_fee transition triggers some
1047        # changes of attributes
1048        self.browser.getControl(name="transition").value = ['approve_school_fee']
1049        self.browser.getControl("Save").click()
1050        self.assertEqual(student['studycourse'].current_session, 2005) # +1
1051        self.assertEqual(student['studycourse'].current_level, 200) # +100
1052        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = Zero = not set
1053        self.assertEqual(student['studycourse'].previous_verdict, 'A')
1054        self.browser.getControl(name="transition").value = ['register_courses']
1055        self.browser.getControl("Save").click()
1056        self.browser.getControl(name="transition").value = ['validate_courses']
1057        self.browser.getControl("Save").click()
1058        self.browser.getControl(name="transition").value = ['return']
1059        self.browser.getControl("Save").click()
1060        return
1061
1062    def test_manage_pg_workflow(self):
1063        # Managers can pass through the whole workflow
1064        IWorkflowState(self.student).setState('school fee paid')
1065        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1066        student = self.app['students'][self.student_id]
1067        self.browser.open(self.trigtrans_path)
1068        self.assertTrue('<option value="reset6">' in self.browser.contents)
1069        self.assertTrue('<option value="register_courses">' in self.browser.contents)
1070        self.assertTrue('<option value="reset5">' in self.browser.contents)
1071        self.certificate.study_mode = 'pg_ft'
1072        self.browser.open(self.trigtrans_path)
1073        self.assertFalse('<option value="reset6">' in self.browser.contents)
1074        self.assertFalse('<option value="register_courses">' in self.browser.contents)
1075        self.assertTrue('<option value="reset5">' in self.browser.contents)
1076        return
1077
1078    def test_manage_import(self):
1079        # Managers can import student data files
1080        datacenter_path = 'http://localhost/app/datacenter'
1081        # Prepare a csv file for students
1082        open('students.csv', 'wb').write(
1083"""firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,password
1084Aaren,Pieri,1,1990-01-02,100000,aa@aa.ng,1234,m,mypwd1
1085Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,m,mypwd1
1086Brit,Berson,3,1990-01-04,100001,aa@aa.ng,1234,m,mypwd1
1087""")
1088        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1089        self.browser.open(datacenter_path)
1090        self.browser.getLink('Upload data').click()
1091        filecontents = StringIO(open('students.csv', 'rb').read())
1092        filewidget = self.browser.getControl(name='uploadfile:file')
1093        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
1094        self.browser.getControl(name='SUBMIT').click()
1095        self.browser.getLink('Process data').click()
1096        button = lookup_submit_value(
1097            'select', 'students_zope.mgr.csv', self.browser)
1098        button.click()
1099        importerselect = self.browser.getControl(name='importer')
1100        modeselect = self.browser.getControl(name='mode')
1101        importerselect.getControl('Student Processor').selected = True
1102        modeselect.getControl(value='create').selected = True
1103        self.browser.getControl('Proceed to step 3').click()
1104        self.assertTrue('Header fields OK' in self.browser.contents)
1105        self.browser.getControl('Perform import').click()
1106        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1107        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
1108        self.assertTrue('Batch processing finished' in self.browser.contents)
1109        open('studycourses.csv', 'wb').write(
1110"""reg_number,matric_number,certificate,current_session,current_level
11111,,CERT1,2008,100
1112,100001,CERT1,2008,100
1113,100002,CERT1,2008,100
1114""")
1115        self.browser.open(datacenter_path)
1116        self.browser.getLink('Upload data').click()
1117        filecontents = StringIO(open('studycourses.csv', 'rb').read())
1118        filewidget = self.browser.getControl(name='uploadfile:file')
1119        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
1120        self.browser.getControl(name='SUBMIT').click()
1121        self.browser.getLink('Process data').click()
1122        button = lookup_submit_value(
1123            'select', 'studycourses_zope.mgr.csv', self.browser)
1124        button.click()
1125        importerselect = self.browser.getControl(name='importer')
1126        modeselect = self.browser.getControl(name='mode')
1127        importerselect.getControl(
1128            'StudentStudyCourse Processor (update only)').selected = True
1129        modeselect.getControl(value='create').selected = True
1130        self.browser.getControl('Proceed to step 3').click()
1131        self.assertTrue('Update mode only' in self.browser.contents)
1132        self.browser.getControl('Proceed to step 3').click()
1133        self.assertTrue('Header fields OK' in self.browser.contents)
1134        self.browser.getControl('Perform import').click()
1135        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1136        self.assertTrue('Successfully processed 2 rows'
1137                        in self.browser.contents)
1138        # The students are properly indexed and we can
1139        # thus find a student in  the department
1140        self.browser.open(self.manage_container_path)
1141        self.browser.getControl(name="searchtype").value = ['depcode']
1142        self.browser.getControl(name="searchterm").value = 'dep1'
1143        self.browser.getControl("Search").click()
1144        self.assertTrue('Aaren Pieri' in self.browser.contents)
1145        # We can search for a new student by name ...
1146        self.browser.getControl(name="searchtype").value = ['fullname']
1147        self.browser.getControl(name="searchterm").value = 'Claus'
1148        self.browser.getControl("Search").click()
1149        self.assertTrue('Claus Finau' in self.browser.contents)
1150        # ... and check if the imported password has been properly set
1151        ctrl = self.browser.getControl(name='entries')
1152        value = ctrl.options[0]
1153        claus = self.app['students'][value]
1154        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
1155        return
1156
1157    def test_handle_clearance_by_co(self):
1158        # Create clearance officer
1159        self.app['users'].addUser('mrclear', 'mrclearsecret')
1160        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
1161        self.app['users']['mrclear'].title = 'Carlo Pitter'
1162        # Clearance officers need not necessarily to get
1163        # the StudentsOfficer site role
1164        #prmglobal = IPrincipalRoleManager(self.app)
1165        #prmglobal.assignRoleToPrincipal('waeup.StudentsOfficer', 'mrclear')
1166        # Assign local ClearanceOfficer role
1167        department = self.app['faculties']['fac1']['dep1']
1168        prmlocal = IPrincipalRoleManager(department)
1169        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
1170        IWorkflowState(self.student).setState('clearance started')
1171        # Login as clearance officer
1172        self.browser.open(self.login_path)
1173        self.browser.getControl(name="form.login").value = 'mrclear'
1174        self.browser.getControl(name="form.password").value = 'mrclearsecret'
1175        self.browser.getControl("Login").click()
1176        self.assertMatches('...You logged in...', self.browser.contents)
1177        # CO can see his roles
1178        self.browser.getLink("My Roles").click()
1179        self.assertMatches(
1180            '...<div>Academics Officer (view only)</div>...',
1181            self.browser.contents)
1182        #self.assertMatches(
1183        #    '...<div>Students Officer (view only)</div>...',
1184        #    self.browser.contents)
1185        # But not his local role ...
1186        self.assertFalse('Clearance Officer' in self.browser.contents)
1187        # ... because we forgot to notify the department that the local role
1188        # has changed
1189        notify(LocalRoleSetEvent(
1190            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
1191        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1192        self.assertTrue('Clearance Officer' in self.browser.contents)
1193        self.assertMatches(
1194            '...<a href="http://localhost/app/faculties/fac1/dep1">...',
1195            self.browser.contents)
1196        # CO can view the student ...
1197        self.browser.open(self.clearance_path)
1198        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1199        self.assertEqual(self.browser.url, self.clearance_path)
1200        # ... but not other students
1201        other_student = Student()
1202        other_student.firstname = u'Dep2'
1203        other_student.lastname = u'Student'
1204        self.app['students'].addStudent(other_student)
1205        other_student_path = (
1206            'http://localhost/app/students/%s' % other_student.student_id)
1207        self.assertRaises(
1208            Unauthorized, self.browser.open, other_student_path)
1209        # Only in state clearance requested the CO does see the 'Clear' button
1210        self.browser.open(self.clearance_path)
1211        self.assertFalse('Clear student' in self.browser.contents)
1212        IWorkflowInfo(self.student).fireTransition('request_clearance')
1213        self.browser.open(self.clearance_path)
1214        self.assertTrue('Clear student' in self.browser.contents)
1215        self.browser.getLink("Clear student").click()
1216        self.assertTrue('Student has been cleared' in self.browser.contents)
1217        self.assertTrue('cleared' in self.browser.contents)
1218        self.browser.open(self.history_path)
1219        self.assertTrue('Cleared by Carlo Pitter' in self.browser.contents)
1220        # Hide real name.
1221        self.app['users']['mrclear'].public_name = 'My Public Name'
1222        self.browser.open(self.clearance_path)
1223        self.browser.getLink("Reject clearance").click()
1224        self.assertEqual(
1225            self.browser.url, self.student_path + '/reject_clearance')
1226        # Type comment why
1227        self.browser.getControl(name="form.officer_comment").value = """Dear Student,
1228You did not fill properly.
1229"""
1230        self.browser.getControl("Save comment").click()
1231        self.assertTrue('Clearance has been annulled' in self.browser.contents)
1232        url = ('http://localhost/app/students/K1000000/'
1233              'contactstudent?body=Dear+Student%2C%0AYou+did+not+fill+properly.'
1234              '%0A&subject=Clearance+has+been+annulled.')
1235        # CO does now see the prefilled contact form and can send a message
1236        self.assertEqual(self.browser.url, url)
1237        self.assertTrue('clearance started' in self.browser.contents)
1238        self.assertTrue('name="form.subject" size="20" type="text" '
1239            'value="Clearance has been annulled."'
1240            in self.browser.contents)
1241        self.assertTrue('name="form.body" rows="10" >Dear Student,'
1242            in self.browser.contents)
1243        self.browser.getControl("Send message now").click()
1244        self.assertTrue('Your message has been sent' in self.browser.contents)
1245        # The comment has been stored ...
1246        self.assertEqual(self.student.officer_comment,
1247            u'Dear Student,\nYou did not fill properly.\n')
1248        # ... and logged
1249        logfile = os.path.join(
1250            self.app['datacenter'].storage, 'logs', 'students.log')
1251        logcontent = open(logfile).read()
1252        self.assertTrue(
1253            'INFO - mrclear - students.browser.StudentRejectClearancePage - '
1254            'K1000000 - comment: Dear Student,<br>You did not fill '
1255            'properly.<br>\n' in logcontent)
1256        self.browser.open(self.history_path)
1257        self.assertTrue("Reset to 'clearance started' by My Public Name" in
1258            self.browser.contents)
1259        IWorkflowInfo(self.student).fireTransition('request_clearance')
1260        self.browser.open(self.clearance_path)
1261        self.browser.getLink("Reject clearance").click()
1262        self.browser.getControl("Save comment").click()
1263        self.assertTrue('Clearance request has been rejected'
1264            in self.browser.contents)
1265        self.assertTrue('clearance started' in self.browser.contents)
1266        # The CO can't clear students if not in state
1267        # clearance requested
1268        self.browser.open(self.student_path + '/clear')
1269        self.assertTrue('Student is in wrong state'
1270            in self.browser.contents)
1271        # The CO can go to his department throug the my_roles page ...
1272        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1273        self.browser.getLink("http://localhost/app/faculties/fac1/dep1").click()
1274        # ... and view the list of students
1275        self.browser.getLink("Show students").click()
1276        self.assertTrue(self.student_id in self.browser.contents)
1277        # The comment is indicated by 'yes'
1278        self.assertTrue('<td><span>yes</span></td>' in self.browser.contents)
1279        # When a student is cleared the comment is automatically deleted
1280        IWorkflowInfo(self.student).fireTransition('request_clearance')
1281        IWorkflowInfo(self.student).fireTransition('clear')
1282        self.assertEqual(self.student.officer_comment, None)
1283
1284    def test_handle_courses_by_ca(self):
1285        # Create course adviser
1286        self.app['users'].addUser('mrsadvise', 'mrsadvisesecret')
1287        self.app['users']['mrsadvise'].email = 'mradvise@foo.ng'
1288        self.app['users']['mrsadvise'].title = u'Helen Procter'
1289        # Assign local CourseAdviser100 role for a certificate
1290        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1291        prmlocal = IPrincipalRoleManager(cert)
1292        prmlocal.assignRoleToPrincipal('waeup.local.CourseAdviser100', 'mrsadvise')
1293        IWorkflowState(self.student).setState('school fee paid')
1294        # Login as course adviser
1295        self.browser.open(self.login_path)
1296        self.browser.getControl(name="form.login").value = 'mrsadvise'
1297        self.browser.getControl(name="form.password").value = 'mrsadvisesecret'
1298        self.browser.getControl("Login").click()
1299        self.assertMatches('...You logged in...', self.browser.contents)
1300        # CO can see his roles
1301        self.browser.getLink("My Roles").click()
1302        self.assertMatches(
1303            '...<div>Academics Officer (view only)</div>...',
1304            self.browser.contents)
1305        # But not his local role ...
1306        self.assertFalse('Course Adviser' in self.browser.contents)
1307        # ... because we forgot to notify the certificate that the local role
1308        # has changed
1309        notify(LocalRoleSetEvent(
1310            cert, 'waeup.local.CourseAdviser100', 'mrsadvise', granted=True))
1311        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1312        self.assertTrue('Course Adviser 100L' in self.browser.contents)
1313        self.assertMatches(
1314            '...<a href="http://localhost/app/faculties/fac1/dep1/certificates/CERT1">...',
1315            self.browser.contents)
1316        # CA can view the student ...
1317        self.browser.open(self.student_path)
1318        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1319        self.assertEqual(self.browser.url, self.student_path)
1320        # ... but not other students
1321        other_student = Student()
1322        other_student.firstname = u'Dep2'
1323        other_student.lastname = u'Student'
1324        self.app['students'].addStudent(other_student)
1325        other_student_path = (
1326            'http://localhost/app/students/%s' % other_student.student_id)
1327        self.assertRaises(
1328            Unauthorized, self.browser.open, other_student_path)
1329        # We add study level 110 to the student's studycourse
1330        studylevel = StudentStudyLevel()
1331        studylevel.level = 110
1332        self.student['studycourse'].addStudentStudyLevel(
1333            cert,studylevel)
1334        L110_student_path = self.studycourse_path + '/110'
1335        # Only in state courses registered and only if the current level
1336        # corresponds with the name of the study level object
1337        # the 100L CA does see the 'Validate' button
1338        self.browser.open(L110_student_path)
1339        self.assertFalse('Validate courses' in self.browser.contents)
1340        IWorkflowInfo(self.student).fireTransition('register_courses')
1341        self.browser.open(L110_student_path)
1342        self.assertFalse('Validate courses' in self.browser.contents)
1343        self.student['studycourse'].current_level = 110
1344        self.browser.open(L110_student_path)
1345        self.assertTrue('Validate courses' in self.browser.contents)
1346        # ... but a 100L CA does not see the button on other levels
1347        studylevel2 = StudentStudyLevel()
1348        studylevel2.level = 200
1349        self.student['studycourse'].addStudentStudyLevel(
1350            cert,studylevel2)
1351        L200_student_path = self.studycourse_path + '/200'
1352        self.browser.open(L200_student_path)
1353        self.assertFalse('Validate courses' in self.browser.contents)
1354        self.browser.open(L110_student_path)
1355        self.browser.getLink("Validate courses").click()
1356        self.assertTrue('Course list has been validated' in self.browser.contents)
1357        self.assertTrue('courses validated' in self.browser.contents)
1358        self.assertEqual(self.student['studycourse']['110'].validated_by,
1359            'Helen Procter')
1360        self.assertMatches(
1361            '<YYYY-MM-DD hh:mm:ss>',
1362            self.student['studycourse']['110'].validation_date.strftime(
1363                "%Y-%m-%d %H:%M:%S"))
1364        self.browser.getLink("Reject courses").click()
1365        self.assertTrue('Course list request has been annulled.'
1366            in self.browser.contents)
1367        urlmessage = 'Course+list+request+has+been+annulled.'
1368        self.assertEqual(self.browser.url, self.student_path +
1369            '/contactstudent?subject=%s' % urlmessage)
1370        self.assertTrue('school fee paid' in self.browser.contents)
1371        self.assertTrue(self.student['studycourse']['110'].validated_by is None)
1372        self.assertTrue(self.student['studycourse']['110'].validation_date is None)
1373        IWorkflowInfo(self.student).fireTransition('register_courses')
1374        self.browser.open(L110_student_path)
1375        self.browser.getLink("Reject courses").click()
1376        self.assertTrue('Course list request has been rejected'
1377            in self.browser.contents)
1378        self.assertTrue('school fee paid' in self.browser.contents)
1379        # CA does now see the contact form and can send a message
1380        self.browser.getControl(name="form.subject").value = 'Important subject'
1381        self.browser.getControl(name="form.body").value = 'Course list rejected'
1382        self.browser.getControl("Send message now").click()
1383        self.assertTrue('Your message has been sent' in self.browser.contents)
1384        # The CA can't validate courses if not in state
1385        # courses registered
1386        self.browser.open(L110_student_path + '/validate_courses')
1387        self.assertTrue('Student is in the wrong state'
1388            in self.browser.contents)
1389        # The CA can go to his certificate through the my_roles page
1390        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1391        self.browser.getLink(
1392            "http://localhost/app/faculties/fac1/dep1/certificates/CERT1").click()
1393        # and view the list of students
1394        self.browser.getLink("Show students").click()
1395        self.assertTrue(self.student_id in self.browser.contents)
1396
1397    def test_change_current_mode(self):
1398        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1399        self.browser.open(self.clearance_path)
1400        self.assertFalse('Employer' in self.browser.contents)
1401        self.browser.open(self.manage_clearance_path)
1402        self.assertFalse('Employer' in self.browser.contents)
1403        self.student.clearance_locked = False
1404        self.browser.open(self.edit_clearance_path)
1405        self.assertFalse('Employer' in self.browser.contents)
1406        # Now we change the study mode of the certificate and a different
1407        # interface is used by clearance views.
1408        self.certificate.study_mode = 'pg_ft'
1409        # Invariants are not being checked here?!
1410        self.certificate.end_level = 100
1411        self.browser.open(self.clearance_path)
1412        self.assertTrue('Employer' in self.browser.contents)
1413        self.browser.open(self.manage_clearance_path)
1414        self.assertTrue('Employer' in self.browser.contents)
1415        self.browser.open(self.edit_clearance_path)
1416        self.assertTrue('Employer' in self.browser.contents)
1417
1418    def test_activate_deactivate_buttons(self):
1419        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1420        self.browser.open(self.student_path)
1421        self.browser.getLink("Deactivate").click()
1422        self.assertTrue(
1423            'Student account has been deactivated.' in self.browser.contents)
1424        self.assertTrue(
1425            'Base Data (account deactivated)' in self.browser.contents)
1426        self.assertTrue(self.student.suspended)
1427        self.browser.getLink("Activate").click()
1428        self.assertTrue(
1429            'Student account has been activated.' in self.browser.contents)
1430        self.assertFalse(
1431            'Base Data (account deactivated)' in self.browser.contents)
1432        self.assertFalse(self.student.suspended)
1433        # History messages have been added ...
1434        self.browser.getLink("History").click()
1435        self.assertTrue(
1436            'Student account deactivated by Manager<br />' in self.browser.contents)
1437        self.assertTrue(
1438            'Student account activated by Manager<br />' in self.browser.contents)
1439        # ... and actions have been logged.
1440        logfile = os.path.join(
1441            self.app['datacenter'].storage, 'logs', 'students.log')
1442        logcontent = open(logfile).read()
1443        self.assertTrue('zope.mgr - students.browser.StudentDeactivatePage - '
1444                        'K1000000 - account deactivated' in logcontent)
1445        self.assertTrue('zope.mgr - students.browser.StudentActivatePage - '
1446                        'K1000000 - account activated' in logcontent)
1447
1448    def test_manage_student_transfer(self):
1449        # Add second certificate
1450        self.certificate2 = createObject('waeup.Certificate')
1451        self.certificate2.code = u'CERT2'
1452        self.certificate2.study_mode = 'ug_ft'
1453        self.certificate2.start_level = 999
1454        self.certificate2.end_level = 999
1455        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1456            self.certificate2)
1457
1458        # Add study level to old study course
1459        studylevel = createObject(u'waeup.StudentStudyLevel')
1460        studylevel.level = 200
1461        self.student['studycourse'].addStudentStudyLevel(
1462            self.certificate, studylevel)
1463        studylevel = createObject(u'waeup.StudentStudyLevel')
1464        studylevel.level = 999
1465        self.student['studycourse'].addStudentStudyLevel(
1466            self.certificate, studylevel)
1467
1468        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1469        self.browser.open(self.student_path)
1470        self.browser.getLink("Transfer").click()
1471        self.browser.getControl(name="form.certificate").value = ['CERT2']
1472        self.browser.getControl(name="form.current_session").value = ['2011']
1473        self.browser.getControl(name="form.current_level").value = ['200']
1474        self.browser.getControl("Transfer").click()
1475        self.assertTrue(
1476            'Current level does not match certificate levels'
1477            in self.browser.contents)
1478        self.browser.getControl(name="form.current_level").value = ['999']
1479        self.browser.getControl("Transfer").click()
1480        self.assertTrue('Successfully transferred' in self.browser.contents)
1481        # The catalog has been updated
1482        cat = queryUtility(ICatalog, name='students_catalog')
1483        results = list(
1484            cat.searchResults(
1485            certcode=('CERT2', 'CERT2')))
1486        self.assertTrue(results[0] is self.student)
1487        results = list(
1488            cat.searchResults(
1489            current_session=(2011, 2011)))
1490        self.assertTrue(results[0] is self.student)
1491        # Add study level to new study course
1492        studylevel = createObject(u'waeup.StudentStudyLevel')
1493        studylevel.level = 999
1494        self.student['studycourse'].addStudentStudyLevel(
1495            self.certificate, studylevel)
1496
1497        # Edit and add pages are locked for old study courses
1498        self.browser.open(self.student_path + '/studycourse/manage')
1499        self.assertFalse('The requested form is locked' in self.browser.contents)
1500        self.browser.open(self.student_path + '/studycourse_1/manage')
1501        self.assertTrue('The requested form is locked' in self.browser.contents)
1502
1503        self.browser.open(self.student_path + '/studycourse/start_session')
1504        self.assertFalse('The requested form is locked' in self.browser.contents)
1505        self.browser.open(self.student_path + '/studycourse_1/start_session')
1506        self.assertTrue('The requested form is locked' in self.browser.contents)
1507
1508        IWorkflowState(self.student).setState('school fee paid')
1509        self.browser.open(self.student_path + '/studycourse/add')
1510        self.assertFalse('The requested form is locked' in self.browser.contents)
1511        self.browser.open(self.student_path + '/studycourse_1/add')
1512        self.assertTrue('The requested form is locked' in self.browser.contents)
1513
1514        self.browser.open(self.student_path + '/studycourse/999/manage')
1515        self.assertFalse('The requested form is locked' in self.browser.contents)
1516        self.browser.open(self.student_path + '/studycourse_1/999/manage')
1517        self.assertTrue('The requested form is locked' in self.browser.contents)
1518
1519        self.browser.open(self.student_path + '/studycourse/999/validate_courses')
1520        self.assertFalse('The requested form is locked' in self.browser.contents)
1521        self.browser.open(self.student_path + '/studycourse_1/999/validate_courses')
1522        self.assertTrue('The requested form is locked' in self.browser.contents)
1523
1524        self.browser.open(self.student_path + '/studycourse/999/reject_courses')
1525        self.assertFalse('The requested form is locked' in self.browser.contents)
1526        self.browser.open(self.student_path + '/studycourse_1/999/reject_courses')
1527        self.assertTrue('The requested form is locked' in self.browser.contents)
1528
1529        self.browser.open(self.student_path + '/studycourse/999/add')
1530        self.assertFalse('The requested form is locked' in self.browser.contents)
1531        self.browser.open(self.student_path + '/studycourse_1/999/add')
1532        self.assertTrue('The requested form is locked' in self.browser.contents)
1533
1534        self.browser.open(self.student_path + '/studycourse/999/edit')
1535        self.assertFalse('The requested form is locked' in self.browser.contents)
1536        self.browser.open(self.student_path + '/studycourse_1/999/edit')
1537        self.assertTrue('The requested form is locked' in self.browser.contents)
1538
1539    def test_login_as_student(self):
1540        # StudentImpersonators can login as student
1541        # Create clearance officer
1542        self.app['users'].addUser('mrofficer', 'mrofficersecret')
1543        self.app['users']['mrofficer'].email = 'mrofficer@foo.ng'
1544        self.app['users']['mrofficer'].title = 'Harry Actor'
1545        prmglobal = IPrincipalRoleManager(self.app)
1546        prmglobal.assignRoleToPrincipal('waeup.StudentImpersonator', 'mrofficer')
1547        prmglobal.assignRoleToPrincipal('waeup.StudentsManager', 'mrofficer')
1548        # Login as student impersonator
1549        self.browser.open(self.login_path)
1550        self.browser.getControl(name="form.login").value = 'mrofficer'
1551        self.browser.getControl(name="form.password").value = 'mrofficersecret'
1552        self.browser.getControl("Login").click()
1553        self.assertMatches('...You logged in...', self.browser.contents)
1554        self.browser.open(self.student_path)
1555        self.browser.getLink("Login as").click()
1556        self.browser.getControl("Set password now").click()
1557        temp_password = self.browser.getControl(name='form.password').value
1558        self.browser.getControl("Login now").click()
1559        self.assertMatches(
1560            '...You successfully logged in as...', self.browser.contents)
1561        # We are logged in as student and can see the 'My Data' tab
1562        self.assertMatches(
1563            '...<a href="#" class="dropdown-toggle">My Data</a>...',
1564            self.browser.contents)
1565        self.browser.getLink("Logout").click()
1566        # The student can't login with the original password ...
1567        self.browser.open(self.login_path)
1568        self.browser.getControl(name="form.login").value = self.student_id
1569        self.browser.getControl(name="form.password").value = 'spwd'
1570        self.browser.getControl("Login").click()
1571        self.assertMatches(
1572            '...Your account has been temporarily deactivated...',
1573            self.browser.contents)
1574        # ... but with the temporary password
1575        self.browser.open(self.login_path)
1576        self.browser.getControl(name="form.login").value = self.student_id
1577        self.browser.getControl(name="form.password").value = temp_password
1578        self.browser.getControl("Login").click()
1579        self.assertMatches('...You logged in...', self.browser.contents)
1580        # Creation of temp_password is properly logged
1581        logfile = os.path.join(
1582            self.app['datacenter'].storage, 'logs', 'students.log')
1583        logcontent = open(logfile).read()
1584        self.assertTrue(
1585            'mrofficer - students.browser.LoginAsStudentStep1 - K1000000 - '
1586            'temp_password generated: %s' % temp_password in logcontent)
1587
1588class StudentUITests(StudentsFullSetup):
1589    # Tests for Student class views and pages
1590
1591    def test_student_change_password(self):
1592        # Students can change the password
1593        self.browser.open(self.login_path)
1594        self.browser.getControl(name="form.login").value = self.student_id
1595        self.browser.getControl(name="form.password").value = 'spwd'
1596        self.browser.getControl("Login").click()
1597        self.assertEqual(self.browser.url, self.student_path)
1598        self.assertTrue('You logged in' in self.browser.contents)
1599        # Change password
1600        self.browser.getLink("Change password").click()
1601        self.browser.getControl(name="change_password").value = 'pw'
1602        self.browser.getControl(
1603            name="change_password_repeat").value = 'pw'
1604        self.browser.getControl("Save").click()
1605        self.assertTrue('Password must have at least' in self.browser.contents)
1606        self.browser.getControl(name="change_password").value = 'new_password'
1607        self.browser.getControl(
1608            name="change_password_repeat").value = 'new_passssword'
1609        self.browser.getControl("Save").click()
1610        self.assertTrue('Passwords do not match' in self.browser.contents)
1611        self.browser.getControl(name="change_password").value = 'new_password'
1612        self.browser.getControl(
1613            name="change_password_repeat").value = 'new_password'
1614        self.browser.getControl("Save").click()
1615        self.assertTrue('Password changed' in self.browser.contents)
1616        # We are still logged in. Changing the password hasn't thrown us out.
1617        self.browser.getLink("Base Data").click()
1618        self.assertEqual(self.browser.url, self.student_path)
1619        # We can logout
1620        self.browser.getLink("Logout").click()
1621        self.assertTrue('You have been logged out' in self.browser.contents)
1622        self.assertEqual(self.browser.url, 'http://localhost/app')
1623        # We can login again with the new password
1624        self.browser.getLink("Login").click()
1625        self.browser.open(self.login_path)
1626        self.browser.getControl(name="form.login").value = self.student_id
1627        self.browser.getControl(name="form.password").value = 'new_password'
1628        self.browser.getControl("Login").click()
1629        self.assertEqual(self.browser.url, self.student_path)
1630        self.assertTrue('You logged in' in self.browser.contents)
1631        return
1632
1633    def test_setpassword(self):
1634        # Set password for first-time access
1635        student = Student()
1636        student.reg_number = u'123456'
1637        student.firstname = u'Klaus'
1638        student.lastname = u'Tester'
1639        self.app['students'].addStudent(student)
1640        setpassword_path = 'http://localhost/app/setpassword'
1641        student_path = 'http://localhost/app/students/%s' % student.student_id
1642        self.browser.open(setpassword_path)
1643        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
1644        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1645        self.browser.getControl(name="reg_number").value = '223456'
1646        self.browser.getControl("Set").click()
1647        self.assertMatches('...No student found...',
1648                           self.browser.contents)
1649        self.browser.getControl(name="reg_number").value = '123456'
1650        self.browser.getControl(name="ac_number").value = '999999'
1651        self.browser.getControl("Set").click()
1652        self.assertMatches('...Access code is invalid...',
1653                           self.browser.contents)
1654        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1655        self.browser.getControl("Set").click()
1656        self.assertMatches('...Password has been set. Your Student Id is...',
1657                           self.browser.contents)
1658        self.browser.getControl("Set").click()
1659        self.assertMatches(
1660            '...Password has already been set. Your Student Id is...',
1661            self.browser.contents)
1662        existing_pwdpin = self.pwdpins[1]
1663        parts = existing_pwdpin.split('-')[1:]
1664        existing_pwdseries, existing_pwdnumber = parts
1665        self.browser.getControl(name="ac_series").value = existing_pwdseries
1666        self.browser.getControl(name="ac_number").value = existing_pwdnumber
1667        self.browser.getControl(name="reg_number").value = '123456'
1668        self.browser.getControl("Set").click()
1669        self.assertMatches(
1670            '...You are using the wrong Access Code...',
1671            self.browser.contents)
1672        # The student can login with the new credentials
1673        self.browser.open(self.login_path)
1674        self.browser.getControl(name="form.login").value = student.student_id
1675        self.browser.getControl(
1676            name="form.password").value = self.existing_pwdnumber
1677        self.browser.getControl("Login").click()
1678        self.assertEqual(self.browser.url, student_path)
1679        self.assertTrue('You logged in' in self.browser.contents)
1680        return
1681
1682    def test_student_login(self):
1683        # Student cant login if their password is not set
1684        self.student.password = None
1685        self.browser.open(self.login_path)
1686        self.browser.getControl(name="form.login").value = self.student_id
1687        self.browser.getControl(name="form.password").value = 'spwd'
1688        self.browser.getControl("Login").click()
1689        self.assertTrue(
1690            'You entered invalid credentials.' in self.browser.contents)
1691        # We set the password again
1692        IUserAccount(
1693            self.app['students'][self.student_id]).setPassword('spwd')
1694        # Students can't login if their account is suspended/deactivated
1695        self.student.suspended = True
1696        self.browser.open(self.login_path)
1697        self.browser.getControl(name="form.login").value = self.student_id
1698        self.browser.getControl(name="form.password").value = 'spwd'
1699        self.browser.getControl("Login").click()
1700        self.assertMatches(
1701            '...Your account has been deactivated...', self.browser.contents)
1702        self.student.suspended = False
1703        # Students can't login if a temporary password has been set and
1704        # is not expired
1705        self.app['students'][self.student_id].setTempPassword(
1706            'anybody', 'temp_spwd')
1707        self.browser.open(self.login_path)
1708        self.browser.getControl(name="form.login").value = self.student_id
1709        self.browser.getControl(name="form.password").value = 'spwd'
1710        self.browser.getControl("Login").click()
1711        self.assertMatches(
1712            '...Your account has been temporarily deactivated...',
1713            self.browser.contents)
1714        # The student can login with the temporary password
1715        self.browser.open(self.login_path)
1716        self.browser.getControl(name="form.login").value = self.student_id
1717        self.browser.getControl(name="form.password").value = 'temp_spwd'
1718        self.browser.getControl("Login").click()
1719        self.assertMatches(
1720            '...You logged in...', self.browser.contents)
1721        # Student can view the base data
1722        self.browser.open(self.student_path)
1723        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1724        self.assertEqual(self.browser.url, self.student_path)
1725        # When the password expires ...
1726        delta = timedelta(minutes=11)
1727        self.app['students'][self.student_id].temp_password[
1728            'timestamp'] = datetime.utcnow() - delta
1729        self.app['students'][self.student_id]._p_changed = True
1730        # ... the student will be automatically logged out
1731        self.assertRaises(
1732            Unauthorized, self.browser.open, self.student_path)
1733        # Then the student can login with the original password
1734        self.browser.open(self.login_path)
1735        self.browser.getControl(name="form.login").value = self.student_id
1736        self.browser.getControl(name="form.password").value = 'spwd'
1737        self.browser.getControl("Login").click()
1738        self.assertMatches(
1739            '...You logged in...', self.browser.contents)
1740
1741    def test_student_clearance(self):
1742        # Student cant login if their password is not set
1743        IWorkflowInfo(self.student).fireTransition('admit')
1744        self.browser.open(self.login_path)
1745        self.browser.getControl(name="form.login").value = self.student_id
1746        self.browser.getControl(name="form.password").value = 'spwd'
1747        self.browser.getControl("Login").click()
1748        self.assertMatches(
1749            '...You logged in...', self.browser.contents)
1750        # Admitted student can upload a passport picture
1751        self.browser.open(self.student_path + '/change_portrait')
1752        ctrl = self.browser.getControl(name='passportuploadedit')
1753        file_obj = open(SAMPLE_IMAGE, 'rb')
1754        file_ctrl = ctrl.mech_control
1755        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1756        self.browser.getControl(
1757            name='upload_passportuploadedit').click()
1758        self.assertTrue(
1759            '<img align="middle" height="125px" src="passport.jpg" />'
1760            in self.browser.contents)
1761        # Students can open admission letter
1762        self.browser.getLink("Base Data").click()
1763        self.browser.getLink("Download admission letter").click()
1764        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1765        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1766        # Student can view the clearance data
1767        self.browser.open(self.student_path)
1768        self.browser.getLink("Clearance Data").click()
1769        # Student can't open clearance edit form before starting clearance
1770        self.browser.open(self.student_path + '/cedit')
1771        self.assertMatches('...The requested form is locked...',
1772                           self.browser.contents)
1773        self.browser.getLink("Clearance Data").click()
1774        self.browser.getLink("Start clearance").click()
1775        self.student.email = None
1776        # Uups, we forgot to fill the email fields
1777        self.browser.getControl("Start clearance").click()
1778        self.assertMatches('...Not all required fields filled...',
1779                           self.browser.contents)
1780        self.browser.open(self.student_path + '/edit_base')
1781        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1782        self.browser.getControl("Save").click()
1783        self.browser.open(self.student_path + '/start_clearance')
1784        self.browser.getControl(name="ac_series").value = '3'
1785        self.browser.getControl(name="ac_number").value = '4444444'
1786        self.browser.getControl("Start clearance now").click()
1787        self.assertMatches('...Activation code is invalid...',
1788                           self.browser.contents)
1789        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1790        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1791        # Owner is Hans Wurst, AC can't be invalidated
1792        self.browser.getControl("Start clearance now").click()
1793        self.assertMatches('...You are not the owner of this access code...',
1794                           self.browser.contents)
1795        # Set the correct owner
1796        self.existing_clrac.owner = self.student_id
1797        # clr_code might be set (and thus returns None) due importing
1798        # an empty clr_code column.
1799        self.student.clr_code = None
1800        self.browser.getControl("Start clearance now").click()
1801        self.assertMatches('...Clearance process has been started...',
1802                           self.browser.contents)
1803        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1804        self.browser.getControl("Save", index=0).click()
1805        # Student can view the clearance data
1806        self.browser.getLink("Clearance Data").click()
1807        # and go back to the edit form
1808        self.browser.getLink("Edit").click()
1809        # Students can upload documents
1810        ctrl = self.browser.getControl(name='birthcertificateupload')
1811        file_obj = open(SAMPLE_IMAGE, 'rb')
1812        file_ctrl = ctrl.mech_control
1813        file_ctrl.add_file(file_obj, filename='my_birth_certificate.jpg')
1814        self.browser.getControl(
1815            name='upload_birthcertificateupload').click()
1816        self.assertTrue(
1817            '<a target="image" href="birth_certificate">Birth Certificate Scan</a>'
1818            in self.browser.contents)
1819        # Students can open clearance slip
1820        self.browser.getLink("View").click()
1821        self.browser.getLink("Download clearance slip").click()
1822        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1823        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1824        # Students can request clearance
1825        self.browser.open(self.edit_clearance_path)
1826        self.browser.getControl("Save and request clearance").click()
1827        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1828        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1829        self.browser.getControl("Request clearance now").click()
1830        self.assertMatches('...Clearance has been requested...',
1831                           self.browser.contents)
1832        # Student can't reopen clearance form after requesting clearance
1833        self.browser.open(self.student_path + '/cedit')
1834        self.assertMatches('...The requested form is locked...',
1835                           self.browser.contents)
1836
1837    def test_student_course_registration(self):
1838        # Student cant login if their password is not set
1839        IWorkflowInfo(self.student).fireTransition('admit')
1840        self.browser.open(self.login_path)
1841        self.browser.getControl(name="form.login").value = self.student_id
1842        self.browser.getControl(name="form.password").value = 'spwd'
1843        self.browser.getControl("Login").click()
1844        # Student can't add study level if not in state 'school fee paid'
1845        self.browser.open(self.student_path + '/studycourse/add')
1846        self.assertMatches('...The requested form is locked...',
1847                           self.browser.contents)
1848        # ... and must be transferred first
1849        IWorkflowState(self.student).setState('school fee paid')
1850        # Now students can add the current study level
1851        self.browser.getLink("Study Course").click()
1852        self.browser.getLink("Add course list").click()
1853        self.assertMatches('...Add current level 100 (Year 1)...',
1854                           self.browser.contents)
1855        self.browser.getControl("Create course list now").click()
1856        # A level with one course ticket was created
1857        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1858        self.browser.getLink("100").click()
1859        self.browser.getLink("Edit course list").click()
1860        self.browser.getControl("Add course ticket").click()
1861        self.browser.getControl(name="form.course").value = ['COURSE1']
1862        self.browser.getControl("Add course ticket").click()
1863        self.assertMatches('...The ticket exists...',
1864                           self.browser.contents)
1865        self.student['studycourse'].current_level = 200
1866        self.browser.getLink("Study Course").click()
1867        self.browser.getLink("Add course list").click()
1868        self.assertMatches('...Add current level 200 (Year 2)...',
1869                           self.browser.contents)
1870        self.browser.getControl("Create course list now").click()
1871        self.browser.getLink("200").click()
1872        self.browser.getLink("Edit course list").click()
1873        self.browser.getControl("Add course ticket").click()
1874        self.browser.getControl(name="form.course").value = ['COURSE1']
1875        self.browser.getControl("Add course ticket").click()
1876        self.assertMatches('...The ticket exists...',
1877                           self.browser.contents)
1878        # Indeed the ticket exists as carry-over course from level 100
1879        # since its score was 0
1880        self.assertTrue(
1881            self.student['studycourse']['200']['COURSE1'].carry_over is True)
1882        # Students can open the pdf course registration slip
1883        self.browser.open(self.student_path + '/studycourse/200')
1884        self.browser.getLink("Download course registration slip").click()
1885        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1886        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1887        # Students can remove course tickets
1888        self.browser.open(self.student_path + '/studycourse/200/edit')
1889        self.browser.getControl("Remove selected", index=0).click()
1890        self.assertTrue('No ticket selected' in self.browser.contents)
1891        # No ticket can be selected since the carry-over course is a core course
1892        self.assertRaises(
1893            LookupError, self.browser.getControl, name='val_id')
1894        self.student['studycourse']['200']['COURSE1'].mandatory = False
1895        self.browser.open(self.student_path + '/studycourse/200/edit')
1896        # Course list can't be registered if total_credits exceeds max_credits
1897        self.student['studycourse']['200']['COURSE1'].credits = 60
1898        self.browser.getControl("Register course list").click()
1899        self.assertTrue('Maximum credits of 50 exceeded' in self.browser.contents)
1900        # Student can now remove the ticket
1901        ctrl = self.browser.getControl(name='val_id')
1902        ctrl.getControl(value='COURSE1').selected = True
1903        self.browser.getControl("Remove selected", index=0).click()
1904        self.assertTrue('Successfully removed' in self.browser.contents)
1905        # Course list can be registered, even if it's empty
1906        self.browser.getControl("Register course list").click()
1907        self.assertTrue('Course list has been registered' in self.browser.contents)
1908        self.assertEqual(self.student.state, 'courses registered')
1909        return
1910
1911    def test_postgraduate_student_access(self):
1912        self.certificate.study_mode = 'pg_ft'
1913        self.certificate.start_level = 999
1914        self.certificate.end_level = 999
1915        self.student['studycourse'].current_level = 999
1916        IWorkflowState(self.student).setState('school fee paid')
1917        self.browser.open(self.login_path)
1918        self.browser.getControl(name="form.login").value = self.student_id
1919        self.browser.getControl(name="form.password").value = 'spwd'
1920        self.browser.getControl("Login").click()
1921        self.assertTrue(
1922            'You logged in.' in self.browser.contents)
1923        # Now students can add the current study level
1924        self.browser.getLink("Study Course").click()
1925        self.browser.getLink("Add course list").click()
1926        self.assertMatches('...Add current level Postgraduate Level...',
1927                           self.browser.contents)
1928        self.browser.getControl("Create course list now").click()
1929        # A level with one course ticket was created
1930        self.assertEqual(self.student['studycourse']['999'].number_of_tickets, 0)
1931        self.browser.getLink("999").click()
1932        self.browser.getLink("Edit course list").click()
1933        self.browser.getControl("Add course ticket").click()
1934        self.browser.getControl(name="form.course").value = ['COURSE1']
1935        self.browser.getControl("Add course ticket").click()
1936        self.assertMatches('...Successfully added COURSE1...',
1937                           self.browser.contents)
1938        # Postgraduate students can't register course lists
1939        self.browser.getControl("Register course list").click()
1940        self.assertTrue("your course list can't bee registered"
1941            in self.browser.contents)
1942        self.assertEqual(self.student.state, 'school fee paid')
1943        return
1944
1945    def test_student_clearance_wo_clrcode(self):
1946        IWorkflowState(self.student).setState('clearance started')
1947        self.browser.open(self.login_path)
1948        self.browser.getControl(name="form.login").value = self.student_id
1949        self.browser.getControl(name="form.password").value = 'spwd'
1950        self.browser.getControl("Login").click()
1951        self.student.clearance_locked = False
1952        self.browser.open(self.edit_clearance_path)
1953        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1954        self.browser.getControl("Save and request clearance").click()
1955        self.assertMatches('...Clearance has been requested...',
1956                           self.browser.contents)
1957
1958    def test_student_clearance_payment(self):
1959        # Login
1960        self.browser.open(self.login_path)
1961        self.browser.getControl(name="form.login").value = self.student_id
1962        self.browser.getControl(name="form.password").value = 'spwd'
1963        self.browser.getControl("Login").click()
1964
1965        # Students can add online clearance payment tickets
1966        self.browser.open(self.payments_path + '/addop')
1967        self.browser.getControl(name="form.p_category").value = ['clearance']
1968        self.browser.getControl("Create ticket").click()
1969        self.assertMatches('...ticket created...',
1970                           self.browser.contents)
1971
1972        # Students can't approve the payment
1973        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
1974        ctrl = self.browser.getControl(name='val_id')
1975        value = ctrl.options[0]
1976        self.browser.getLink(value).click()
1977        payment_url = self.browser.url
1978        self.assertRaises(
1979            Unauthorized, self.browser.open, payment_url + '/approve')
1980        # In the base package they can 'use' a fake approval view
1981        self.browser.open(payment_url + '/fake_approve')
1982        self.assertMatches('...Payment approved...',
1983                          self.browser.contents)
1984        expected = '''...
1985        <td>
1986          <span>Paid</span>
1987        </td>...'''
1988        expected = '''...
1989        <td>
1990          <span>Paid</span>
1991        </td>...'''
1992        self.assertMatches(expected,self.browser.contents)
1993        payment_id = self.student['payments'].keys()[0]
1994        payment = self.student['payments'][payment_id]
1995        self.assertEqual(payment.p_state, 'paid')
1996        self.assertEqual(payment.r_amount_approved, 3456.0)
1997        self.assertEqual(payment.r_code, 'AP')
1998        self.assertEqual(payment.r_desc, u'Payment approved by Anna Tester')
1999        # The new CLR-0 pin has been created
2000        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
2001        pin = self.app['accesscodes']['CLR-0'].keys()[0]
2002        ac = self.app['accesscodes']['CLR-0'][pin]
2003        self.assertEqual(ac.owner, self.student_id)
2004        self.assertEqual(ac.cost, 3456.0)
2005
2006        # Students can open the pdf payment slip
2007        self.browser.open(payment_url + '/payment_slip.pdf')
2008        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2009        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2010
2011        # The new CLR-0 pin can be used for starting clearance
2012        # but they have to upload a passport picture first
2013        # which is only possible in state admitted
2014        self.browser.open(self.student_path + '/change_portrait')
2015        self.assertMatches('...form is locked...',
2016                          self.browser.contents)
2017        IWorkflowInfo(self.student).fireTransition('admit')
2018        self.browser.open(self.student_path + '/change_portrait')
2019        image = open(SAMPLE_IMAGE, 'rb')
2020        ctrl = self.browser.getControl(name='passportuploadedit')
2021        file_ctrl = ctrl.mech_control
2022        file_ctrl.add_file(image, filename='my_photo.jpg')
2023        self.browser.getControl(
2024            name='upload_passportuploadedit').click()
2025        self.browser.open(self.student_path + '/start_clearance')
2026        parts = pin.split('-')[1:]
2027        clrseries, clrnumber = parts
2028        self.browser.getControl(name="ac_series").value = clrseries
2029        self.browser.getControl(name="ac_number").value = clrnumber
2030        self.browser.getControl("Start clearance now").click()
2031        self.assertMatches('...Clearance process has been started...',
2032                           self.browser.contents)
2033
2034    def test_student_schoolfee_payment(self):
2035        configuration = createObject('waeup.SessionConfiguration')
2036        configuration.academic_session = 2005
2037        self.app['configuration'].addSessionConfiguration(configuration)
2038        # Login
2039        self.browser.open(self.login_path)
2040        self.browser.getControl(name="form.login").value = self.student_id
2041        self.browser.getControl(name="form.password").value = 'spwd'
2042        self.browser.getControl("Login").click()
2043
2044        # Students can add online school fee payment tickets.
2045        IWorkflowState(self.student).setState('returning')
2046        self.browser.open(self.payments_path)
2047        self.assertRaises(
2048            LookupError, self.browser.getControl, name='val_id')
2049        self.browser.getLink("Add current session payment ticket").click()
2050        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2051        self.browser.getControl("Create ticket").click()
2052        self.assertMatches('...ticket created...',
2053                           self.browser.contents)
2054        ctrl = self.browser.getControl(name='val_id')
2055        value = ctrl.options[0]
2056        self.browser.getLink(value).click()
2057        self.assertMatches('...Amount Authorized...',
2058                           self.browser.contents)
2059        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2060        # Payment session and will be calculated as defined
2061        # in w.k.students.utils because we set changed the state
2062        # to returning
2063        self.assertEqual(self.student['payments'][value].p_session, 2005)
2064        self.assertEqual(self.student['payments'][value].p_level, 200)
2065
2066        # Student is the payee of the payment ticket.
2067        webservice = IPaymentWebservice(self.student['payments'][value])
2068        self.assertEqual(webservice.display_fullname, 'Anna Tester')
2069        self.assertEqual(webservice.id, self.student_id)
2070        self.assertEqual(webservice.faculty, 'fac1')
2071        self.assertEqual(webservice.department, 'dep1')
2072
2073        # We simulate the approval
2074        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2075        self.browser.open(self.browser.url + '/fake_approve')
2076        self.assertMatches('...Payment approved...',
2077                          self.browser.contents)
2078
2079        # The new SFE-0 pin can be used for starting new session
2080        self.browser.open(self.studycourse_path)
2081        self.browser.getLink('Start new session').click()
2082        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2083        parts = pin.split('-')[1:]
2084        sfeseries, sfenumber = parts
2085        self.browser.getControl(name="ac_series").value = sfeseries
2086        self.browser.getControl(name="ac_number").value = sfenumber
2087        self.browser.getControl("Start now").click()
2088        self.assertMatches('...Session started...',
2089                           self.browser.contents)
2090        self.assertTrue(self.student.state == 'school fee paid')
2091        return
2092
2093    def test_student_bedallocation_payment(self):
2094        # Login
2095        self.browser.open(self.login_path)
2096        self.browser.getControl(name="form.login").value = self.student_id
2097        self.browser.getControl(name="form.password").value = 'spwd'
2098        self.browser.getControl("Login").click()
2099        self.browser.open(self.payments_path)
2100        self.browser.open(self.payments_path + '/addop')
2101        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2102        self.browser.getControl("Create ticket").click()
2103        self.assertMatches('...ticket created...',
2104                           self.browser.contents)
2105        # Students can remove only online payment tickets which have
2106        # not received a valid callback
2107        self.browser.open(self.payments_path)
2108        ctrl = self.browser.getControl(name='val_id')
2109        value = ctrl.options[0]
2110        ctrl.getControl(value=value).selected = True
2111        self.browser.getControl("Remove selected", index=0).click()
2112        self.assertTrue('Successfully removed' in self.browser.contents)
2113
2114    def test_student_maintenance_payment(self):
2115        # Login
2116        self.browser.open(self.login_path)
2117        self.browser.getControl(name="form.login").value = self.student_id
2118        self.browser.getControl(name="form.password").value = 'spwd'
2119        self.browser.getControl("Login").click()
2120        self.browser.open(self.payments_path)
2121        self.browser.open(self.payments_path + '/addop')
2122        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2123        self.browser.getControl("Create ticket").click()
2124        self.assertMatches('...You have not yet booked accommodation...',
2125                           self.browser.contents)
2126        # We continue this test in test_student_accommodation
2127
2128    def test_student_previous_payments(self):
2129        configuration = createObject('waeup.SessionConfiguration')
2130        configuration.academic_session = 2000
2131        configuration.clearance_fee = 3456.0
2132        configuration.booking_fee = 123.4
2133        self.app['configuration'].addSessionConfiguration(configuration)
2134        configuration2 = createObject('waeup.SessionConfiguration')
2135        configuration2.academic_session = 2003
2136        configuration2.clearance_fee = 3456.0
2137        configuration2.booking_fee = 123.4
2138        self.app['configuration'].addSessionConfiguration(configuration2)
2139        configuration3 = createObject('waeup.SessionConfiguration')
2140        configuration3.academic_session = 2005
2141        configuration3.clearance_fee = 3456.0
2142        configuration3.booking_fee = 123.4
2143        self.app['configuration'].addSessionConfiguration(configuration3)
2144        self.student['studycourse'].entry_session = 2002
2145
2146        # Login
2147        self.browser.open(self.login_path)
2148        self.browser.getControl(name="form.login").value = self.student_id
2149        self.browser.getControl(name="form.password").value = 'spwd'
2150        self.browser.getControl("Login").click()
2151
2152        # Students can add previous school fee payment tickets in any state.
2153        IWorkflowState(self.student).setState('courses registered')
2154        self.browser.open(self.payments_path)
2155        self.browser.getLink("Add previous session payment ticket").click()
2156
2157        # Previous session payment form is provided
2158        self.assertEqual(self.student.current_session, 2004)
2159        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2160        self.browser.getControl(name="form.p_session").value = ['2000']
2161        self.browser.getControl(name="form.p_level").value = ['300']
2162        self.browser.getControl("Create ticket").click()
2163        self.assertMatches('...The previous session must not fall below...',
2164                           self.browser.contents)
2165        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2166        self.browser.getControl(name="form.p_session").value = ['2005']
2167        self.browser.getControl(name="form.p_level").value = ['300']
2168        self.browser.getControl("Create ticket").click()
2169        self.assertMatches('...This is not a previous session...',
2170                           self.browser.contents)
2171        # Students can pay current session school fee.
2172        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2173        self.browser.getControl(name="form.p_session").value = ['2004']
2174        self.browser.getControl(name="form.p_level").value = ['300']
2175        self.browser.getControl("Create ticket").click()
2176        self.assertMatches('...ticket created...',
2177                           self.browser.contents)
2178        ctrl = self.browser.getControl(name='val_id')
2179        value = ctrl.options[0]
2180        self.browser.getLink(value).click()
2181        self.assertMatches('...Amount Authorized...',
2182                           self.browser.contents)
2183        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2184
2185        # Payment session is properly set
2186        self.assertEqual(self.student['payments'][value].p_session, 2004)
2187        self.assertEqual(self.student['payments'][value].p_level, 300)
2188
2189        # We simulate the approval
2190        self.browser.open(self.browser.url + '/fake_approve')
2191        self.assertMatches('...Payment approved...',
2192                          self.browser.contents)
2193
2194        # No AC has been created
2195        self.assertEqual(len(self.app['accesscodes']['SFE-0'].keys()), 0)
2196        self.assertTrue(self.student['payments'][value].ac is None)
2197
2198        # Current payment flag is set False
2199        self.assertFalse(self.student['payments'][value].p_current)
2200
2201        # Button and form are not available for students who are in
2202        # states up to cleared
2203        self.student['studycourse'].entry_session = 2004
2204        IWorkflowState(self.student).setState('cleared')
2205        self.browser.open(self.payments_path)
2206        self.assertFalse(
2207            "Add previous session payment ticket" in self.browser.contents)
2208        self.browser.open(self.payments_path + '/addpp')
2209        self.assertTrue(
2210            "No previous payment to be made" in self.browser.contents)
2211        return
2212
2213    def test_postgraduate_student_payments(self):
2214        configuration = createObject('waeup.SessionConfiguration')
2215        configuration.academic_session = 2005
2216        self.app['configuration'].addSessionConfiguration(configuration)
2217        self.certificate.study_mode = 'pg_ft'
2218        self.certificate.start_level = 999
2219        self.certificate.end_level = 999
2220        self.student['studycourse'].current_level = 999
2221        # Login
2222        self.browser.open(self.login_path)
2223        self.browser.getControl(name="form.login").value = self.student_id
2224        self.browser.getControl(name="form.password").value = 'spwd'
2225        self.browser.getControl("Login").click()
2226        # Students can add online school fee payment tickets.
2227        IWorkflowState(self.student).setState('cleared')
2228        self.browser.open(self.payments_path)
2229        self.browser.getLink("Add current session payment ticket").click()
2230        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2231        self.browser.getControl("Create ticket").click()
2232        self.assertMatches('...ticket created...',
2233                           self.browser.contents)
2234        ctrl = self.browser.getControl(name='val_id')
2235        value = ctrl.options[0]
2236        self.browser.getLink(value).click()
2237        self.assertMatches('...Amount Authorized...',
2238                           self.browser.contents)
2239        # Payment session and level are current ones.
2240        # Postgrads have to pay school_fee_1.
2241        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
2242        self.assertEqual(self.student['payments'][value].p_session, 2004)
2243        self.assertEqual(self.student['payments'][value].p_level, 999)
2244
2245        # We simulate the approval
2246        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2247        self.browser.open(self.browser.url + '/fake_approve')
2248        self.assertMatches('...Payment approved...',
2249                          self.browser.contents)
2250
2251        # The new SFE-0 pin can be used for starting session
2252        self.browser.open(self.studycourse_path)
2253        self.browser.getLink('Start new session').click()
2254        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2255        parts = pin.split('-')[1:]
2256        sfeseries, sfenumber = parts
2257        self.browser.getControl(name="ac_series").value = sfeseries
2258        self.browser.getControl(name="ac_number").value = sfenumber
2259        self.browser.getControl("Start now").click()
2260        self.assertMatches('...Session started...',
2261                           self.browser.contents)
2262        self.assertTrue(self.student.state == 'school fee paid')
2263
2264        # Postgrad students do not need to register courses the
2265        # can just pay for the next session.
2266        self.browser.open(self.payments_path)
2267        # Remove first payment to be sure that we access the right ticket
2268        del self.student['payments'][value]
2269        self.browser.getLink("Add current session payment ticket").click()
2270        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2271        self.browser.getControl("Create ticket").click()
2272        ctrl = self.browser.getControl(name='val_id')
2273        value = ctrl.options[0]
2274        self.browser.getLink(value).click()
2275        # Payment session has increased by one, payment level remains the same.
2276        # Returning Postgraduates have to pay school_fee_2.
2277        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2278        self.assertEqual(self.student['payments'][value].p_session, 2005)
2279        self.assertEqual(self.student['payments'][value].p_level, 999)
2280
2281        # Student is still in old session
2282        self.assertEqual(self.student.current_session, 2004)
2283
2284        # We do not need to pay the ticket if any other
2285        # SFE pin is provided
2286        pin_container = self.app['accesscodes']
2287        pin_container.createBatch(
2288            datetime.utcnow(), 'some_userid', 'SFE', 9.99, 1)
2289        pin = pin_container['SFE-1'].values()[0].representation
2290        sfeseries, sfenumber = pin.split('-')[1:]
2291        # The new SFE-1 pin can be used for starting new session
2292        self.browser.open(self.studycourse_path)
2293        self.browser.getLink('Start new session').click()
2294        self.browser.getControl(name="ac_series").value = sfeseries
2295        self.browser.getControl(name="ac_number").value = sfenumber
2296        self.browser.getControl("Start now").click()
2297        self.assertMatches('...Session started...',
2298                           self.browser.contents)
2299        self.assertTrue(self.student.state == 'school fee paid')
2300        # Student is in new session
2301        self.assertEqual(self.student.current_session, 2005)
2302        self.assertEqual(self.student['studycourse'].current_level, 999)
2303        return
2304
2305    def test_student_accommodation(self):
2306        # Login
2307        self.browser.open(self.login_path)
2308        self.browser.getControl(name="form.login").value = self.student_id
2309        self.browser.getControl(name="form.password").value = 'spwd'
2310        self.browser.getControl("Login").click()
2311
2312        # Students can add online booking fee payment tickets and open the
2313        # callback view (see test_manage_payments)
2314        self.browser.getLink("Payments").click()
2315        self.browser.getLink("Add current session payment ticket").click()
2316        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2317        self.browser.getControl("Create ticket").click()
2318        ctrl = self.browser.getControl(name='val_id')
2319        value = ctrl.options[0]
2320        self.browser.getLink(value).click()
2321        self.browser.open(self.browser.url + '/fake_approve')
2322        # The new HOS-0 pin has been created
2323        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
2324        pin = self.app['accesscodes']['HOS-0'].keys()[0]
2325        ac = self.app['accesscodes']['HOS-0'][pin]
2326        parts = pin.split('-')[1:]
2327        sfeseries, sfenumber = parts
2328
2329        # Students can use HOS code and book a bed space with it ...
2330        self.browser.open(self.acco_path)
2331        # ... but not if booking period has expired ...
2332        self.app['hostels'].enddate = datetime.now(pytz.utc)
2333        self.browser.getLink("Book accommodation").click()
2334        self.assertMatches('...Outside booking period: ...',
2335                           self.browser.contents)
2336        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
2337        # ... or student is not the an allowed state ...
2338        self.browser.getLink("Book accommodation").click()
2339        self.assertMatches('...You are in the wrong...',
2340                           self.browser.contents)
2341        IWorkflowInfo(self.student).fireTransition('admit')
2342        self.browser.getLink("Book accommodation").click()
2343        self.assertMatches('...Activation Code:...',
2344                           self.browser.contents)
2345        # Student can't used faked ACs ...
2346        self.browser.getControl(name="ac_series").value = u'nonsense'
2347        self.browser.getControl(name="ac_number").value = sfenumber
2348        self.browser.getControl("Create bed ticket").click()
2349        self.assertMatches('...Activation code is invalid...',
2350                           self.browser.contents)
2351        # ... or ACs owned by somebody else.
2352        ac.owner = u'Anybody'
2353        self.browser.getControl(name="ac_series").value = sfeseries
2354        self.browser.getControl(name="ac_number").value = sfenumber
2355        self.browser.getControl("Create bed ticket").click()
2356        self.assertMatches('...You are not the owner of this access code...',
2357                           self.browser.contents)
2358        ac.owner = self.student_id
2359        self.browser.getControl(name="ac_series").value = sfeseries
2360        self.browser.getControl(name="ac_number").value = sfenumber
2361        self.browser.getControl("Create bed ticket").click()
2362        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2363                           self.browser.contents)
2364
2365        # Bed has been allocated
2366        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
2367        self.assertTrue(bed.owner == self.student_id)
2368
2369        # BedTicketAddPage is now blocked
2370        self.browser.getLink("Book accommodation").click()
2371        self.assertMatches('...You already booked a bed space...',
2372            self.browser.contents)
2373
2374        # The bed ticket displays the data correctly
2375        self.browser.open(self.acco_path + '/2004')
2376        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2377                           self.browser.contents)
2378        self.assertMatches('...2004/2005...', self.browser.contents)
2379        self.assertMatches('...regular_male_fr...', self.browser.contents)
2380        self.assertMatches('...%s...' % pin, self.browser.contents)
2381
2382        # Students can open the pdf slip
2383        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
2384        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2385        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2386
2387        # Students can't relocate themselves
2388        self.assertFalse('Relocate' in self.browser.contents)
2389        relocate_path = self.acco_path + '/2004/relocate'
2390        self.assertRaises(
2391            Unauthorized, self.browser.open, relocate_path)
2392
2393        # Students can't the Remove button and check boxes
2394        self.browser.open(self.acco_path)
2395        self.assertFalse('Remove' in self.browser.contents)
2396        self.assertFalse('val_id' in self.browser.contents)
2397
2398        # Students can pay maintenance fee now
2399        self.browser.open(self.payments_path)
2400        self.browser.open(self.payments_path + '/addop')
2401        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2402        self.browser.getControl("Create ticket").click()
2403        self.assertMatches('...Payment ticket created...',
2404                           self.browser.contents)
2405        return
2406
2407    def test_change_password_request(self):
2408        self.browser.open('http://localhost/app/changepw')
2409        self.browser.getControl(name="form.identifier").value = '123'
2410        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
2411        self.browser.getControl("Send login credentials").click()
2412        self.assertTrue('An email with' in self.browser.contents)
2413
2414class StudentRequestPWTests(StudentsFullSetup):
2415    # Tests for student registration
2416
2417    layer = FunctionalLayer
2418
2419    def test_request_pw(self):
2420        # Student with wrong number can't be found.
2421        self.browser.open('http://localhost/app/requestpw')
2422        self.browser.getControl(name="form.firstname").value = 'Anna'
2423        self.browser.getControl(name="form.number").value = 'anynumber'
2424        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2425        self.browser.getControl("Send login credentials").click()
2426        self.assertTrue('No student record found.'
2427            in self.browser.contents)
2428        # Anonymous is not informed that firstname verification failed.
2429        # It seems that the record doesn't exist.
2430        self.browser.open('http://localhost/app/requestpw')
2431        self.browser.getControl(name="form.firstname").value = 'Johnny'
2432        self.browser.getControl(name="form.number").value = '123'
2433        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2434        self.browser.getControl("Send login credentials").click()
2435        self.assertTrue('No student record found.'
2436            in self.browser.contents)
2437        # Even with the correct firstname we can't register if a
2438        # password has been set and used.
2439        self.browser.getControl(name="form.firstname").value = 'Anna'
2440        self.browser.getControl(name="form.number").value = '123'
2441        self.browser.getControl("Send login credentials").click()
2442        self.assertTrue('Your password has already been set and used.'
2443            in self.browser.contents)
2444        self.browser.open('http://localhost/app/requestpw')
2445        self.app['students'][self.student_id].password = None
2446        # The firstname field, used for verification, is not case-sensitive.
2447        self.browser.getControl(name="form.firstname").value = 'aNNa'
2448        self.browser.getControl(name="form.number").value = '123'
2449        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2450        self.browser.getControl("Send login credentials").click()
2451        # Yeah, we succeded ...
2452        self.assertTrue('Your password request was successful.'
2453            in self.browser.contents)
2454        # We can also use the matric_number instead.
2455        self.browser.open('http://localhost/app/requestpw')
2456        self.browser.getControl(name="form.firstname").value = 'aNNa'
2457        self.browser.getControl(name="form.number").value = '234'
2458        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2459        self.browser.getControl("Send login credentials").click()
2460        self.assertTrue('Your password request was successful.'
2461            in self.browser.contents)
2462        # ... and  student can be found in the catalog via the email address
2463        cat = queryUtility(ICatalog, name='students_catalog')
2464        results = list(
2465            cat.searchResults(
2466            email=('new@yy.zz', 'new@yy.zz')))
2467        self.assertEqual(self.student,results[0])
2468        logfile = os.path.join(
2469            self.app['datacenter'].storage, 'logs', 'main.log')
2470        logcontent = open(logfile).read()
2471        self.assertTrue('zope.anybody - students.browser.StudentRequestPasswordPage - '
2472                        '234 (K1000000) - new@yy.zz' in logcontent)
2473        return
2474
2475    def test_student_locked_level_forms(self):
2476
2477        # Add two study levels, one current and one previous
2478        studylevel = createObject(u'waeup.StudentStudyLevel')
2479        studylevel.level = 100
2480        self.student['studycourse'].addStudentStudyLevel(
2481            self.certificate, studylevel)
2482        studylevel = createObject(u'waeup.StudentStudyLevel')
2483        studylevel.level = 200
2484        self.student['studycourse'].addStudentStudyLevel(
2485            self.certificate, studylevel)
2486        IWorkflowState(self.student).setState('school fee paid')
2487        self.student['studycourse'].current_level = 200
2488
2489        self.browser.open(self.login_path)
2490        self.browser.getControl(name="form.login").value = self.student_id
2491        self.browser.getControl(name="form.password").value = 'spwd'
2492        self.browser.getControl("Login").click()
2493
2494        self.browser.open(self.student_path + '/studycourse/200/edit')
2495        self.assertFalse('The requested form is locked' in self.browser.contents)
2496        self.browser.open(self.student_path + '/studycourse/100/edit')
2497        self.assertTrue('The requested form is locked' in self.browser.contents)
2498
2499        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2500        self.assertFalse('The requested form is locked' in self.browser.contents)
2501        self.browser.open(self.student_path + '/studycourse/100/ctadd')
2502        self.assertTrue('The requested form is locked' in self.browser.contents)
2503
2504        IWorkflowState(self.student).setState('courses registered')
2505        self.browser.open(self.student_path + '/studycourse/200/edit')
2506        self.assertTrue('The requested form is locked' in self.browser.contents)
2507        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2508        self.assertTrue('The requested form is locked' in self.browser.contents)
2509
2510
2511class PublicPagesTests(StudentsFullSetup):
2512    # Tests for swebservices
2513
2514    layer = FunctionalLayer
2515
2516    def test_paymentrequest(self):
2517        payment = createObject('waeup.StudentOnlinePayment')
2518        payment.p_category = u'schoolfee'
2519        payment.p_session = self.student.current_session
2520        payment.p_item = u'My Certificate'
2521        payment.p_id = u'anyid'
2522        self.student['payments']['anykey'] = payment
2523        # Request information about unpaid payment ticket
2524        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2525        self.assertEqual(self.browser.contents, '-1')
2526        # Request information about paid payment ticket
2527        payment.p_state = u'paid'
2528        notify(grok.ObjectModifiedEvent(payment))
2529        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2530        self.assertEqual(self.browser.contents,
2531            'FULL_NAME=Anna Tester&FACULTY=fac1&DEPARTMENT=dep1'
2532            '&PAYMENT_ITEM=My Certificate&PAYMENT_CATEGORY=School Fee'
2533            '&ACADEMIC_SESSION=2004/2005&MATRIC_NUMBER=234&FEE_AMOUNT=0.0')
2534        self.browser.open('http://localhost/app/paymentrequest?NONSENSE=nonsense')
2535        self.assertEqual(self.browser.contents, '-1')
2536        self.browser.open('http://localhost/app/paymentrequest?P_ID=nonsense')
2537        self.assertEqual(self.browser.contents, '-1')
Note: See TracBrowser for help on using the repository browser.