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

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

Move test to kofacustom.nigeria.

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