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

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

Add timestamp to personal data. This timestamp will be checked when students log in. Students sould be remembered to frequently update their personal data.

  • Property svn:keywords set to Id
File size: 130.7 KB
Line 
1## $Id: test_browser.py 9543 2012-11-05 18:03:39Z 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.course.credits = 100
728        self.browser.getControl("Add course ticket").click()
729        self.assertMatches(
730            '...Total credits exceed 58...', self.browser.contents)
731        self.course.credits = 10
732        self.browser.getControl("Add course ticket").click()
733        self.assertTrue('Successfully added' in self.browser.contents)
734        self.browser.getControl("Add course ticket").click()
735        self.browser.getControl(name="form.course").value = ['COURSE1']
736        self.browser.getControl("Add course ticket").click()
737        self.assertTrue('The ticket exists' in self.browser.contents)
738        self.browser.getControl("Cancel").click()
739        self.browser.getLink("COURSE1").click()
740        self.browser.getLink("Manage").click()
741        self.browser.getControl(name="form.score").value = '10'
742        self.browser.getControl("Save").click()
743        self.assertTrue('Form has been saved' in self.browser.contents)
744        # Carry-over courses will be collected when next level is created
745        self.browser.open(self.student_path + '/studycourse/manage')
746        # Add next level
747        self.browser.getControl(name="addlevel").value = ['200']
748        self.browser.getControl(name="level_session").value = ['2005']
749        self.browser.getControl("Add study level").click()
750        self.browser.getLink("200").click()
751        self.assertMatches(
752            '...: Study Level 200 (Year 2)...', self.browser.contents)
753        # COURSE1 has score 0 and thus will become a carry-over course
754        # in level 200
755        self.assertEqual(
756            sorted(self.student['studycourse']['200'].keys()), [u'COURSE1'])
757        self.assertTrue(
758            self.student['studycourse']['200']['COURSE1'].carry_over)
759        return
760
761    def test_manage_payments(self):
762        # Managers can add online school fee payment tickets
763        # if certain requirements are met
764        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
765        self.browser.open(self.payments_path)
766        IWorkflowState(self.student).setState('cleared')
767        self.browser.getLink("Add current session payment ticket").click()
768        self.browser.getControl(name="form.p_category").value = ['schoolfee']
769        self.browser.getControl("Create ticket").click()
770        self.assertMatches('...ticket created...',
771                           self.browser.contents)
772        ctrl = self.browser.getControl(name='val_id')
773        value = ctrl.options[0]
774        self.browser.getLink(value).click()
775        self.assertMatches('...Amount Authorized...',
776                           self.browser.contents)
777        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
778        payment_url = self.browser.url
779
780        # The pdf payment slip can't yet be opened
781        #self.browser.open(payment_url + '/payment_slip.pdf')
782        #self.assertMatches('...Ticket not yet paid...',
783        #                   self.browser.contents)
784
785        # The same payment (with same p_item, p_session and p_category)
786        # can be initialized a second time if the former ticket is not yet paid.
787        self.browser.open(self.payments_path)
788        self.browser.getLink("Add current session payment ticket").click()
789        self.browser.getControl(name="form.p_category").value = ['schoolfee']
790        self.browser.getControl("Create ticket").click()
791        self.assertMatches('...Payment ticket created...',
792                           self.browser.contents)
793
794        # The ticket can be found in the payments_catalog
795        cat = queryUtility(ICatalog, name='payments_catalog')
796        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
797        self.assertTrue(len(results), 1)
798        self.assertTrue(results[0] is self.student['payments'][value])
799
800        # Managers can approve the payment
801        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
802        self.browser.open(payment_url)
803        self.browser.getLink("Approve payment").click()
804        self.assertMatches('...Payment approved...',
805                          self.browser.contents)
806        # Approval is logged
807        logfile = os.path.join(
808            self.app['datacenter'].storage, 'logs', 'students.log')
809        logcontent = open(logfile).read()
810        self.assertTrue(
811            'zope.mgr - students.browser.OnlinePaymentApprovePage '
812            '- K1000000 - schoolfee payment approved'
813            in logcontent)
814
815        # The authorized amount has been stored in the access code
816        self.assertEqual(
817            self.app['accesscodes']['SFE-0'].values()[0].cost,40000.0)
818
819        # The catalog has been updated
820        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
821        self.assertTrue(len(results), 0)
822        results = list(cat.searchResults(p_state=('paid', 'paid')))
823        self.assertTrue(len(results), 1)
824        self.assertTrue(results[0] is self.student['payments'][value])
825
826        # Payments can't be approved twice
827        self.browser.open(payment_url + '/approve')
828        self.assertMatches('...This ticket has already been paid...',
829                          self.browser.contents)
830
831        # Now the first ticket is paid and no more ticket of same type
832        # (with same p_item, p_session and p_category) can be added
833        self.browser.open(self.payments_path)
834        self.browser.getLink("Add current session payment ticket").click()
835        self.browser.getControl(name="form.p_category").value = ['schoolfee']
836        self.browser.getControl("Create ticket").click()
837        self.assertMatches(
838            '...This type of payment has already been made...',
839            self.browser.contents)
840
841        # Managers can open the pdf payment slip
842        self.browser.open(payment_url)
843        self.browser.getLink("Download payment slip").click()
844        self.assertEqual(self.browser.headers['Status'], '200 Ok')
845        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
846
847        # Managers can remove online school fee payment tickets
848        self.browser.open(self.payments_path)
849        self.browser.getControl("Remove selected").click()
850        self.assertMatches('...No payment selected...', self.browser.contents)
851        ctrl = self.browser.getControl(name='val_id')
852        value = ctrl.options[0]
853        ctrl.getControl(value=value).selected = True
854        self.browser.getControl("Remove selected", index=0).click()
855        self.assertTrue('Successfully removed' in self.browser.contents)
856
857        # Managers can add online clearance payment tickets
858        self.browser.open(self.payments_path + '/addop')
859        self.browser.getControl(name="form.p_category").value = ['clearance']
860        self.browser.getControl("Create ticket").click()
861        self.assertMatches('...ticket created...',
862                           self.browser.contents)
863
864        # Managers can approve the payment
865        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
866        ctrl = self.browser.getControl(name='val_id')
867        value = ctrl.options[1] # The clearance payment is the second in the table
868        self.browser.getLink(value).click()
869        self.browser.open(self.browser.url + '/approve')
870        self.assertMatches('...Payment approved...',
871                          self.browser.contents)
872        expected = '''...
873        <td>
874          <span>Paid</span>
875        </td>...'''
876        self.assertMatches(expected,self.browser.contents)
877        # The new CLR-0 pin has been created
878        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
879        pin = self.app['accesscodes']['CLR-0'].keys()[0]
880        ac = self.app['accesscodes']['CLR-0'][pin]
881        self.assertEqual(ac.owner, self.student_id)
882        self.assertEqual(ac.cost, 3456.0)
883        return
884
885    def test_manage_accommodation(self):
886        logfile = os.path.join(
887            self.app['datacenter'].storage, 'logs', 'students.log')
888        # Managers can add online booking fee payment tickets and open the
889        # callback view (see test_manage_payments)
890        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
891        self.browser.open(self.payments_path)
892        self.browser.getLink("Add current session payment ticket").click()
893        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
894        # If student is not in accommodation session, payment cannot be processed
895        self.app['hostels'].accommodation_session = 2011
896        self.browser.getControl("Create ticket").click()
897        self.assertMatches('...Your current session does not match...',
898                           self.browser.contents)
899        self.app['hostels'].accommodation_session = 2004
900        self.browser.getLink("Add current session payment ticket").click()
901        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
902        self.browser.getControl("Create ticket").click()
903        ctrl = self.browser.getControl(name='val_id')
904        value = ctrl.options[0]
905        self.browser.getLink(value).click()
906        self.browser.open(self.browser.url + '/approve')
907        # The new HOS-0 pin has been created
908        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
909        pin = self.app['accesscodes']['HOS-0'].keys()[0]
910        ac = self.app['accesscodes']['HOS-0'][pin]
911        self.assertEqual(ac.owner, self.student_id)
912        parts = pin.split('-')[1:]
913        sfeseries, sfenumber = parts
914        # Managers can use HOS code and book a bed space with it
915        self.browser.open(self.acco_path)
916        self.browser.getLink("Book accommodation").click()
917        self.assertMatches('...You are in the wrong...',
918                           self.browser.contents)
919        IWorkflowInfo(self.student).fireTransition('admit')
920        # An existing HOS code can only be used if students
921        # are in accommodation session
922        self.student['studycourse'].current_session = 2003
923        self.browser.getLink("Book accommodation").click()
924        self.assertMatches('...Your current session does not match...',
925                           self.browser.contents)
926        self.student['studycourse'].current_session = 2004
927        # All requirements are met and ticket can be created
928        self.browser.getLink("Book accommodation").click()
929        self.assertMatches('...Activation Code:...',
930                           self.browser.contents)
931        self.browser.getControl(name="ac_series").value = sfeseries
932        self.browser.getControl(name="ac_number").value = sfenumber
933        self.browser.getControl("Create bed ticket").click()
934        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
935                           self.browser.contents)
936        # Bed has been allocated
937        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
938        self.assertTrue(bed1.owner == self.student_id)
939        # BedTicketAddPage is now blocked
940        self.browser.getLink("Book accommodation").click()
941        self.assertMatches('...You already booked a bed space...',
942            self.browser.contents)
943        # The bed ticket displays the data correctly
944        self.browser.open(self.acco_path + '/2004')
945        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
946                           self.browser.contents)
947        self.assertMatches('...2004/2005...', self.browser.contents)
948        self.assertMatches('...regular_male_fr...', self.browser.contents)
949        self.assertMatches('...%s...' % pin, self.browser.contents)
950        # Booking is properly logged
951        logcontent = open(logfile).read()
952        self.assertTrue('zope.mgr - students.browser.BedTicketAddPage '
953            '- K1000000 - booked: hall-1_A_101_A' in logcontent)
954        # Managers can relocate students if the student's bed_type has changed
955        self.browser.getLink("Relocate student").click()
956        self.assertMatches(
957            "...Student can't be relocated...", self.browser.contents)
958        self.student.sex = u'f'
959        self.browser.getLink("Relocate student").click()
960        self.assertMatches(
961            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
962        self.assertTrue(bed1.owner == NOT_OCCUPIED)
963        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
964        self.assertTrue(bed2.owner == self.student_id)
965        self.assertTrue(self.student['accommodation'][
966            '2004'].bed_type == u'regular_female_fr')
967        # Relocation is properly logged
968        logcontent = open(logfile).read()
969        self.assertTrue('zope.mgr - students.browser.BedTicketRelocationPage '
970            '- K1000000 - relocated: hall-1_A_101_B' in logcontent)
971        # The payment object still shows the original payment item
972        payment_id = self.student['payments'].keys()[0]
973        payment = self.student['payments'][payment_id]
974        self.assertTrue(payment.p_item == u'regular_male_fr')
975        # Managers can relocate students if the bed's bed_type has changed
976        bed1.bed_type = u'regular_female_fr'
977        bed2.bed_type = u'regular_male_fr'
978        notify(grok.ObjectModifiedEvent(bed1))
979        notify(grok.ObjectModifiedEvent(bed2))
980        self.browser.getLink("Relocate student").click()
981        self.assertMatches(
982            "...Student relocated...", self.browser.contents)
983        self.assertMatches(
984            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
985        self.assertMatches(bed1.owner, self.student_id)
986        self.assertMatches(bed2.owner, NOT_OCCUPIED)
987        # Managers can't relocate students if bed is reserved
988        self.student.sex = u'm'
989        bed1.bed_type = u'regular_female_reserved'
990        notify(grok.ObjectModifiedEvent(bed1))
991        self.browser.getLink("Relocate student").click()
992        self.assertMatches(
993            "...Students in reserved beds can't be relocated...",
994            self.browser.contents)
995        # Managers can relocate students if booking has been cancelled but
996        # other bed space has been manually allocated after cancellation
997        old_owner = bed1.releaseBed()
998        self.assertMatches(old_owner, self.student_id)
999        bed2.owner = self.student_id
1000        self.browser.open(self.acco_path + '/2004')
1001        self.assertMatches(
1002            "...booking cancelled...", self.browser.contents)
1003        self.browser.getLink("Relocate student").click()
1004        # We didn't informed the catalog therefore the new owner is not found
1005        self.assertMatches(
1006            "...There is no free bed in your category regular_male_fr...",
1007            self.browser.contents)
1008        # Now we fire the event properly
1009        notify(grok.ObjectModifiedEvent(bed2))
1010        self.browser.getLink("Relocate student").click()
1011        self.assertMatches(
1012            "...Student relocated...", self.browser.contents)
1013        self.assertMatches(
1014            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1015          # Managers can delete bed tickets
1016        self.browser.open(self.acco_path)
1017        ctrl = self.browser.getControl(name='val_id')
1018        value = ctrl.options[0]
1019        ctrl.getControl(value=value).selected = True
1020        self.browser.getControl("Remove selected", index=0).click()
1021        self.assertMatches('...Successfully removed...', self.browser.contents)
1022        # The bed has been properly released by the event handler
1023        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1024        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1025        return
1026
1027    def test_manage_workflow(self):
1028        # Managers can pass through the whole workflow
1029        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1030        student = self.app['students'][self.student_id]
1031        self.browser.open(self.trigtrans_path)
1032        self.assertTrue(student.clearance_locked)
1033        self.browser.getControl(name="transition").value = ['admit']
1034        self.browser.getControl("Save").click()
1035        self.assertTrue(student.clearance_locked)
1036        self.browser.getControl(name="transition").value = ['start_clearance']
1037        self.browser.getControl("Save").click()
1038        self.assertFalse(student.clearance_locked)
1039        self.browser.getControl(name="transition").value = ['request_clearance']
1040        self.browser.getControl("Save").click()
1041        self.assertTrue(student.clearance_locked)
1042        self.browser.getControl(name="transition").value = ['clear']
1043        self.browser.getControl("Save").click()
1044        # Managers approve payment, they do not pay
1045        self.assertFalse('pay_first_school_fee' in self.browser.contents)
1046        self.browser.getControl(
1047            name="transition").value = ['approve_first_school_fee']
1048        self.browser.getControl("Save").click()
1049        self.browser.getControl(name="transition").value = ['reset6']
1050        self.browser.getControl("Save").click()
1051        # In state returning the pay_school_fee transition triggers some
1052        # changes of attributes
1053        self.browser.getControl(name="transition").value = ['approve_school_fee']
1054        self.browser.getControl("Save").click()
1055        self.assertEqual(student['studycourse'].current_session, 2005) # +1
1056        self.assertEqual(student['studycourse'].current_level, 200) # +100
1057        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = Zero = not set
1058        self.assertEqual(student['studycourse'].previous_verdict, 'A')
1059        self.browser.getControl(name="transition").value = ['register_courses']
1060        self.browser.getControl("Save").click()
1061        self.browser.getControl(name="transition").value = ['validate_courses']
1062        self.browser.getControl("Save").click()
1063        self.browser.getControl(name="transition").value = ['return']
1064        self.browser.getControl("Save").click()
1065        return
1066
1067    def test_manage_pg_workflow(self):
1068        # Managers can pass through the whole workflow
1069        IWorkflowState(self.student).setState('school fee paid')
1070        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1071        student = self.app['students'][self.student_id]
1072        self.browser.open(self.trigtrans_path)
1073        self.assertTrue('<option value="reset6">' in self.browser.contents)
1074        self.assertTrue('<option value="register_courses">' in self.browser.contents)
1075        self.assertTrue('<option value="reset5">' in self.browser.contents)
1076        self.certificate.study_mode = 'pg_ft'
1077        self.browser.open(self.trigtrans_path)
1078        self.assertFalse('<option value="reset6">' in self.browser.contents)
1079        self.assertFalse('<option value="register_courses">' in self.browser.contents)
1080        self.assertTrue('<option value="reset5">' in self.browser.contents)
1081        return
1082
1083    def test_manage_import(self):
1084        # Managers can import student data files
1085        datacenter_path = 'http://localhost/app/datacenter'
1086        # Prepare a csv file for students
1087        open('students.csv', 'wb').write(
1088"""firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,password
1089Aaren,Pieri,1,1990-01-02,100000,aa@aa.ng,1234,m,mypwd1
1090Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,m,mypwd1
1091Brit,Berson,3,1990-01-04,100001,aa@aa.ng,1234,m,mypwd1
1092""")
1093        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1094        self.browser.open(datacenter_path)
1095        self.browser.getLink('Upload data').click()
1096        filecontents = StringIO(open('students.csv', 'rb').read())
1097        filewidget = self.browser.getControl(name='uploadfile:file')
1098        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
1099        self.browser.getControl(name='SUBMIT').click()
1100        self.browser.getLink('Process data').click()
1101        button = lookup_submit_value(
1102            'select', 'students_zope.mgr.csv', self.browser)
1103        button.click()
1104        importerselect = self.browser.getControl(name='importer')
1105        modeselect = self.browser.getControl(name='mode')
1106        importerselect.getControl('Student Processor').selected = True
1107        modeselect.getControl(value='create').selected = True
1108        self.browser.getControl('Proceed to step 3').click()
1109        self.assertTrue('Header fields OK' in self.browser.contents)
1110        self.browser.getControl('Perform import').click()
1111        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1112        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
1113        self.assertTrue('Batch processing finished' in self.browser.contents)
1114        open('studycourses.csv', 'wb').write(
1115"""reg_number,matric_number,certificate,current_session,current_level
11161,,CERT1,2008,100
1117,100001,CERT1,2008,100
1118,100002,CERT1,2008,100
1119""")
1120        self.browser.open(datacenter_path)
1121        self.browser.getLink('Upload data').click()
1122        filecontents = StringIO(open('studycourses.csv', 'rb').read())
1123        filewidget = self.browser.getControl(name='uploadfile:file')
1124        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
1125        self.browser.getControl(name='SUBMIT').click()
1126        self.browser.getLink('Process data').click()
1127        button = lookup_submit_value(
1128            'select', 'studycourses_zope.mgr.csv', self.browser)
1129        button.click()
1130        importerselect = self.browser.getControl(name='importer')
1131        modeselect = self.browser.getControl(name='mode')
1132        importerselect.getControl(
1133            'StudentStudyCourse Processor (update only)').selected = True
1134        modeselect.getControl(value='create').selected = True
1135        self.browser.getControl('Proceed to step 3').click()
1136        self.assertTrue('Update mode only' in self.browser.contents)
1137        self.browser.getControl('Proceed to step 3').click()
1138        self.assertTrue('Header fields OK' in self.browser.contents)
1139        self.browser.getControl('Perform import').click()
1140        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1141        self.assertTrue('Successfully processed 2 rows'
1142                        in self.browser.contents)
1143        # The students are properly indexed and we can
1144        # thus find a student in  the department
1145        self.browser.open(self.manage_container_path)
1146        self.browser.getControl(name="searchtype").value = ['depcode']
1147        self.browser.getControl(name="searchterm").value = 'dep1'
1148        self.browser.getControl("Search").click()
1149        self.assertTrue('Aaren Pieri' in self.browser.contents)
1150        # We can search for a new student by name ...
1151        self.browser.getControl(name="searchtype").value = ['fullname']
1152        self.browser.getControl(name="searchterm").value = 'Claus'
1153        self.browser.getControl("Search").click()
1154        self.assertTrue('Claus Finau' in self.browser.contents)
1155        # ... and check if the imported password has been properly set
1156        ctrl = self.browser.getControl(name='entries')
1157        value = ctrl.options[0]
1158        claus = self.app['students'][value]
1159        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
1160        return
1161
1162    def test_handle_clearance_by_co(self):
1163        # Create clearance officer
1164        self.app['users'].addUser('mrclear', 'mrclearsecret')
1165        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
1166        self.app['users']['mrclear'].title = 'Carlo Pitter'
1167        # Clearance officers need not necessarily to get
1168        # the StudentsOfficer site role
1169        #prmglobal = IPrincipalRoleManager(self.app)
1170        #prmglobal.assignRoleToPrincipal('waeup.StudentsOfficer', 'mrclear')
1171        # Assign local ClearanceOfficer role
1172        department = self.app['faculties']['fac1']['dep1']
1173        prmlocal = IPrincipalRoleManager(department)
1174        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
1175        IWorkflowState(self.student).setState('clearance started')
1176        # Login as clearance officer
1177        self.browser.open(self.login_path)
1178        self.browser.getControl(name="form.login").value = 'mrclear'
1179        self.browser.getControl(name="form.password").value = 'mrclearsecret'
1180        self.browser.getControl("Login").click()
1181        self.assertMatches('...You logged in...', self.browser.contents)
1182        # CO can see his roles
1183        self.browser.getLink("My Roles").click()
1184        self.assertMatches(
1185            '...<div>Academics Officer (view only)</div>...',
1186            self.browser.contents)
1187        #self.assertMatches(
1188        #    '...<div>Students Officer (view only)</div>...',
1189        #    self.browser.contents)
1190        # But not his local role ...
1191        self.assertFalse('Clearance Officer' in self.browser.contents)
1192        # ... because we forgot to notify the department that the local role
1193        # has changed
1194        notify(LocalRoleSetEvent(
1195            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
1196        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1197        self.assertTrue('Clearance Officer' in self.browser.contents)
1198        self.assertMatches(
1199            '...<a href="http://localhost/app/faculties/fac1/dep1">...',
1200            self.browser.contents)
1201        # CO can view the student ...
1202        self.browser.open(self.clearance_path)
1203        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1204        self.assertEqual(self.browser.url, self.clearance_path)
1205        # ... but not other students
1206        other_student = Student()
1207        other_student.firstname = u'Dep2'
1208        other_student.lastname = u'Student'
1209        self.app['students'].addStudent(other_student)
1210        other_student_path = (
1211            'http://localhost/app/students/%s' % other_student.student_id)
1212        self.assertRaises(
1213            Unauthorized, self.browser.open, other_student_path)
1214        # Only in state clearance requested the CO does see the 'Clear' button
1215        self.browser.open(self.clearance_path)
1216        self.assertFalse('Clear student' in self.browser.contents)
1217        IWorkflowInfo(self.student).fireTransition('request_clearance')
1218        self.browser.open(self.clearance_path)
1219        self.assertTrue('Clear student' in self.browser.contents)
1220        self.browser.getLink("Clear student").click()
1221        self.assertTrue('Student has been cleared' in self.browser.contents)
1222        self.assertTrue('cleared' in self.browser.contents)
1223        self.browser.open(self.history_path)
1224        self.assertTrue('Cleared by Carlo Pitter' in self.browser.contents)
1225        # Hide real name.
1226        self.app['users']['mrclear'].public_name = 'My Public Name'
1227        self.browser.open(self.clearance_path)
1228        self.browser.getLink("Reject clearance").click()
1229        self.assertEqual(
1230            self.browser.url, self.student_path + '/reject_clearance')
1231        # Type comment why
1232        self.browser.getControl(name="form.officer_comment").value = """Dear Student,
1233You did not fill properly.
1234"""
1235        self.browser.getControl("Save comment").click()
1236        self.assertTrue('Clearance has been annulled' in self.browser.contents)
1237        url = ('http://localhost/app/students/K1000000/'
1238              'contactstudent?body=Dear+Student%2C%0AYou+did+not+fill+properly.'
1239              '%0A&subject=Clearance+has+been+annulled.')
1240        # CO does now see the prefilled contact form and can send a message
1241        self.assertEqual(self.browser.url, url)
1242        self.assertTrue('clearance started' in self.browser.contents)
1243        self.assertTrue('name="form.subject" size="20" type="text" '
1244            'value="Clearance has been annulled."'
1245            in self.browser.contents)
1246        self.assertTrue('name="form.body" rows="10" >Dear Student,'
1247            in self.browser.contents)
1248        self.browser.getControl("Send message now").click()
1249        self.assertTrue('Your message has been sent' in self.browser.contents)
1250        # The comment has been stored ...
1251        self.assertEqual(self.student.officer_comment,
1252            u'Dear Student,\nYou did not fill properly.\n')
1253        # ... and logged
1254        logfile = os.path.join(
1255            self.app['datacenter'].storage, 'logs', 'students.log')
1256        logcontent = open(logfile).read()
1257        self.assertTrue(
1258            'INFO - mrclear - students.browser.StudentRejectClearancePage - '
1259            'K1000000 - comment: Dear Student,<br>You did not fill '
1260            'properly.<br>\n' in logcontent)
1261        self.browser.open(self.history_path)
1262        self.assertTrue("Reset to 'clearance started' by My Public Name" in
1263            self.browser.contents)
1264        IWorkflowInfo(self.student).fireTransition('request_clearance')
1265        self.browser.open(self.clearance_path)
1266        self.browser.getLink("Reject clearance").click()
1267        self.browser.getControl("Save comment").click()
1268        self.assertTrue('Clearance request has been rejected'
1269            in self.browser.contents)
1270        self.assertTrue('clearance started' in self.browser.contents)
1271        # The CO can't clear students if not in state
1272        # clearance requested
1273        self.browser.open(self.student_path + '/clear')
1274        self.assertTrue('Student is in wrong state'
1275            in self.browser.contents)
1276        # The CO can go to his department throug the my_roles page ...
1277        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1278        self.browser.getLink("http://localhost/app/faculties/fac1/dep1").click()
1279        # ... and view the list of students
1280        self.browser.getLink("Show students").click()
1281        self.assertTrue(self.student_id in self.browser.contents)
1282        # The comment is indicated by 'yes'
1283        self.assertTrue('<td><span>yes</span></td>' in self.browser.contents)
1284        # When a student is cleared the comment is automatically deleted
1285        IWorkflowInfo(self.student).fireTransition('request_clearance')
1286        IWorkflowInfo(self.student).fireTransition('clear')
1287        self.assertEqual(self.student.officer_comment, None)
1288
1289    def test_handle_courses_by_ca(self):
1290        # Create course adviser
1291        self.app['users'].addUser('mrsadvise', 'mrsadvisesecret')
1292        self.app['users']['mrsadvise'].email = 'mradvise@foo.ng'
1293        self.app['users']['mrsadvise'].title = u'Helen Procter'
1294        # Assign local CourseAdviser100 role for a certificate
1295        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1296        prmlocal = IPrincipalRoleManager(cert)
1297        prmlocal.assignRoleToPrincipal('waeup.local.CourseAdviser100', 'mrsadvise')
1298        IWorkflowState(self.student).setState('school fee paid')
1299        # Login as course adviser
1300        self.browser.open(self.login_path)
1301        self.browser.getControl(name="form.login").value = 'mrsadvise'
1302        self.browser.getControl(name="form.password").value = 'mrsadvisesecret'
1303        self.browser.getControl("Login").click()
1304        self.assertMatches('...You logged in...', self.browser.contents)
1305        # CO can see his roles
1306        self.browser.getLink("My Roles").click()
1307        self.assertMatches(
1308            '...<div>Academics Officer (view only)</div>...',
1309            self.browser.contents)
1310        # But not his local role ...
1311        self.assertFalse('Course Adviser' in self.browser.contents)
1312        # ... because we forgot to notify the certificate that the local role
1313        # has changed
1314        notify(LocalRoleSetEvent(
1315            cert, 'waeup.local.CourseAdviser100', 'mrsadvise', granted=True))
1316        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1317        self.assertTrue('Course Adviser 100L' in self.browser.contents)
1318        self.assertMatches(
1319            '...<a href="http://localhost/app/faculties/fac1/dep1/certificates/CERT1">...',
1320            self.browser.contents)
1321        # CA can view the student ...
1322        self.browser.open(self.student_path)
1323        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1324        self.assertEqual(self.browser.url, self.student_path)
1325        # ... but not other students
1326        other_student = Student()
1327        other_student.firstname = u'Dep2'
1328        other_student.lastname = u'Student'
1329        self.app['students'].addStudent(other_student)
1330        other_student_path = (
1331            'http://localhost/app/students/%s' % other_student.student_id)
1332        self.assertRaises(
1333            Unauthorized, self.browser.open, other_student_path)
1334        # We add study level 110 to the student's studycourse
1335        studylevel = StudentStudyLevel()
1336        studylevel.level = 110
1337        self.student['studycourse'].addStudentStudyLevel(
1338            cert,studylevel)
1339        L110_student_path = self.studycourse_path + '/110'
1340        # Only in state courses registered and only if the current level
1341        # corresponds with the name of the study level object
1342        # the 100L CA does see the 'Validate' button
1343        self.browser.open(L110_student_path)
1344        self.assertFalse('Validate courses' in self.browser.contents)
1345        IWorkflowInfo(self.student).fireTransition('register_courses')
1346        self.browser.open(L110_student_path)
1347        self.assertFalse('Validate courses' in self.browser.contents)
1348        self.student['studycourse'].current_level = 110
1349        self.browser.open(L110_student_path)
1350        self.assertTrue('Validate courses' in self.browser.contents)
1351        # ... but a 100L CA does not see the button on other levels
1352        studylevel2 = StudentStudyLevel()
1353        studylevel2.level = 200
1354        self.student['studycourse'].addStudentStudyLevel(
1355            cert,studylevel2)
1356        L200_student_path = self.studycourse_path + '/200'
1357        self.browser.open(L200_student_path)
1358        self.assertFalse('Validate courses' in self.browser.contents)
1359        self.browser.open(L110_student_path)
1360        self.browser.getLink("Validate courses").click()
1361        self.assertTrue('Course list has been validated' in self.browser.contents)
1362        self.assertTrue('courses validated' in self.browser.contents)
1363        self.assertEqual(self.student['studycourse']['110'].validated_by,
1364            'Helen Procter')
1365        self.assertMatches(
1366            '<YYYY-MM-DD hh:mm:ss>',
1367            self.student['studycourse']['110'].validation_date.strftime(
1368                "%Y-%m-%d %H:%M:%S"))
1369        self.browser.getLink("Reject courses").click()
1370        self.assertTrue('Course list request has been annulled.'
1371            in self.browser.contents)
1372        urlmessage = 'Course+list+request+has+been+annulled.'
1373        self.assertEqual(self.browser.url, self.student_path +
1374            '/contactstudent?subject=%s' % urlmessage)
1375        self.assertTrue('school fee paid' in self.browser.contents)
1376        self.assertTrue(self.student['studycourse']['110'].validated_by is None)
1377        self.assertTrue(self.student['studycourse']['110'].validation_date is None)
1378        IWorkflowInfo(self.student).fireTransition('register_courses')
1379        self.browser.open(L110_student_path)
1380        self.browser.getLink("Reject courses").click()
1381        self.assertTrue('Course list request has been rejected'
1382            in self.browser.contents)
1383        self.assertTrue('school fee paid' in self.browser.contents)
1384        # CA does now see the contact form and can send a message
1385        self.browser.getControl(name="form.subject").value = 'Important subject'
1386        self.browser.getControl(name="form.body").value = 'Course list rejected'
1387        self.browser.getControl("Send message now").click()
1388        self.assertTrue('Your message has been sent' in self.browser.contents)
1389        # The CA can't validate courses if not in state
1390        # courses registered
1391        self.browser.open(L110_student_path + '/validate_courses')
1392        self.assertTrue('Student is in the wrong state'
1393            in self.browser.contents)
1394        # The CA can go to his certificate through the my_roles page
1395        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1396        self.browser.getLink(
1397            "http://localhost/app/faculties/fac1/dep1/certificates/CERT1").click()
1398        # and view the list of students
1399        self.browser.getLink("Show students").click()
1400        self.assertTrue(self.student_id in self.browser.contents)
1401
1402    def test_change_current_mode(self):
1403        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1404        self.browser.open(self.clearance_path)
1405        self.assertFalse('Employer' in self.browser.contents)
1406        self.browser.open(self.manage_clearance_path)
1407        self.assertFalse('Employer' in self.browser.contents)
1408        self.student.clearance_locked = False
1409        self.browser.open(self.edit_clearance_path)
1410        self.assertFalse('Employer' in self.browser.contents)
1411        # Now we change the study mode of the certificate and a different
1412        # interface is used by clearance views.
1413        self.certificate.study_mode = 'pg_ft'
1414        # Invariants are not being checked here?!
1415        self.certificate.end_level = 100
1416        self.browser.open(self.clearance_path)
1417        self.assertTrue('Employer' in self.browser.contents)
1418        self.browser.open(self.manage_clearance_path)
1419        self.assertTrue('Employer' in self.browser.contents)
1420        self.browser.open(self.edit_clearance_path)
1421        self.assertTrue('Employer' in self.browser.contents)
1422
1423    def test_activate_deactivate_buttons(self):
1424        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1425        self.browser.open(self.student_path)
1426        self.browser.getLink("Deactivate").click()
1427        self.assertTrue(
1428            'Student account has been deactivated.' in self.browser.contents)
1429        self.assertTrue(
1430            'Base Data (account deactivated)' in self.browser.contents)
1431        self.assertTrue(self.student.suspended)
1432        self.browser.getLink("Activate").click()
1433        self.assertTrue(
1434            'Student account has been activated.' in self.browser.contents)
1435        self.assertFalse(
1436            'Base Data (account deactivated)' in self.browser.contents)
1437        self.assertFalse(self.student.suspended)
1438        # History messages have been added ...
1439        self.browser.getLink("History").click()
1440        self.assertTrue(
1441            'Student account deactivated by Manager<br />' in self.browser.contents)
1442        self.assertTrue(
1443            'Student account activated by Manager<br />' in self.browser.contents)
1444        # ... and actions have been logged.
1445        logfile = os.path.join(
1446            self.app['datacenter'].storage, 'logs', 'students.log')
1447        logcontent = open(logfile).read()
1448        self.assertTrue('zope.mgr - students.browser.StudentDeactivatePage - '
1449                        'K1000000 - account deactivated' in logcontent)
1450        self.assertTrue('zope.mgr - students.browser.StudentActivatePage - '
1451                        'K1000000 - account activated' in logcontent)
1452
1453    def test_manage_student_transfer(self):
1454        # Add second certificate
1455        self.certificate2 = createObject('waeup.Certificate')
1456        self.certificate2.code = u'CERT2'
1457        self.certificate2.study_mode = 'ug_ft'
1458        self.certificate2.start_level = 999
1459        self.certificate2.end_level = 999
1460        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1461            self.certificate2)
1462
1463        # Add study level to old study course
1464        studylevel = createObject(u'waeup.StudentStudyLevel')
1465        studylevel.level = 200
1466        self.student['studycourse'].addStudentStudyLevel(
1467            self.certificate, studylevel)
1468        studylevel = createObject(u'waeup.StudentStudyLevel')
1469        studylevel.level = 999
1470        self.student['studycourse'].addStudentStudyLevel(
1471            self.certificate, studylevel)
1472
1473        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1474        self.browser.open(self.student_path)
1475        self.browser.getLink("Transfer").click()
1476        self.browser.getControl(name="form.certificate").value = ['CERT2']
1477        self.browser.getControl(name="form.current_session").value = ['2011']
1478        self.browser.getControl(name="form.current_level").value = ['200']
1479        self.browser.getControl("Transfer").click()
1480        self.assertTrue(
1481            'Current level does not match certificate levels'
1482            in self.browser.contents)
1483        self.browser.getControl(name="form.current_level").value = ['999']
1484        self.browser.getControl("Transfer").click()
1485        self.assertTrue('Successfully transferred' in self.browser.contents)
1486        # The catalog has been updated
1487        cat = queryUtility(ICatalog, name='students_catalog')
1488        results = list(
1489            cat.searchResults(
1490            certcode=('CERT2', 'CERT2')))
1491        self.assertTrue(results[0] is self.student)
1492        results = list(
1493            cat.searchResults(
1494            current_session=(2011, 2011)))
1495        self.assertTrue(results[0] is self.student)
1496        # Add study level to new study course
1497        studylevel = createObject(u'waeup.StudentStudyLevel')
1498        studylevel.level = 999
1499        self.student['studycourse'].addStudentStudyLevel(
1500            self.certificate, studylevel)
1501
1502        # Edit and add pages are locked for old study courses
1503        self.browser.open(self.student_path + '/studycourse/manage')
1504        self.assertFalse('The requested form is locked' in self.browser.contents)
1505        self.browser.open(self.student_path + '/studycourse_1/manage')
1506        self.assertTrue('The requested form is locked' in self.browser.contents)
1507
1508        self.browser.open(self.student_path + '/studycourse/start_session')
1509        self.assertFalse('The requested form is locked' in self.browser.contents)
1510        self.browser.open(self.student_path + '/studycourse_1/start_session')
1511        self.assertTrue('The requested form is locked' in self.browser.contents)
1512
1513        IWorkflowState(self.student).setState('school fee paid')
1514        self.browser.open(self.student_path + '/studycourse/add')
1515        self.assertFalse('The requested form is locked' in self.browser.contents)
1516        self.browser.open(self.student_path + '/studycourse_1/add')
1517        self.assertTrue('The requested form is locked' in self.browser.contents)
1518
1519        self.browser.open(self.student_path + '/studycourse/999/manage')
1520        self.assertFalse('The requested form is locked' in self.browser.contents)
1521        self.browser.open(self.student_path + '/studycourse_1/999/manage')
1522        self.assertTrue('The requested form is locked' in self.browser.contents)
1523
1524        self.browser.open(self.student_path + '/studycourse/999/validate_courses')
1525        self.assertFalse('The requested form is locked' in self.browser.contents)
1526        self.browser.open(self.student_path + '/studycourse_1/999/validate_courses')
1527        self.assertTrue('The requested form is locked' in self.browser.contents)
1528
1529        self.browser.open(self.student_path + '/studycourse/999/reject_courses')
1530        self.assertFalse('The requested form is locked' in self.browser.contents)
1531        self.browser.open(self.student_path + '/studycourse_1/999/reject_courses')
1532        self.assertTrue('The requested form is locked' in self.browser.contents)
1533
1534        self.browser.open(self.student_path + '/studycourse/999/add')
1535        self.assertFalse('The requested form is locked' in self.browser.contents)
1536        self.browser.open(self.student_path + '/studycourse_1/999/add')
1537        self.assertTrue('The requested form is locked' in self.browser.contents)
1538
1539        self.browser.open(self.student_path + '/studycourse/999/edit')
1540        self.assertFalse('The requested form is locked' in self.browser.contents)
1541        self.browser.open(self.student_path + '/studycourse_1/999/edit')
1542        self.assertTrue('The requested form is locked' in self.browser.contents)
1543
1544    def test_login_as_student(self):
1545        # StudentImpersonators can login as student
1546        # Create clearance officer
1547        self.app['users'].addUser('mrofficer', 'mrofficersecret')
1548        self.app['users']['mrofficer'].email = 'mrofficer@foo.ng'
1549        self.app['users']['mrofficer'].title = 'Harry Actor'
1550        prmglobal = IPrincipalRoleManager(self.app)
1551        prmglobal.assignRoleToPrincipal('waeup.StudentImpersonator', 'mrofficer')
1552        prmglobal.assignRoleToPrincipal('waeup.StudentsManager', 'mrofficer')
1553        # Login as student impersonator
1554        self.browser.open(self.login_path)
1555        self.browser.getControl(name="form.login").value = 'mrofficer'
1556        self.browser.getControl(name="form.password").value = 'mrofficersecret'
1557        self.browser.getControl("Login").click()
1558        self.assertMatches('...You logged in...', self.browser.contents)
1559        self.browser.open(self.student_path)
1560        self.browser.getLink("Login as").click()
1561        self.browser.getControl("Set password now").click()
1562        temp_password = self.browser.getControl(name='form.password').value
1563        self.browser.getControl("Login now").click()
1564        self.assertMatches(
1565            '...You successfully logged in as...', self.browser.contents)
1566        # We are logged in as student and can see the 'My Data' tab
1567        self.assertMatches(
1568            '...<a href="#" class="dropdown-toggle">My Data</a>...',
1569            self.browser.contents)
1570        self.browser.getLink("Logout").click()
1571        # The student can't login with the original password ...
1572        self.browser.open(self.login_path)
1573        self.browser.getControl(name="form.login").value = self.student_id
1574        self.browser.getControl(name="form.password").value = 'spwd'
1575        self.browser.getControl("Login").click()
1576        self.assertMatches(
1577            '...Your account has been temporarily deactivated...',
1578            self.browser.contents)
1579        # ... but with the temporary password
1580        self.browser.open(self.login_path)
1581        self.browser.getControl(name="form.login").value = self.student_id
1582        self.browser.getControl(name="form.password").value = temp_password
1583        self.browser.getControl("Login").click()
1584        self.assertMatches('...You logged in...', self.browser.contents)
1585        # Creation of temp_password is properly logged
1586        logfile = os.path.join(
1587            self.app['datacenter'].storage, 'logs', 'students.log')
1588        logcontent = open(logfile).read()
1589        self.assertTrue(
1590            'mrofficer - students.browser.LoginAsStudentStep1 - K1000000 - '
1591            'temp_password generated: %s' % temp_password in logcontent)
1592
1593class StudentUITests(StudentsFullSetup):
1594    # Tests for Student class views and pages
1595
1596    def test_student_change_password(self):
1597        # Students can change the password
1598        self.browser.open(self.login_path)
1599        self.browser.getControl(name="form.login").value = self.student_id
1600        self.browser.getControl(name="form.password").value = 'spwd'
1601        self.browser.getControl("Login").click()
1602        self.assertEqual(self.browser.url, self.student_path)
1603        self.assertTrue('You logged in' in self.browser.contents)
1604        # Change password
1605        self.browser.getLink("Change password").click()
1606        self.browser.getControl(name="change_password").value = 'pw'
1607        self.browser.getControl(
1608            name="change_password_repeat").value = 'pw'
1609        self.browser.getControl("Save").click()
1610        self.assertTrue('Password must have at least' 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_passssword'
1614        self.browser.getControl("Save").click()
1615        self.assertTrue('Passwords do not match' in self.browser.contents)
1616        self.browser.getControl(name="change_password").value = 'new_password'
1617        self.browser.getControl(
1618            name="change_password_repeat").value = 'new_password'
1619        self.browser.getControl("Save").click()
1620        self.assertTrue('Password changed' in self.browser.contents)
1621        # We are still logged in. Changing the password hasn't thrown us out.
1622        self.browser.getLink("Base Data").click()
1623        self.assertEqual(self.browser.url, self.student_path)
1624        # We can logout
1625        self.browser.getLink("Logout").click()
1626        self.assertTrue('You have been logged out' in self.browser.contents)
1627        self.assertEqual(self.browser.url, 'http://localhost/app')
1628        # We can login again with the new password
1629        self.browser.getLink("Login").click()
1630        self.browser.open(self.login_path)
1631        self.browser.getControl(name="form.login").value = self.student_id
1632        self.browser.getControl(name="form.password").value = 'new_password'
1633        self.browser.getControl("Login").click()
1634        self.assertEqual(self.browser.url, self.student_path)
1635        self.assertTrue('You logged in' in self.browser.contents)
1636        return
1637
1638    def test_setpassword(self):
1639        # Set password for first-time access
1640        student = Student()
1641        student.reg_number = u'123456'
1642        student.firstname = u'Klaus'
1643        student.lastname = u'Tester'
1644        self.app['students'].addStudent(student)
1645        setpassword_path = 'http://localhost/app/setpassword'
1646        student_path = 'http://localhost/app/students/%s' % student.student_id
1647        self.browser.open(setpassword_path)
1648        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
1649        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1650        self.browser.getControl(name="reg_number").value = '223456'
1651        self.browser.getControl("Set").click()
1652        self.assertMatches('...No student found...',
1653                           self.browser.contents)
1654        self.browser.getControl(name="reg_number").value = '123456'
1655        self.browser.getControl(name="ac_number").value = '999999'
1656        self.browser.getControl("Set").click()
1657        self.assertMatches('...Access code is invalid...',
1658                           self.browser.contents)
1659        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1660        self.browser.getControl("Set").click()
1661        self.assertMatches('...Password has been set. Your Student Id is...',
1662                           self.browser.contents)
1663        self.browser.getControl("Set").click()
1664        self.assertMatches(
1665            '...Password has already been set. Your Student Id is...',
1666            self.browser.contents)
1667        existing_pwdpin = self.pwdpins[1]
1668        parts = existing_pwdpin.split('-')[1:]
1669        existing_pwdseries, existing_pwdnumber = parts
1670        self.browser.getControl(name="ac_series").value = existing_pwdseries
1671        self.browser.getControl(name="ac_number").value = existing_pwdnumber
1672        self.browser.getControl(name="reg_number").value = '123456'
1673        self.browser.getControl("Set").click()
1674        self.assertMatches(
1675            '...You are using the wrong Access Code...',
1676            self.browser.contents)
1677        # The student can login with the new credentials
1678        self.browser.open(self.login_path)
1679        self.browser.getControl(name="form.login").value = student.student_id
1680        self.browser.getControl(
1681            name="form.password").value = self.existing_pwdnumber
1682        self.browser.getControl("Login").click()
1683        self.assertEqual(self.browser.url, student_path)
1684        self.assertTrue('You logged in' in self.browser.contents)
1685        return
1686
1687    def test_student_login(self):
1688        # Student cant login if their password is not set
1689        self.student.password = None
1690        self.browser.open(self.login_path)
1691        self.browser.getControl(name="form.login").value = self.student_id
1692        self.browser.getControl(name="form.password").value = 'spwd'
1693        self.browser.getControl("Login").click()
1694        self.assertTrue(
1695            'You entered invalid credentials.' in self.browser.contents)
1696        # We set the password again
1697        IUserAccount(
1698            self.app['students'][self.student_id]).setPassword('spwd')
1699        # Students can't login if their account is suspended/deactivated
1700        self.student.suspended = True
1701        self.browser.open(self.login_path)
1702        self.browser.getControl(name="form.login").value = self.student_id
1703        self.browser.getControl(name="form.password").value = 'spwd'
1704        self.browser.getControl("Login").click()
1705        self.assertMatches(
1706            '...Your account has been deactivated...', self.browser.contents)
1707        self.student.suspended = False
1708        # Students can't login if a temporary password has been set and
1709        # is not expired
1710        self.app['students'][self.student_id].setTempPassword(
1711            'anybody', 'temp_spwd')
1712        self.browser.open(self.login_path)
1713        self.browser.getControl(name="form.login").value = self.student_id
1714        self.browser.getControl(name="form.password").value = 'spwd'
1715        self.browser.getControl("Login").click()
1716        self.assertMatches(
1717            '...Your account has been temporarily deactivated...',
1718            self.browser.contents)
1719        # The student can login with the temporary password
1720        self.browser.open(self.login_path)
1721        self.browser.getControl(name="form.login").value = self.student_id
1722        self.browser.getControl(name="form.password").value = 'temp_spwd'
1723        self.browser.getControl("Login").click()
1724        self.assertMatches(
1725            '...You logged in...', self.browser.contents)
1726        # Student can view the base data
1727        self.browser.open(self.student_path)
1728        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1729        self.assertEqual(self.browser.url, self.student_path)
1730        # When the password expires ...
1731        delta = timedelta(minutes=11)
1732        self.app['students'][self.student_id].temp_password[
1733            'timestamp'] = datetime.utcnow() - delta
1734        self.app['students'][self.student_id]._p_changed = True
1735        # ... the student will be automatically logged out
1736        self.assertRaises(
1737            Unauthorized, self.browser.open, self.student_path)
1738        # Then the student can login with the original password
1739        self.browser.open(self.login_path)
1740        self.browser.getControl(name="form.login").value = self.student_id
1741        self.browser.getControl(name="form.password").value = 'spwd'
1742        self.browser.getControl("Login").click()
1743        self.assertMatches(
1744            '...You logged in...', self.browser.contents)
1745
1746    def test_student_clearance(self):
1747        # Student cant login if their password is not set
1748        IWorkflowInfo(self.student).fireTransition('admit')
1749        self.browser.open(self.login_path)
1750        self.browser.getControl(name="form.login").value = self.student_id
1751        self.browser.getControl(name="form.password").value = 'spwd'
1752        self.browser.getControl("Login").click()
1753        self.assertMatches(
1754            '...You logged in...', self.browser.contents)
1755        # Admitted student can upload a passport picture
1756        self.browser.open(self.student_path + '/change_portrait')
1757        ctrl = self.browser.getControl(name='passportuploadedit')
1758        file_obj = open(SAMPLE_IMAGE, 'rb')
1759        file_ctrl = ctrl.mech_control
1760        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1761        self.browser.getControl(
1762            name='upload_passportuploadedit').click()
1763        self.assertTrue(
1764            '<img align="middle" height="125px" src="passport.jpg" />'
1765            in self.browser.contents)
1766        # Students can open admission letter
1767        self.browser.getLink("Base Data").click()
1768        self.browser.getLink("Download admission letter").click()
1769        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1770        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1771        # Student can view the clearance data
1772        self.browser.open(self.student_path)
1773        self.browser.getLink("Clearance Data").click()
1774        # Student can't open clearance edit form before starting clearance
1775        self.browser.open(self.student_path + '/cedit')
1776        self.assertMatches('...The requested form is locked...',
1777                           self.browser.contents)
1778        self.browser.getLink("Clearance Data").click()
1779        self.browser.getLink("Start clearance").click()
1780        self.student.email = None
1781        # Uups, we forgot to fill the email fields
1782        self.browser.getControl("Start clearance").click()
1783        self.assertMatches('...Not all required fields filled...',
1784                           self.browser.contents)
1785        self.browser.open(self.student_path + '/edit_base')
1786        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1787        self.browser.getControl("Save").click()
1788        self.browser.open(self.student_path + '/start_clearance')
1789        self.browser.getControl(name="ac_series").value = '3'
1790        self.browser.getControl(name="ac_number").value = '4444444'
1791        self.browser.getControl("Start clearance now").click()
1792        self.assertMatches('...Activation code is invalid...',
1793                           self.browser.contents)
1794        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1795        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1796        # Owner is Hans Wurst, AC can't be invalidated
1797        self.browser.getControl("Start clearance now").click()
1798        self.assertMatches('...You are not the owner of this access code...',
1799                           self.browser.contents)
1800        # Set the correct owner
1801        self.existing_clrac.owner = self.student_id
1802        # clr_code might be set (and thus returns None) due importing
1803        # an empty clr_code column.
1804        self.student.clr_code = None
1805        self.browser.getControl("Start clearance now").click()
1806        self.assertMatches('...Clearance process has been started...',
1807                           self.browser.contents)
1808        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1809        self.browser.getControl("Save", index=0).click()
1810        # Student can view the clearance data
1811        self.browser.getLink("Clearance Data").click()
1812        # and go back to the edit form
1813        self.browser.getLink("Edit").click()
1814        # Students can upload documents
1815        ctrl = self.browser.getControl(name='birthcertificateupload')
1816        file_obj = open(SAMPLE_IMAGE, 'rb')
1817        file_ctrl = ctrl.mech_control
1818        file_ctrl.add_file(file_obj, filename='my_birth_certificate.jpg')
1819        self.browser.getControl(
1820            name='upload_birthcertificateupload').click()
1821        self.assertTrue(
1822            '<a target="image" href="birth_certificate">Birth Certificate Scan</a>'
1823            in self.browser.contents)
1824        # Students can open clearance slip
1825        self.browser.getLink("View").click()
1826        self.browser.getLink("Download clearance slip").click()
1827        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1828        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1829        # Students can request clearance
1830        self.browser.open(self.edit_clearance_path)
1831        self.browser.getControl("Save and request clearance").click()
1832        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1833        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1834        self.browser.getControl("Request clearance now").click()
1835        self.assertMatches('...Clearance has been requested...',
1836                           self.browser.contents)
1837        # Student can't reopen clearance form after requesting clearance
1838        self.browser.open(self.student_path + '/cedit')
1839        self.assertMatches('...The requested form is locked...',
1840                           self.browser.contents)
1841
1842    def test_student_course_registration(self):
1843        # Student cant login if their password is not set
1844        IWorkflowInfo(self.student).fireTransition('admit')
1845        self.browser.open(self.login_path)
1846        self.browser.getControl(name="form.login").value = self.student_id
1847        self.browser.getControl(name="form.password").value = 'spwd'
1848        self.browser.getControl("Login").click()
1849        # Student can't add study level if not in state 'school fee paid'
1850        self.browser.open(self.student_path + '/studycourse/add')
1851        self.assertMatches('...The requested form is locked...',
1852                           self.browser.contents)
1853        # ... and must be transferred first
1854        IWorkflowState(self.student).setState('school fee paid')
1855        # Now students can add the current study level
1856        self.browser.getLink("Study Course").click()
1857        self.browser.getLink("Add course list").click()
1858        self.assertMatches('...Add current level 100 (Year 1)...',
1859                           self.browser.contents)
1860        self.browser.getControl("Create course list now").click()
1861        # A level with one course ticket was created
1862        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1863        self.browser.getLink("100").click()
1864        self.browser.getLink("Edit course list").click()
1865        self.browser.getControl("Add course ticket").click()
1866        self.browser.getControl(name="form.course").value = ['COURSE1']
1867        self.browser.getControl("Add course ticket").click()
1868        self.assertMatches('...The ticket exists...',
1869                           self.browser.contents)
1870        self.student['studycourse'].current_level = 200
1871        self.browser.getLink("Study Course").click()
1872        self.browser.getLink("Add course list").click()
1873        self.assertMatches('...Add current level 200 (Year 2)...',
1874                           self.browser.contents)
1875        self.browser.getControl("Create course list now").click()
1876        self.browser.getLink("200").click()
1877        self.browser.getLink("Edit course list").click()
1878        self.browser.getControl("Add course ticket").click()
1879        self.browser.getControl(name="form.course").value = ['COURSE1']
1880        self.course.credits = 100
1881        self.browser.getControl("Add course ticket").click()
1882        self.assertMatches(
1883            '...Your total credits exceed 58...', self.browser.contents)
1884        self.course.credits = 10
1885        self.browser.getControl("Add course ticket").click()
1886        self.assertMatches('...The ticket exists...',
1887                           self.browser.contents)
1888        # Indeed the ticket exists as carry-over course from level 100
1889        # since its score was 0
1890        self.assertTrue(
1891            self.student['studycourse']['200']['COURSE1'].carry_over is True)
1892        # Students can open the pdf course registration slip
1893        self.browser.open(self.student_path + '/studycourse/200')
1894        self.browser.getLink("Download course registration slip").click()
1895        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1896        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1897        # Students can remove course tickets
1898        self.browser.open(self.student_path + '/studycourse/200/edit')
1899        self.browser.getControl("Remove selected", index=0).click()
1900        self.assertTrue('No ticket selected' in self.browser.contents)
1901        # No ticket can be selected since the carry-over course is a core course
1902        self.assertRaises(
1903            LookupError, self.browser.getControl, name='val_id')
1904        self.student['studycourse']['200']['COURSE1'].mandatory = False
1905        self.browser.open(self.student_path + '/studycourse/200/edit')
1906        # Course list can't be registered if total_credits exceeds max_credits
1907        self.student['studycourse']['200']['COURSE1'].credits = 60
1908        self.browser.getControl("Register course list").click()
1909        self.assertTrue('Maximum credits of 50 exceeded' in self.browser.contents)
1910        # Student can now remove the ticket
1911        ctrl = self.browser.getControl(name='val_id')
1912        ctrl.getControl(value='COURSE1').selected = True
1913        self.browser.getControl("Remove selected", index=0).click()
1914        self.assertTrue('Successfully removed' in self.browser.contents)
1915        # Course list can be registered, even if it's empty
1916        self.browser.getControl("Register course list").click()
1917        self.assertTrue('Course list has been registered' in self.browser.contents)
1918        self.assertEqual(self.student.state, 'courses registered')
1919        return
1920
1921    def test_postgraduate_student_access(self):
1922        self.certificate.study_mode = 'pg_ft'
1923        self.certificate.start_level = 999
1924        self.certificate.end_level = 999
1925        self.student['studycourse'].current_level = 999
1926        IWorkflowState(self.student).setState('school fee paid')
1927        self.browser.open(self.login_path)
1928        self.browser.getControl(name="form.login").value = self.student_id
1929        self.browser.getControl(name="form.password").value = 'spwd'
1930        self.browser.getControl("Login").click()
1931        self.assertTrue(
1932            'You logged in.' in self.browser.contents)
1933        # Now students can add the current study level
1934        self.browser.getLink("Study Course").click()
1935        self.browser.getLink("Add course list").click()
1936        self.assertMatches('...Add current level Postgraduate Level...',
1937                           self.browser.contents)
1938        self.browser.getControl("Create course list now").click()
1939        # A level with one course ticket was created
1940        self.assertEqual(self.student['studycourse']['999'].number_of_tickets, 0)
1941        self.browser.getLink("999").click()
1942        self.browser.getLink("Edit course list").click()
1943        self.browser.getControl("Add course ticket").click()
1944        self.browser.getControl(name="form.course").value = ['COURSE1']
1945        self.browser.getControl("Add course ticket").click()
1946        self.assertMatches('...Successfully added COURSE1...',
1947                           self.browser.contents)
1948        # Postgraduate students can't register course lists
1949        self.browser.getControl("Register course list").click()
1950        self.assertTrue("your course list can't bee registered"
1951            in self.browser.contents)
1952        self.assertEqual(self.student.state, 'school fee paid')
1953        return
1954
1955    def test_student_clearance_wo_clrcode(self):
1956        IWorkflowState(self.student).setState('clearance started')
1957        self.browser.open(self.login_path)
1958        self.browser.getControl(name="form.login").value = self.student_id
1959        self.browser.getControl(name="form.password").value = 'spwd'
1960        self.browser.getControl("Login").click()
1961        self.student.clearance_locked = False
1962        self.browser.open(self.edit_clearance_path)
1963        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1964        self.browser.getControl("Save and request clearance").click()
1965        self.assertMatches('...Clearance has been requested...',
1966                           self.browser.contents)
1967
1968    def test_student_clearance_payment(self):
1969        # Login
1970        self.browser.open(self.login_path)
1971        self.browser.getControl(name="form.login").value = self.student_id
1972        self.browser.getControl(name="form.password").value = 'spwd'
1973        self.browser.getControl("Login").click()
1974
1975        # Students can add online clearance payment tickets
1976        self.browser.open(self.payments_path + '/addop')
1977        self.browser.getControl(name="form.p_category").value = ['clearance']
1978        self.browser.getControl("Create ticket").click()
1979        self.assertMatches('...ticket created...',
1980                           self.browser.contents)
1981
1982        # Students can't approve the payment
1983        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
1984        ctrl = self.browser.getControl(name='val_id')
1985        value = ctrl.options[0]
1986        self.browser.getLink(value).click()
1987        payment_url = self.browser.url
1988        self.assertRaises(
1989            Unauthorized, self.browser.open, payment_url + '/approve')
1990        # In the base package they can 'use' a fake approval view
1991        self.browser.open(payment_url + '/fake_approve')
1992        self.assertMatches('...Payment approved...',
1993                          self.browser.contents)
1994        expected = '''...
1995        <td>
1996          <span>Paid</span>
1997        </td>...'''
1998        expected = '''...
1999        <td>
2000          <span>Paid</span>
2001        </td>...'''
2002        self.assertMatches(expected,self.browser.contents)
2003        payment_id = self.student['payments'].keys()[0]
2004        payment = self.student['payments'][payment_id]
2005        self.assertEqual(payment.p_state, 'paid')
2006        self.assertEqual(payment.r_amount_approved, 3456.0)
2007        self.assertEqual(payment.r_code, 'AP')
2008        self.assertEqual(payment.r_desc, u'Payment approved by Anna Tester')
2009        # The new CLR-0 pin has been created
2010        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
2011        pin = self.app['accesscodes']['CLR-0'].keys()[0]
2012        ac = self.app['accesscodes']['CLR-0'][pin]
2013        self.assertEqual(ac.owner, self.student_id)
2014        self.assertEqual(ac.cost, 3456.0)
2015
2016        # Students can open the pdf payment slip
2017        self.browser.open(payment_url + '/payment_slip.pdf')
2018        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2019        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2020
2021        # The new CLR-0 pin can be used for starting clearance
2022        # but they have to upload a passport picture first
2023        # which is only possible in state admitted
2024        self.browser.open(self.student_path + '/change_portrait')
2025        self.assertMatches('...form is locked...',
2026                          self.browser.contents)
2027        IWorkflowInfo(self.student).fireTransition('admit')
2028        self.browser.open(self.student_path + '/change_portrait')
2029        image = open(SAMPLE_IMAGE, 'rb')
2030        ctrl = self.browser.getControl(name='passportuploadedit')
2031        file_ctrl = ctrl.mech_control
2032        file_ctrl.add_file(image, filename='my_photo.jpg')
2033        self.browser.getControl(
2034            name='upload_passportuploadedit').click()
2035        self.browser.open(self.student_path + '/start_clearance')
2036        parts = pin.split('-')[1:]
2037        clrseries, clrnumber = parts
2038        self.browser.getControl(name="ac_series").value = clrseries
2039        self.browser.getControl(name="ac_number").value = clrnumber
2040        self.browser.getControl("Start clearance now").click()
2041        self.assertMatches('...Clearance process has been started...',
2042                           self.browser.contents)
2043
2044    def test_student_schoolfee_payment(self):
2045        configuration = createObject('waeup.SessionConfiguration')
2046        configuration.academic_session = 2005
2047        self.app['configuration'].addSessionConfiguration(configuration)
2048        # Login
2049        self.browser.open(self.login_path)
2050        self.browser.getControl(name="form.login").value = self.student_id
2051        self.browser.getControl(name="form.password").value = 'spwd'
2052        self.browser.getControl("Login").click()
2053
2054        # Students can add online school fee payment tickets.
2055        IWorkflowState(self.student).setState('returning')
2056        self.browser.open(self.payments_path)
2057        self.assertRaises(
2058            LookupError, self.browser.getControl, name='val_id')
2059        self.browser.getLink("Add current session payment ticket").click()
2060        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2061        self.browser.getControl("Create ticket").click()
2062        self.assertMatches('...ticket created...',
2063                           self.browser.contents)
2064        ctrl = self.browser.getControl(name='val_id')
2065        value = ctrl.options[0]
2066        self.browser.getLink(value).click()
2067        self.assertMatches('...Amount Authorized...',
2068                           self.browser.contents)
2069        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2070        # Payment session and will be calculated as defined
2071        # in w.k.students.utils because we set changed the state
2072        # to returning
2073        self.assertEqual(self.student['payments'][value].p_session, 2005)
2074        self.assertEqual(self.student['payments'][value].p_level, 200)
2075
2076        # Student is the payee of the payment ticket.
2077        webservice = IPaymentWebservice(self.student['payments'][value])
2078        self.assertEqual(webservice.display_fullname, 'Anna Tester')
2079        self.assertEqual(webservice.id, self.student_id)
2080        self.assertEqual(webservice.faculty, 'fac1')
2081        self.assertEqual(webservice.department, 'dep1')
2082
2083        # We simulate the approval
2084        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2085        self.browser.open(self.browser.url + '/fake_approve')
2086        self.assertMatches('...Payment approved...',
2087                          self.browser.contents)
2088
2089        # The new SFE-0 pin can be used for starting new session
2090        self.browser.open(self.studycourse_path)
2091        self.browser.getLink('Start new session').click()
2092        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2093        parts = pin.split('-')[1:]
2094        sfeseries, sfenumber = parts
2095        self.browser.getControl(name="ac_series").value = sfeseries
2096        self.browser.getControl(name="ac_number").value = sfenumber
2097        self.browser.getControl("Start now").click()
2098        self.assertMatches('...Session started...',
2099                           self.browser.contents)
2100        self.assertTrue(self.student.state == 'school fee paid')
2101        return
2102
2103    def test_student_bedallocation_payment(self):
2104        # Login
2105        self.browser.open(self.login_path)
2106        self.browser.getControl(name="form.login").value = self.student_id
2107        self.browser.getControl(name="form.password").value = 'spwd'
2108        self.browser.getControl("Login").click()
2109        self.browser.open(self.payments_path)
2110        self.browser.open(self.payments_path + '/addop')
2111        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2112        self.browser.getControl("Create ticket").click()
2113        self.assertMatches('...ticket created...',
2114                           self.browser.contents)
2115        # Students can remove only online payment tickets which have
2116        # not received a valid callback
2117        self.browser.open(self.payments_path)
2118        ctrl = self.browser.getControl(name='val_id')
2119        value = ctrl.options[0]
2120        ctrl.getControl(value=value).selected = True
2121        self.browser.getControl("Remove selected", index=0).click()
2122        self.assertTrue('Successfully removed' in self.browser.contents)
2123
2124    def test_student_maintenance_payment(self):
2125        # Login
2126        self.browser.open(self.login_path)
2127        self.browser.getControl(name="form.login").value = self.student_id
2128        self.browser.getControl(name="form.password").value = 'spwd'
2129        self.browser.getControl("Login").click()
2130        self.browser.open(self.payments_path)
2131        self.browser.open(self.payments_path + '/addop')
2132        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2133        self.browser.getControl("Create ticket").click()
2134        self.assertMatches('...You have not yet booked accommodation...',
2135                           self.browser.contents)
2136        # We continue this test in test_student_accommodation
2137
2138    def test_student_previous_payments(self):
2139        configuration = createObject('waeup.SessionConfiguration')
2140        configuration.academic_session = 2000
2141        configuration.clearance_fee = 3456.0
2142        configuration.booking_fee = 123.4
2143        self.app['configuration'].addSessionConfiguration(configuration)
2144        configuration2 = createObject('waeup.SessionConfiguration')
2145        configuration2.academic_session = 2003
2146        configuration2.clearance_fee = 3456.0
2147        configuration2.booking_fee = 123.4
2148        self.app['configuration'].addSessionConfiguration(configuration2)
2149        configuration3 = createObject('waeup.SessionConfiguration')
2150        configuration3.academic_session = 2005
2151        configuration3.clearance_fee = 3456.0
2152        configuration3.booking_fee = 123.4
2153        self.app['configuration'].addSessionConfiguration(configuration3)
2154        self.student['studycourse'].entry_session = 2002
2155
2156        # Login
2157        self.browser.open(self.login_path)
2158        self.browser.getControl(name="form.login").value = self.student_id
2159        self.browser.getControl(name="form.password").value = 'spwd'
2160        self.browser.getControl("Login").click()
2161
2162        # Students can add previous school fee payment tickets in any state.
2163        IWorkflowState(self.student).setState('courses registered')
2164        self.browser.open(self.payments_path)
2165        self.browser.getLink("Add previous session payment ticket").click()
2166
2167        # Previous session payment form is provided
2168        self.assertEqual(self.student.current_session, 2004)
2169        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2170        self.browser.getControl(name="form.p_session").value = ['2000']
2171        self.browser.getControl(name="form.p_level").value = ['300']
2172        self.browser.getControl("Create ticket").click()
2173        self.assertMatches('...The previous session must not fall below...',
2174                           self.browser.contents)
2175        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2176        self.browser.getControl(name="form.p_session").value = ['2005']
2177        self.browser.getControl(name="form.p_level").value = ['300']
2178        self.browser.getControl("Create ticket").click()
2179        self.assertMatches('...This is not a previous session...',
2180                           self.browser.contents)
2181        # Students can pay current session school fee.
2182        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2183        self.browser.getControl(name="form.p_session").value = ['2004']
2184        self.browser.getControl(name="form.p_level").value = ['300']
2185        self.browser.getControl("Create ticket").click()
2186        self.assertMatches('...ticket created...',
2187                           self.browser.contents)
2188        ctrl = self.browser.getControl(name='val_id')
2189        value = ctrl.options[0]
2190        self.browser.getLink(value).click()
2191        self.assertMatches('...Amount Authorized...',
2192                           self.browser.contents)
2193        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2194
2195        # Payment session is properly set
2196        self.assertEqual(self.student['payments'][value].p_session, 2004)
2197        self.assertEqual(self.student['payments'][value].p_level, 300)
2198
2199        # We simulate the approval
2200        self.browser.open(self.browser.url + '/fake_approve')
2201        self.assertMatches('...Payment approved...',
2202                          self.browser.contents)
2203
2204        # No AC has been created
2205        self.assertEqual(len(self.app['accesscodes']['SFE-0'].keys()), 0)
2206        self.assertTrue(self.student['payments'][value].ac is None)
2207
2208        # Current payment flag is set False
2209        self.assertFalse(self.student['payments'][value].p_current)
2210
2211        # Button and form are not available for students who are in
2212        # states up to cleared
2213        self.student['studycourse'].entry_session = 2004
2214        IWorkflowState(self.student).setState('cleared')
2215        self.browser.open(self.payments_path)
2216        self.assertFalse(
2217            "Add previous session payment ticket" in self.browser.contents)
2218        self.browser.open(self.payments_path + '/addpp')
2219        self.assertTrue(
2220            "No previous payment to be made" in self.browser.contents)
2221        return
2222
2223    def test_postgraduate_student_payments(self):
2224        configuration = createObject('waeup.SessionConfiguration')
2225        configuration.academic_session = 2005
2226        self.app['configuration'].addSessionConfiguration(configuration)
2227        self.certificate.study_mode = 'pg_ft'
2228        self.certificate.start_level = 999
2229        self.certificate.end_level = 999
2230        self.student['studycourse'].current_level = 999
2231        # Login
2232        self.browser.open(self.login_path)
2233        self.browser.getControl(name="form.login").value = self.student_id
2234        self.browser.getControl(name="form.password").value = 'spwd'
2235        self.browser.getControl("Login").click()
2236        # Students can add online school fee payment tickets.
2237        IWorkflowState(self.student).setState('cleared')
2238        self.browser.open(self.payments_path)
2239        self.browser.getLink("Add current session payment ticket").click()
2240        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2241        self.browser.getControl("Create ticket").click()
2242        self.assertMatches('...ticket created...',
2243                           self.browser.contents)
2244        ctrl = self.browser.getControl(name='val_id')
2245        value = ctrl.options[0]
2246        self.browser.getLink(value).click()
2247        self.assertMatches('...Amount Authorized...',
2248                           self.browser.contents)
2249        # Payment session and level are current ones.
2250        # Postgrads have to pay school_fee_1.
2251        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
2252        self.assertEqual(self.student['payments'][value].p_session, 2004)
2253        self.assertEqual(self.student['payments'][value].p_level, 999)
2254
2255        # We simulate the approval
2256        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2257        self.browser.open(self.browser.url + '/fake_approve')
2258        self.assertMatches('...Payment approved...',
2259                          self.browser.contents)
2260
2261        # The new SFE-0 pin can be used for starting session
2262        self.browser.open(self.studycourse_path)
2263        self.browser.getLink('Start new session').click()
2264        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2265        parts = pin.split('-')[1:]
2266        sfeseries, sfenumber = parts
2267        self.browser.getControl(name="ac_series").value = sfeseries
2268        self.browser.getControl(name="ac_number").value = sfenumber
2269        self.browser.getControl("Start now").click()
2270        self.assertMatches('...Session started...',
2271                           self.browser.contents)
2272        self.assertTrue(self.student.state == 'school fee paid')
2273
2274        # Postgrad students do not need to register courses the
2275        # can just pay for the next session.
2276        self.browser.open(self.payments_path)
2277        # Remove first payment to be sure that we access the right ticket
2278        del self.student['payments'][value]
2279        self.browser.getLink("Add current session payment ticket").click()
2280        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2281        self.browser.getControl("Create ticket").click()
2282        ctrl = self.browser.getControl(name='val_id')
2283        value = ctrl.options[0]
2284        self.browser.getLink(value).click()
2285        # Payment session has increased by one, payment level remains the same.
2286        # Returning Postgraduates have to pay school_fee_2.
2287        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2288        self.assertEqual(self.student['payments'][value].p_session, 2005)
2289        self.assertEqual(self.student['payments'][value].p_level, 999)
2290
2291        # Student is still in old session
2292        self.assertEqual(self.student.current_session, 2004)
2293
2294        # We do not need to pay the ticket if any other
2295        # SFE pin is provided
2296        pin_container = self.app['accesscodes']
2297        pin_container.createBatch(
2298            datetime.utcnow(), 'some_userid', 'SFE', 9.99, 1)
2299        pin = pin_container['SFE-1'].values()[0].representation
2300        sfeseries, sfenumber = pin.split('-')[1:]
2301        # The new SFE-1 pin can be used for starting new session
2302        self.browser.open(self.studycourse_path)
2303        self.browser.getLink('Start new session').click()
2304        self.browser.getControl(name="ac_series").value = sfeseries
2305        self.browser.getControl(name="ac_number").value = sfenumber
2306        self.browser.getControl("Start now").click()
2307        self.assertMatches('...Session started...',
2308                           self.browser.contents)
2309        self.assertTrue(self.student.state == 'school fee paid')
2310        # Student is in new session
2311        self.assertEqual(self.student.current_session, 2005)
2312        self.assertEqual(self.student['studycourse'].current_level, 999)
2313        return
2314
2315    def test_student_accommodation(self):
2316        # Login
2317        self.browser.open(self.login_path)
2318        self.browser.getControl(name="form.login").value = self.student_id
2319        self.browser.getControl(name="form.password").value = 'spwd'
2320        self.browser.getControl("Login").click()
2321
2322        # Students can add online booking fee payment tickets and open the
2323        # callback view (see test_manage_payments)
2324        self.browser.getLink("Payments").click()
2325        self.browser.getLink("Add current session payment ticket").click()
2326        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2327        self.browser.getControl("Create ticket").click()
2328        ctrl = self.browser.getControl(name='val_id')
2329        value = ctrl.options[0]
2330        self.browser.getLink(value).click()
2331        self.browser.open(self.browser.url + '/fake_approve')
2332        # The new HOS-0 pin has been created
2333        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
2334        pin = self.app['accesscodes']['HOS-0'].keys()[0]
2335        ac = self.app['accesscodes']['HOS-0'][pin]
2336        parts = pin.split('-')[1:]
2337        sfeseries, sfenumber = parts
2338
2339        # Students can use HOS code and book a bed space with it ...
2340        self.browser.open(self.acco_path)
2341        # ... but not if booking period has expired ...
2342        self.app['hostels'].enddate = datetime.now(pytz.utc)
2343        self.browser.getLink("Book accommodation").click()
2344        self.assertMatches('...Outside booking period: ...',
2345                           self.browser.contents)
2346        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
2347        # ... or student is not the an allowed state ...
2348        self.browser.getLink("Book accommodation").click()
2349        self.assertMatches('...You are in the wrong...',
2350                           self.browser.contents)
2351        IWorkflowInfo(self.student).fireTransition('admit')
2352        self.browser.getLink("Book accommodation").click()
2353        self.assertMatches('...Activation Code:...',
2354                           self.browser.contents)
2355        # Student can't used faked ACs ...
2356        self.browser.getControl(name="ac_series").value = u'nonsense'
2357        self.browser.getControl(name="ac_number").value = sfenumber
2358        self.browser.getControl("Create bed ticket").click()
2359        self.assertMatches('...Activation code is invalid...',
2360                           self.browser.contents)
2361        # ... or ACs owned by somebody else.
2362        ac.owner = u'Anybody'
2363        self.browser.getControl(name="ac_series").value = sfeseries
2364        self.browser.getControl(name="ac_number").value = sfenumber
2365        self.browser.getControl("Create bed ticket").click()
2366        self.assertMatches('...You are not the owner of this access code...',
2367                           self.browser.contents)
2368        ac.owner = self.student_id
2369        self.browser.getControl(name="ac_series").value = sfeseries
2370        self.browser.getControl(name="ac_number").value = sfenumber
2371        self.browser.getControl("Create bed ticket").click()
2372        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2373                           self.browser.contents)
2374
2375        # Bed has been allocated
2376        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
2377        self.assertTrue(bed.owner == self.student_id)
2378
2379        # BedTicketAddPage is now blocked
2380        self.browser.getLink("Book accommodation").click()
2381        self.assertMatches('...You already booked a bed space...',
2382            self.browser.contents)
2383
2384        # The bed ticket displays the data correctly
2385        self.browser.open(self.acco_path + '/2004')
2386        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2387                           self.browser.contents)
2388        self.assertMatches('...2004/2005...', self.browser.contents)
2389        self.assertMatches('...regular_male_fr...', self.browser.contents)
2390        self.assertMatches('...%s...' % pin, self.browser.contents)
2391
2392        # Students can open the pdf slip
2393        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
2394        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2395        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2396
2397        # Students can't relocate themselves
2398        self.assertFalse('Relocate' in self.browser.contents)
2399        relocate_path = self.acco_path + '/2004/relocate'
2400        self.assertRaises(
2401            Unauthorized, self.browser.open, relocate_path)
2402
2403        # Students can't the Remove button and check boxes
2404        self.browser.open(self.acco_path)
2405        self.assertFalse('Remove' in self.browser.contents)
2406        self.assertFalse('val_id' in self.browser.contents)
2407
2408        # Students can pay maintenance fee now
2409        self.browser.open(self.payments_path)
2410        self.browser.open(self.payments_path + '/addop')
2411        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2412        self.browser.getControl("Create ticket").click()
2413        self.assertMatches('...Payment ticket created...',
2414                           self.browser.contents)
2415        return
2416
2417    def test_change_password_request(self):
2418        self.browser.open('http://localhost/app/changepw')
2419        self.browser.getControl(name="form.identifier").value = '123'
2420        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
2421        self.browser.getControl("Send login credentials").click()
2422        self.assertTrue('An email with' in self.browser.contents)
2423
2424    def test_student_expired_personal_data(self):
2425        # Login
2426        delta = timedelta(days=180)
2427        self.student.personal_updated = datetime.utcnow() - delta
2428        self.browser.open(self.login_path)
2429        self.browser.getControl(name="form.login").value = self.student_id
2430        self.browser.getControl(name="form.password").value = 'spwd'
2431        self.browser.getControl("Login").click()
2432        self.assertEqual(self.browser.url, self.student_path)
2433        self.assertTrue(
2434            'You logged in' in self.browser.contents)
2435        self.browser.getLink("Logout").click()
2436        delta = timedelta(days=181)
2437        self.student.personal_updated = datetime.utcnow() - delta
2438        self.browser.open(self.login_path)
2439        self.browser.getControl(name="form.login").value = self.student_id
2440        self.browser.getControl(name="form.password").value = 'spwd'
2441        self.browser.getControl("Login").click()
2442        self.assertEqual(self.browser.url, self.edit_personal_path)
2443        self.assertTrue(
2444            'Your personal data record expired' in self.browser.contents)
2445
2446class StudentRequestPWTests(StudentsFullSetup):
2447    # Tests for student registration
2448
2449    layer = FunctionalLayer
2450
2451    def test_request_pw(self):
2452        # Student with wrong number can't be found.
2453        self.browser.open('http://localhost/app/requestpw')
2454        self.browser.getControl(name="form.firstname").value = 'Anna'
2455        self.browser.getControl(name="form.number").value = 'anynumber'
2456        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2457        self.browser.getControl("Send login credentials").click()
2458        self.assertTrue('No student record found.'
2459            in self.browser.contents)
2460        # Anonymous is not informed that firstname verification failed.
2461        # It seems that the record doesn't exist.
2462        self.browser.open('http://localhost/app/requestpw')
2463        self.browser.getControl(name="form.firstname").value = 'Johnny'
2464        self.browser.getControl(name="form.number").value = '123'
2465        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2466        self.browser.getControl("Send login credentials").click()
2467        self.assertTrue('No student record found.'
2468            in self.browser.contents)
2469        # Even with the correct firstname we can't register if a
2470        # password has been set and used.
2471        self.browser.getControl(name="form.firstname").value = 'Anna'
2472        self.browser.getControl(name="form.number").value = '123'
2473        self.browser.getControl("Send login credentials").click()
2474        self.assertTrue('Your password has already been set and used.'
2475            in self.browser.contents)
2476        self.browser.open('http://localhost/app/requestpw')
2477        self.app['students'][self.student_id].password = None
2478        # The firstname field, used for verification, is not case-sensitive.
2479        self.browser.getControl(name="form.firstname").value = 'aNNa'
2480        self.browser.getControl(name="form.number").value = '123'
2481        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2482        self.browser.getControl("Send login credentials").click()
2483        # Yeah, we succeded ...
2484        self.assertTrue('Your password request was successful.'
2485            in self.browser.contents)
2486        # We can also use the matric_number instead.
2487        self.browser.open('http://localhost/app/requestpw')
2488        self.browser.getControl(name="form.firstname").value = 'aNNa'
2489        self.browser.getControl(name="form.number").value = '234'
2490        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2491        self.browser.getControl("Send login credentials").click()
2492        self.assertTrue('Your password request was successful.'
2493            in self.browser.contents)
2494        # ... and  student can be found in the catalog via the email address
2495        cat = queryUtility(ICatalog, name='students_catalog')
2496        results = list(
2497            cat.searchResults(
2498            email=('new@yy.zz', 'new@yy.zz')))
2499        self.assertEqual(self.student,results[0])
2500        logfile = os.path.join(
2501            self.app['datacenter'].storage, 'logs', 'main.log')
2502        logcontent = open(logfile).read()
2503        self.assertTrue('zope.anybody - students.browser.StudentRequestPasswordPage - '
2504                        '234 (K1000000) - new@yy.zz' in logcontent)
2505        return
2506
2507    def test_student_locked_level_forms(self):
2508
2509        # Add two study levels, one current and one previous
2510        studylevel = createObject(u'waeup.StudentStudyLevel')
2511        studylevel.level = 100
2512        self.student['studycourse'].addStudentStudyLevel(
2513            self.certificate, studylevel)
2514        studylevel = createObject(u'waeup.StudentStudyLevel')
2515        studylevel.level = 200
2516        self.student['studycourse'].addStudentStudyLevel(
2517            self.certificate, studylevel)
2518        IWorkflowState(self.student).setState('school fee paid')
2519        self.student['studycourse'].current_level = 200
2520
2521        self.browser.open(self.login_path)
2522        self.browser.getControl(name="form.login").value = self.student_id
2523        self.browser.getControl(name="form.password").value = 'spwd'
2524        self.browser.getControl("Login").click()
2525
2526        self.browser.open(self.student_path + '/studycourse/200/edit')
2527        self.assertFalse('The requested form is locked' in self.browser.contents)
2528        self.browser.open(self.student_path + '/studycourse/100/edit')
2529        self.assertTrue('The requested form is locked' in self.browser.contents)
2530
2531        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2532        self.assertFalse('The requested form is locked' in self.browser.contents)
2533        self.browser.open(self.student_path + '/studycourse/100/ctadd')
2534        self.assertTrue('The requested form is locked' in self.browser.contents)
2535
2536        IWorkflowState(self.student).setState('courses registered')
2537        self.browser.open(self.student_path + '/studycourse/200/edit')
2538        self.assertTrue('The requested form is locked' in self.browser.contents)
2539        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2540        self.assertTrue('The requested form is locked' in self.browser.contents)
2541
2542
2543class PublicPagesTests(StudentsFullSetup):
2544    # Tests for swebservices
2545
2546    layer = FunctionalLayer
2547
2548    def test_paymentrequest(self):
2549        payment = createObject('waeup.StudentOnlinePayment')
2550        payment.p_category = u'schoolfee'
2551        payment.p_session = self.student.current_session
2552        payment.p_item = u'My Certificate'
2553        payment.p_id = u'anyid'
2554        self.student['payments']['anykey'] = payment
2555        # Request information about unpaid payment ticket
2556        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2557        self.assertEqual(self.browser.contents, '-1')
2558        # Request information about paid payment ticket
2559        payment.p_state = u'paid'
2560        notify(grok.ObjectModifiedEvent(payment))
2561        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2562        self.assertEqual(self.browser.contents,
2563            'FULL_NAME=Anna Tester&FACULTY=fac1&DEPARTMENT=dep1'
2564            '&PAYMENT_ITEM=My Certificate&PAYMENT_CATEGORY=School Fee'
2565            '&ACADEMIC_SESSION=2004/2005&MATRIC_NUMBER=234&FEE_AMOUNT=0.0')
2566        self.browser.open('http://localhost/app/paymentrequest?NONSENSE=nonsense')
2567        self.assertEqual(self.browser.contents, '-1')
2568        self.browser.open('http://localhost/app/paymentrequest?P_ID=nonsense')
2569        self.assertEqual(self.browser.contents, '-1')
Note: See TracBrowser for help on using the repository browser.