source: main/waeup.kofa/backup-trunk/src/waeup/kofa/students/tests/test_browser.py @ 10009

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

Previous session must really be a previous session.

  • Property svn:keywords set to Id
File size: 111.3 KB
Line 
1## $Id: test_browser.py 9204 2012-09-19 13:59:39Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Test the student-related UI components.
20"""
21import shutil
22import tempfile
23import pytz
24from datetime import datetime, timedelta
25from StringIO import StringIO
26import os
27import grok
28from zope.event import notify
29from zope.component import createObject, queryUtility
30from zope.component.hooks import setSite, clearSite
31from zope.catalog.interfaces import ICatalog
32from zope.security.interfaces import Unauthorized
33from zope.securitypolicy.interfaces import IPrincipalRoleManager
34from zope.testbrowser.testing import Browser
35from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
36from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
37from waeup.kofa.app import University
38from waeup.kofa.payments.interfaces import IPaymentWebservice
39from waeup.kofa.students.student import Student
40from waeup.kofa.students.studylevel import StudentStudyLevel
41from waeup.kofa.university.faculty import Faculty
42from waeup.kofa.university.department import Department
43from waeup.kofa.interfaces import IUserAccount
44from waeup.kofa.authentication import LocalRoleSetEvent
45from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
46
47PH_LEN = 2059  # Length of placeholder file
48
49SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
50SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
51
52def lookup_submit_value(name, value, browser):
53    """Find a button with a certain value."""
54    for num in range(0, 100):
55        try:
56            button = browser.getControl(name=name, index=num)
57            if button.value.endswith(value):
58                return button
59        except IndexError:
60            break
61    return None
62
63class StudentsFullSetup(FunctionalTestCase):
64    # A test case that only contains a setup and teardown
65    #
66    # Complete setup for students handlings is rather complex and
67    # requires lots of things created before we can start. This is a
68    # setup that does all this, creates a university, creates PINs,
69    # etc.  so that we do not have to bother with that in different
70    # test cases.
71
72    layer = FunctionalLayer
73
74    def setUp(self):
75        super(StudentsFullSetup, self).setUp()
76
77        # Setup a sample site for each test
78        app = University()
79        self.dc_root = tempfile.mkdtemp()
80        app['datacenter'].setStoragePath(self.dc_root)
81
82        # Prepopulate the ZODB...
83        self.getRootFolder()['app'] = app
84        # we add the site immediately after creation to the
85        # ZODB. Catalogs and other local utilities are not setup
86        # before that step.
87        self.app = self.getRootFolder()['app']
88        # Set site here. Some of the following setup code might need
89        # to access grok.getSite() and should get our new app then
90        setSite(app)
91
92        # Add student with subobjects
93        student = createObject('waeup.Student')
94        student.firstname = u'Anna'
95        student.lastname = u'Tester'
96        student.reg_number = u'123'
97        student.matric_number = u'234'
98        student.sex = u'm'
99        student.email = 'aa@aa.ng'
100        student.phone = u'1234'
101        self.app['students'].addStudent(student)
102        self.student_id = student.student_id
103        self.student = self.app['students'][self.student_id]
104
105        # Set password
106        IUserAccount(
107            self.app['students'][self.student_id]).setPassword('spwd')
108
109        self.login_path = 'http://localhost/app/login'
110        self.container_path = 'http://localhost/app/students'
111        self.manage_container_path = self.container_path + '/@@manage'
112        self.add_student_path = self.container_path + '/addstudent'
113        self.student_path = self.container_path + '/' + self.student_id
114        self.manage_student_path = self.student_path + '/manage_base'
115        self.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        # Managers can add and delete second file
610        self.browser.open(self.manage_clearance_path)
611        image = open(SAMPLE_IMAGE, 'rb')
612        ctrl = self.browser.getControl(name='birthcertificateupload')
613        file_ctrl = ctrl.mech_control
614        file_ctrl.add_file(image, filename='my_acceptance_letter.jpg')
615        self.browser.getControl(
616            name='upload_acceptanceletterupload').click()
617        self.assertFalse(
618            '<a target="image" href="acc_let">'
619            in self.browser.contents)
620        ctrl = self.browser.getControl(name='acceptanceletterupload')
621        file_ctrl = ctrl.mech_control
622        image.seek(0)
623        file_ctrl.add_file(image, filename='my_acceptance_letter.jpg')
624        self.browser.getControl(
625            name='upload_acceptanceletterupload').click()
626        self.assertTrue(
627            '<a target="image" href="acc_let">'
628            in self.browser.contents)
629        self.browser.getControl(
630            name='delete_acceptanceletterupload').click()
631        self.assertTrue(
632            'acc_let deleted'
633            in self.browser.contents)
634        # Managers can upload a file via the StudentBaseManageFormPage
635        self.browser.open(self.manage_student_path)
636        image = open(SAMPLE_IMAGE_BMP, 'rb')
637        ctrl = self.browser.getControl(name='passportuploadmanage')
638        file_ctrl = ctrl.mech_control
639        file_ctrl.add_file(image, filename='my_photo.bmp')
640        self.browser.getControl(
641            name='upload_passportuploadmanage').click()
642        self.assertTrue('jpg file extension expected'
643            in self.browser.contents)
644        ctrl = self.browser.getControl(name='passportuploadmanage')
645        file_ctrl = ctrl.mech_control
646        image = open(SAMPLE_IMAGE, 'rb')
647        file_ctrl.add_file(image, filename='my_photo.jpg')
648        self.browser.getControl(
649            name='upload_passportuploadmanage').click()
650        self.assertTrue(
651            '<img align="middle" height="125px" src="passport.jpg" />'
652            in self.browser.contents)
653        # We remove the passport file again
654        self.browser.open(self.manage_student_path)
655        self.browser.getControl('Delete').click()
656        self.browser.open(self.student_path + '/clearance.pdf')
657        self.assertEqual(self.browser.headers['Status'], '200 Ok')
658        self.assertEqual(self.browser.headers['Content-Type'],
659                         'application/pdf')
660
661    def test_manage_course_lists(self):
662        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
663        self.browser.open(self.student_path)
664        self.browser.getLink("Study Course").click()
665        self.assertEqual(self.browser.headers['Status'], '200 Ok')
666        self.assertEqual(self.browser.url, self.studycourse_path)
667        self.assertTrue('Undergraduate Full-Time' in self.browser.contents)
668        self.browser.getLink("Manage").click()
669        self.assertTrue('Manage study course' in self.browser.contents)
670        # Before we can select a level, the certificate must
671        # be selected and saved
672        self.browser.getControl(name="form.certificate").value = ['CERT1']
673        self.browser.getControl(name="form.current_session").value = ['2004']
674        self.browser.getControl(name="form.current_verdict").value = ['A']
675        self.browser.getControl(name="form.entry_mode").value = ['ug_ft']
676        self.browser.getControl("Save").click()
677        # Now we can save also the current level which depends on start and end
678        # level of the certificate
679        self.browser.getControl(name="form.current_level").value = ['100']
680        self.browser.getControl("Save").click()
681        # Managers can add and remove any study level (course list)
682        self.browser.getControl(name="addlevel").value = ['100']
683        self.browser.getControl("Add study level").click()
684        self.assertMatches('...<span>100</span>...', self.browser.contents)
685        self.browser.getControl("Add study level").click()
686        self.assertMatches('...This level exists...', self.browser.contents)
687        self.browser.getControl("Remove selected").click()
688        self.assertMatches(
689            '...No study level selected...', self.browser.contents)
690        self.browser.getControl(name="val_id").value = ['100']
691        self.browser.getControl("Remove selected").click()
692        self.assertMatches('...Successfully removed...', self.browser.contents)
693        # Add level again
694        self.browser.getControl(name="addlevel").value = ['100']
695        self.browser.getControl("Add study level").click()
696
697        # Managers can view and manage course lists
698        self.browser.getLink("100").click()
699        self.assertMatches(
700            '...: Study Level 100 (Year 1)...', self.browser.contents)
701        self.browser.getLink("Manage").click()
702        self.browser.getControl(name="form.level_session").value = ['2002']
703        self.browser.getControl("Save").click()
704        self.browser.getControl("Remove selected").click()
705        self.assertMatches('...No ticket selected...', self.browser.contents)
706        ctrl = self.browser.getControl(name='val_id')
707        ctrl.getControl(value='COURSE1').selected = True
708        self.browser.getControl("Remove selected", index=0).click()
709        self.assertTrue('Successfully removed' in self.browser.contents)
710        self.browser.getControl("Add course ticket").click()
711        self.browser.getControl(name="form.course").value = ['COURSE1']
712        self.browser.getControl("Add course ticket").click()
713        self.assertTrue('Successfully added' in self.browser.contents)
714        self.browser.getControl("Add course ticket").click()
715        self.browser.getControl(name="form.course").value = ['COURSE1']
716        self.browser.getControl("Add course ticket").click()
717        self.assertTrue('The ticket exists' in self.browser.contents)
718        self.browser.getControl("Cancel").click()
719        self.browser.getLink("COURSE1").click()
720        self.browser.getLink("Manage").click()
721        self.browser.getControl(name="form.score").value = '10'
722        self.browser.getControl("Save").click()
723        self.assertTrue('Form has been saved' in self.browser.contents)
724        # Carry-over courses will be collected when next level is created
725        self.browser.open(self.student_path + '/studycourse/manage')
726        # Add next level
727        self.browser.getControl(name="addlevel").value = ['200']
728        self.browser.getControl("Add study level").click()
729        self.browser.getLink("200").click()
730        self.assertMatches(
731            '...: Study Level 200 (Year 2)...', self.browser.contents)
732        # COURSE1 has score 0 and thus will become a carry-over course
733        # in level 200
734        self.assertEqual(
735            sorted(self.student['studycourse']['200'].keys()), [u'COURSE1'])
736        self.assertTrue(
737            self.student['studycourse']['200']['COURSE1'].carry_over)
738        return
739
740    def test_manage_workflow(self):
741        # Managers can pass through the whole workflow
742        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
743        student = self.app['students'][self.student_id]
744        self.browser.open(self.manage_student_path)
745        self.assertTrue(student.clearance_locked)
746        self.browser.getControl(name="transition").value = ['admit']
747        self.browser.getControl("Save").click()
748        self.assertTrue(student.clearance_locked)
749        self.browser.getControl(name="transition").value = ['start_clearance']
750        self.browser.getControl("Save").click()
751        self.assertFalse(student.clearance_locked)
752        self.browser.getControl(name="transition").value = ['request_clearance']
753        self.browser.getControl("Save").click()
754        self.assertTrue(student.clearance_locked)
755        self.browser.getControl(name="transition").value = ['clear']
756        self.browser.getControl("Save").click()
757        # Managers approve payment, they do not pay
758        self.assertFalse('pay_first_school_fee' in self.browser.contents)
759        self.browser.getControl(
760            name="transition").value = ['approve_first_school_fee']
761        self.browser.getControl("Save").click()
762        self.browser.getControl(name="transition").value = ['reset6']
763        self.browser.getControl("Save").click()
764        # In state returning the pay_school_fee transition triggers some
765        # changes of attributes
766        self.browser.getControl(name="transition").value = ['approve_school_fee']
767        self.browser.getControl("Save").click()
768        self.assertEqual(student['studycourse'].current_session, 2005) # +1
769        self.assertEqual(student['studycourse'].current_level, 200) # +100
770        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = Zero = not set
771        self.assertEqual(student['studycourse'].previous_verdict, 'A')
772        self.browser.getControl(name="transition").value = ['register_courses']
773        self.browser.getControl("Save").click()
774        self.browser.getControl(name="transition").value = ['validate_courses']
775        self.browser.getControl("Save").click()
776        self.browser.getControl(name="transition").value = ['return']
777        self.browser.getControl("Save").click()
778        return
779
780    def test_manage_pg_workflow(self):
781        # Managers can pass through the whole workflow
782        IWorkflowState(self.student).setState('school fee paid')
783        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
784        student = self.app['students'][self.student_id]
785        self.browser.open(self.manage_student_path)
786        self.assertTrue('<option value="reset6">' in self.browser.contents)
787        self.assertTrue('<option value="register_courses">' in self.browser.contents)
788        self.assertTrue('<option value="reset5">' in self.browser.contents)
789        self.certificate.study_mode = 'pg_ft'
790        self.browser.open(self.manage_student_path)
791        self.assertFalse('<option value="reset6">' in self.browser.contents)
792        self.assertFalse('<option value="register_courses">' in self.browser.contents)
793        self.assertTrue('<option value="reset5">' in self.browser.contents)
794        return
795
796    def test_manage_import(self):
797        # Managers can import student data files
798        datacenter_path = 'http://localhost/app/datacenter'
799        # Prepare a csv file for students
800        open('students.csv', 'wb').write(
801"""firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,password
802Aaren,Pieri,1,1990-01-02,100000,aa@aa.ng,1234,m,mypwd1
803Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,m,mypwd1
804Brit,Berson,3,1990-01-04,100001,aa@aa.ng,1234,m,mypwd1
805""")
806        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
807        self.browser.open(datacenter_path)
808        self.browser.getLink('Upload data').click()
809        filecontents = StringIO(open('students.csv', 'rb').read())
810        filewidget = self.browser.getControl(name='uploadfile:file')
811        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
812        self.browser.getControl(name='SUBMIT').click()
813        self.browser.getLink('Process data').click()
814        button = lookup_submit_value(
815            'select', 'students_zope.mgr.csv', self.browser)
816        button.click()
817        importerselect = self.browser.getControl(name='importer')
818        modeselect = self.browser.getControl(name='mode')
819        importerselect.getControl('Student Processor').selected = True
820        modeselect.getControl(value='create').selected = True
821        self.browser.getControl('Proceed to step 3').click()
822        self.assertTrue('Header fields OK' in self.browser.contents)
823        self.browser.getControl('Perform import').click()
824        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
825        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
826        self.assertTrue('Batch processing finished' in self.browser.contents)
827        open('studycourses.csv', 'wb').write(
828"""reg_number,matric_number,certificate,current_session,current_level
8291,,CERT1,2008,100
830,100001,CERT1,2008,100
831,100002,CERT1,2008,100
832""")
833        self.browser.open(datacenter_path)
834        self.browser.getLink('Upload data').click()
835        filecontents = StringIO(open('studycourses.csv', 'rb').read())
836        filewidget = self.browser.getControl(name='uploadfile:file')
837        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
838        self.browser.getControl(name='SUBMIT').click()
839        self.browser.getLink('Process data').click()
840        button = lookup_submit_value(
841            'select', 'studycourses_zope.mgr.csv', self.browser)
842        button.click()
843        importerselect = self.browser.getControl(name='importer')
844        modeselect = self.browser.getControl(name='mode')
845        importerselect.getControl(
846            'StudentStudyCourse Processor (update only)').selected = True
847        modeselect.getControl(value='create').selected = True
848        self.browser.getControl('Proceed to step 3').click()
849        self.assertTrue('Update mode only' in self.browser.contents)
850        self.browser.getControl('Proceed to step 3').click()
851        self.assertTrue('Header fields OK' in self.browser.contents)
852        self.browser.getControl('Perform import').click()
853        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
854        self.assertTrue('Successfully processed 2 rows'
855                        in self.browser.contents)
856        # The students are properly indexed and we can
857        # thus find a student in  the department
858        self.browser.open(self.manage_container_path)
859        self.browser.getControl(name="searchtype").value = ['depcode']
860        self.browser.getControl(name="searchterm").value = 'dep1'
861        self.browser.getControl("Search").click()
862        self.assertTrue('Aaren Pieri' in self.browser.contents)
863        # We can search for a new student by name ...
864        self.browser.getControl(name="searchtype").value = ['fullname']
865        self.browser.getControl(name="searchterm").value = 'Claus'
866        self.browser.getControl("Search").click()
867        self.assertTrue('Claus Finau' in self.browser.contents)
868        # ... and check if the imported password has been properly set
869        ctrl = self.browser.getControl(name='entries')
870        value = ctrl.options[0]
871        claus = self.app['students'][value]
872        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
873        return
874
875    def test_handle_clearance_by_co(self):
876        # Create clearance officer
877        self.app['users'].addUser('mrclear', 'mrclearsecret')
878        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
879        self.app['users']['mrclear'].title = 'Carlo Pitter'
880        # Clearance officers need not necessarily to get
881        # the StudentsOfficer site role
882        #prmglobal = IPrincipalRoleManager(self.app)
883        #prmglobal.assignRoleToPrincipal('waeup.StudentsOfficer', 'mrclear')
884        # Assign local ClearanceOfficer role
885        department = self.app['faculties']['fac1']['dep1']
886        prmlocal = IPrincipalRoleManager(department)
887        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
888        IWorkflowState(self.student).setState('clearance started')
889        # Login as clearance officer
890        self.browser.open(self.login_path)
891        self.browser.getControl(name="form.login").value = 'mrclear'
892        self.browser.getControl(name="form.password").value = 'mrclearsecret'
893        self.browser.getControl("Login").click()
894        self.assertMatches('...You logged in...', self.browser.contents)
895        # CO can see his roles
896        self.browser.getLink("My Roles").click()
897        self.assertMatches(
898            '...<div>Academics Officer (view only)</div>...',
899            self.browser.contents)
900        #self.assertMatches(
901        #    '...<div>Students Officer (view only)</div>...',
902        #    self.browser.contents)
903        # But not his local role ...
904        self.assertFalse('Clearance Officer' in self.browser.contents)
905        # ... because we forgot to notify the department that the local role
906        # has changed
907        notify(LocalRoleSetEvent(
908            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
909        self.browser.open('http://localhost/app/users/mrclear/my_roles')
910        self.assertTrue('Clearance Officer' in self.browser.contents)
911        self.assertMatches(
912            '...<a href="http://localhost/app/faculties/fac1/dep1">...',
913            self.browser.contents)
914        # CO can view the student ...
915        self.browser.open(self.clearance_path)
916        self.assertEqual(self.browser.headers['Status'], '200 Ok')
917        self.assertEqual(self.browser.url, self.clearance_path)
918        # ... but not other students
919        other_student = Student()
920        other_student.firstname = u'Dep2'
921        other_student.lastname = u'Student'
922        self.app['students'].addStudent(other_student)
923        other_student_path = (
924            'http://localhost/app/students/%s' % other_student.student_id)
925        self.assertRaises(
926            Unauthorized, self.browser.open, other_student_path)
927        # Only in state clearance requested the CO does see the 'Clear' button
928        self.browser.open(self.clearance_path)
929        self.assertFalse('Clear student' in self.browser.contents)
930        IWorkflowInfo(self.student).fireTransition('request_clearance')
931        self.browser.open(self.clearance_path)
932        self.assertTrue('Clear student' in self.browser.contents)
933        self.browser.getLink("Clear student").click()
934        self.assertTrue('Student has been cleared' in self.browser.contents)
935        self.assertTrue('cleared' in self.browser.contents)
936        self.browser.open(self.history_path)
937        self.assertTrue('Cleared by Carlo Pitter' in self.browser.contents)
938        # Hide real name.
939        self.app['users']['mrclear'].public_name = 'My Public Name'
940        self.browser.open(self.clearance_path)
941        self.browser.getLink("Reject clearance").click()
942        self.assertTrue('Clearance has been annulled' in self.browser.contents)
943        urlmessage = 'Clearance+has+been+annulled.'
944        # CO does now see the contact form
945        self.assertEqual(self.browser.url, self.student_path +
946            '/contactstudent?subject=%s' % urlmessage)
947        self.assertTrue('clearance started' in self.browser.contents)
948        self.browser.open(self.history_path)
949        self.assertTrue("Reset to 'clearance started' by My Public Name" in
950            self.browser.contents)
951        IWorkflowInfo(self.student).fireTransition('request_clearance')
952        self.browser.open(self.clearance_path)
953        self.browser.getLink("Reject clearance").click()
954        self.assertTrue('Clearance request has been rejected'
955            in self.browser.contents)
956        self.assertTrue('clearance started' in self.browser.contents)
957        # CO does now also see the contact form and can send a message
958        self.browser.getControl(name="form.subject").value = 'Important subject'
959        self.browser.getControl(name="form.body").value = 'Clearance rejected'
960        self.browser.getControl("Send message now").click()
961        self.assertTrue('Your message has been sent' in self.browser.contents)
962        # The CO can't clear students if not in state
963        # clearance requested
964        self.browser.open(self.student_path + '/clear')
965        self.assertTrue('Student is in wrong state'
966            in self.browser.contents)
967        # The CO can go to his department throug the my_roles page
968        self.browser.open('http://localhost/app/users/mrclear/my_roles')
969        self.browser.getLink("http://localhost/app/faculties/fac1/dep1").click()
970        # and view the list of students
971        self.browser.getLink("Show students").click()
972        self.assertTrue(self.student_id in self.browser.contents)
973
974    def test_handle_courses_by_ca(self):
975        # Create course adviser
976        self.app['users'].addUser('mrsadvise', 'mrsadvisesecret')
977        self.app['users']['mrsadvise'].email = 'mradvise@foo.ng'
978        self.app['users']['mrsadvise'].title = u'Helen Procter'
979        # Assign local CourseAdviser100 role for a certificate
980        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
981        prmlocal = IPrincipalRoleManager(cert)
982        prmlocal.assignRoleToPrincipal('waeup.local.CourseAdviser100', 'mrsadvise')
983        IWorkflowState(self.student).setState('school fee paid')
984        # Login as course adviser
985        self.browser.open(self.login_path)
986        self.browser.getControl(name="form.login").value = 'mrsadvise'
987        self.browser.getControl(name="form.password").value = 'mrsadvisesecret'
988        self.browser.getControl("Login").click()
989        self.assertMatches('...You logged in...', self.browser.contents)
990        # CO can see his roles
991        self.browser.getLink("My Roles").click()
992        self.assertMatches(
993            '...<div>Academics Officer (view only)</div>...',
994            self.browser.contents)
995        # But not his local role ...
996        self.assertFalse('Course Adviser' in self.browser.contents)
997        # ... because we forgot to notify the certificate that the local role
998        # has changed
999        notify(LocalRoleSetEvent(
1000            cert, 'waeup.local.CourseAdviser100', 'mrsadvise', granted=True))
1001        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1002        self.assertTrue('Course Adviser 100L' in self.browser.contents)
1003        self.assertMatches(
1004            '...<a href="http://localhost/app/faculties/fac1/dep1/certificates/CERT1">...',
1005            self.browser.contents)
1006        # CA can view the student ...
1007        self.browser.open(self.student_path)
1008        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1009        self.assertEqual(self.browser.url, self.student_path)
1010        # ... but not other students
1011        other_student = Student()
1012        other_student.firstname = u'Dep2'
1013        other_student.lastname = u'Student'
1014        self.app['students'].addStudent(other_student)
1015        other_student_path = (
1016            'http://localhost/app/students/%s' % other_student.student_id)
1017        self.assertRaises(
1018            Unauthorized, self.browser.open, other_student_path)
1019        # We add study level 110 to the student's studycourse
1020        studylevel = StudentStudyLevel()
1021        studylevel.level = 110
1022        self.student['studycourse'].addStudentStudyLevel(
1023            cert,studylevel)
1024        L110_student_path = self.studycourse_path + '/110'
1025        # Only in state courses registered and only if the current level
1026        # corresponds with the name of the study level object
1027        # the 100L CA does see the 'Validate' button
1028        self.browser.open(L110_student_path)
1029        self.assertFalse('Validate courses' in self.browser.contents)
1030        IWorkflowInfo(self.student).fireTransition('register_courses')
1031        self.browser.open(L110_student_path)
1032        self.assertFalse('Validate courses' in self.browser.contents)
1033        self.student['studycourse'].current_level = 110
1034        self.browser.open(L110_student_path)
1035        self.assertTrue('Validate courses' in self.browser.contents)
1036        # ... but a 100L CA does not see the button on other levels
1037        studylevel2 = StudentStudyLevel()
1038        studylevel2.level = 200
1039        self.student['studycourse'].addStudentStudyLevel(
1040            cert,studylevel2)
1041        L200_student_path = self.studycourse_path + '/200'
1042        self.browser.open(L200_student_path)
1043        self.assertFalse('Validate courses' in self.browser.contents)
1044        self.browser.open(L110_student_path)
1045        self.browser.getLink("Validate courses").click()
1046        self.assertTrue('Course list has been validated' in self.browser.contents)
1047        self.assertTrue('courses validated' in self.browser.contents)
1048        self.assertEqual(self.student['studycourse']['110'].validated_by,
1049            'Helen Procter')
1050        self.assertMatches(
1051            '<YYYY-MM-DD hh:mm:ss>',
1052            self.student['studycourse']['110'].validation_date.strftime(
1053                "%Y-%m-%d %H:%M:%S"))
1054        self.browser.getLink("Reject courses").click()
1055        self.assertTrue('Course list request has been annulled.'
1056            in self.browser.contents)
1057        urlmessage = 'Course+list+request+has+been+annulled.'
1058        self.assertEqual(self.browser.url, self.student_path +
1059            '/contactstudent?subject=%s' % urlmessage)
1060        self.assertTrue('school fee paid' in self.browser.contents)
1061        self.assertTrue(self.student['studycourse']['110'].validated_by is None)
1062        self.assertTrue(self.student['studycourse']['110'].validation_date is None)
1063        IWorkflowInfo(self.student).fireTransition('register_courses')
1064        self.browser.open(L110_student_path)
1065        self.browser.getLink("Reject courses").click()
1066        self.assertTrue('Course list request has been rejected'
1067            in self.browser.contents)
1068        self.assertTrue('school fee paid' in self.browser.contents)
1069        # CA does now see the contact form and can send a message
1070        self.browser.getControl(name="form.subject").value = 'Important subject'
1071        self.browser.getControl(name="form.body").value = 'Course list rejected'
1072        self.browser.getControl("Send message now").click()
1073        self.assertTrue('Your message has been sent' in self.browser.contents)
1074        # The CA can't validate courses if not in state
1075        # courses registered
1076        self.browser.open(L110_student_path + '/validate_courses')
1077        self.assertTrue('Student is in the wrong state'
1078            in self.browser.contents)
1079        # The CA can go to his certificate through the my_roles page
1080        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1081        self.browser.getLink(
1082            "http://localhost/app/faculties/fac1/dep1/certificates/CERT1").click()
1083        # and view the list of students
1084        self.browser.getLink("Show students").click()
1085        self.assertTrue(self.student_id in self.browser.contents)
1086
1087    def test_student_change_password(self):
1088        # Students can change the password
1089        self.browser.open(self.login_path)
1090        self.browser.getControl(name="form.login").value = self.student_id
1091        self.browser.getControl(name="form.password").value = 'spwd'
1092        self.browser.getControl("Login").click()
1093        self.assertEqual(self.browser.url, self.student_path)
1094        self.assertTrue('You logged in' in self.browser.contents)
1095        # Change password
1096        self.browser.getLink("Change password").click()
1097        self.browser.getControl(name="change_password").value = 'pw'
1098        self.browser.getControl(
1099            name="change_password_repeat").value = 'pw'
1100        self.browser.getControl("Save").click()
1101        self.assertTrue('Password must have at least' in self.browser.contents)
1102        self.browser.getControl(name="change_password").value = 'new_password'
1103        self.browser.getControl(
1104            name="change_password_repeat").value = 'new_passssword'
1105        self.browser.getControl("Save").click()
1106        self.assertTrue('Passwords do not match' in self.browser.contents)
1107        self.browser.getControl(name="change_password").value = 'new_password'
1108        self.browser.getControl(
1109            name="change_password_repeat").value = 'new_password'
1110        self.browser.getControl("Save").click()
1111        self.assertTrue('Password changed' in self.browser.contents)
1112        # We are still logged in. Changing the password hasn't thrown us out.
1113        self.browser.getLink("Base Data").click()
1114        self.assertEqual(self.browser.url, self.student_path)
1115        # We can logout
1116        self.browser.getLink("Logout").click()
1117        self.assertTrue('You have been logged out' in self.browser.contents)
1118        self.assertEqual(self.browser.url, 'http://localhost/app')
1119        # We can login again with the new password
1120        self.browser.getLink("Login").click()
1121        self.browser.open(self.login_path)
1122        self.browser.getControl(name="form.login").value = self.student_id
1123        self.browser.getControl(name="form.password").value = 'new_password'
1124        self.browser.getControl("Login").click()
1125        self.assertEqual(self.browser.url, self.student_path)
1126        self.assertTrue('You logged in' in self.browser.contents)
1127        return
1128
1129    def test_setpassword(self):
1130        # Set password for first-time access
1131        student = Student()
1132        student.reg_number = u'123456'
1133        student.firstname = u'Klaus'
1134        student.lastname = u'Tester'
1135        self.app['students'].addStudent(student)
1136        setpassword_path = 'http://localhost/app/setpassword'
1137        student_path = 'http://localhost/app/students/%s' % student.student_id
1138        self.browser.open(setpassword_path)
1139        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
1140        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1141        self.browser.getControl(name="reg_number").value = '223456'
1142        self.browser.getControl("Set").click()
1143        self.assertMatches('...No student found...',
1144                           self.browser.contents)
1145        self.browser.getControl(name="reg_number").value = '123456'
1146        self.browser.getControl(name="ac_number").value = '999999'
1147        self.browser.getControl("Set").click()
1148        self.assertMatches('...Access code is invalid...',
1149                           self.browser.contents)
1150        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1151        self.browser.getControl("Set").click()
1152        self.assertMatches('...Password has been set. Your Student Id is...',
1153                           self.browser.contents)
1154        self.browser.getControl("Set").click()
1155        self.assertMatches(
1156            '...Password has already been set. Your Student Id is...',
1157            self.browser.contents)
1158        existing_pwdpin = self.pwdpins[1]
1159        parts = existing_pwdpin.split('-')[1:]
1160        existing_pwdseries, existing_pwdnumber = parts
1161        self.browser.getControl(name="ac_series").value = existing_pwdseries
1162        self.browser.getControl(name="ac_number").value = existing_pwdnumber
1163        self.browser.getControl(name="reg_number").value = '123456'
1164        self.browser.getControl("Set").click()
1165        self.assertMatches(
1166            '...You are using the wrong Access Code...',
1167            self.browser.contents)
1168        # The student can login with the new credentials
1169        self.browser.open(self.login_path)
1170        self.browser.getControl(name="form.login").value = student.student_id
1171        self.browser.getControl(
1172            name="form.password").value = self.existing_pwdnumber
1173        self.browser.getControl("Login").click()
1174        self.assertEqual(self.browser.url, student_path)
1175        self.assertTrue('You logged in' in self.browser.contents)
1176        return
1177
1178    def test_student_access(self):
1179        # Students can access their own objects
1180        # and can perform actions
1181        IWorkflowInfo(self.student).fireTransition('admit')
1182        # Students can't login if their account is suspended/deactivated
1183        self.student.suspended = True
1184        self.browser.open(self.login_path)
1185        self.browser.getControl(name="form.login").value = self.student_id
1186        self.browser.getControl(name="form.password").value = 'spwd'
1187        self.browser.getControl("Login").click()
1188        self.assertTrue(
1189            'Your account has been deactivated.' in self.browser.contents)
1190        self.student.suspended = False
1191        self.browser.getControl("Login").click()
1192        self.assertTrue(
1193            'You logged in.' in self.browser.contents)
1194        # Admitted student can upload a passport picture
1195        self.browser.open(self.student_path + '/change_portrait')
1196        ctrl = self.browser.getControl(name='passportuploadedit')
1197        file_obj = open(SAMPLE_IMAGE, 'rb')
1198        file_ctrl = ctrl.mech_control
1199        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1200        self.browser.getControl(
1201            name='upload_passportuploadedit').click()
1202        self.assertTrue(
1203            '<img align="middle" height="125px" src="passport.jpg" />'
1204            in self.browser.contents)
1205        # Students can open admission letter
1206        self.browser.getLink("Base Data").click()
1207        self.browser.getLink("Download admission letter").click()
1208        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1209        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1210        # Student can view the clearance data
1211        self.browser.open(self.student_path)
1212        self.browser.getLink("Clearance Data").click()
1213        # Student can't open clearance edit form before starting clearance
1214        self.browser.open(self.student_path + '/cedit')
1215        self.assertMatches('...The requested form is locked...',
1216                           self.browser.contents)
1217        self.browser.getLink("Clearance Data").click()
1218        self.browser.getLink("Start clearance").click()
1219        self.student.email = None
1220        # Uups, we forgot to fill the email fields
1221        self.browser.getControl("Start clearance").click()
1222        self.assertMatches('...Not all required fields filled...',
1223                           self.browser.contents)
1224        self.browser.open(self.student_path + '/edit_base')
1225        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1226        self.browser.getControl("Save").click()
1227        self.browser.open(self.student_path + '/start_clearance')
1228        self.browser.getControl(name="ac_series").value = '3'
1229        self.browser.getControl(name="ac_number").value = '4444444'
1230        self.browser.getControl("Start clearance now").click()
1231        self.assertMatches('...Activation code is invalid...',
1232                           self.browser.contents)
1233        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1234        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1235        # Owner is Hans Wurst, AC can't be invalidated
1236        self.browser.getControl("Start clearance now").click()
1237        self.assertMatches('...You are not the owner of this access code...',
1238                           self.browser.contents)
1239        # Set the correct owner
1240        self.existing_clrac.owner = self.student_id
1241        # clr_code might be set (and thus returns None) due importing
1242        # an empty clr_code column.
1243        self.student.clr_code = None
1244        self.browser.getControl("Start clearance now").click()
1245        self.assertMatches('...Clearance process has been started...',
1246                           self.browser.contents)
1247        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1248        self.browser.getControl("Save", index=0).click()
1249        # Student can view the clearance data
1250        self.browser.getLink("Clearance Data").click()
1251        # and go back to the edit form
1252        self.browser.getLink("Edit").click()
1253        # Students can upload documents
1254        ctrl = self.browser.getControl(name='birthcertificateupload')
1255        file_obj = open(SAMPLE_IMAGE, 'rb')
1256        file_ctrl = ctrl.mech_control
1257        file_ctrl.add_file(file_obj, filename='my_birth_certificate.jpg')
1258        self.browser.getControl(
1259            name='upload_birthcertificateupload').click()
1260        self.assertTrue(
1261            '<a target="image" href="birth_certificate">Birth Certificate Scan</a>'
1262            in self.browser.contents)
1263        # Students can open clearance slip
1264        self.browser.getLink("View").click()
1265        self.browser.getLink("Download clearance slip").click()
1266        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1267        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1268        # Students can request clearance
1269        self.browser.open(self.edit_clearance_path)
1270        self.browser.getControl("Save and request clearance").click()
1271        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1272        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1273        self.browser.getControl("Request clearance now").click()
1274        self.assertMatches('...Clearance has been requested...',
1275                           self.browser.contents)
1276        # Student can't reopen clearance form after requesting clearance
1277        self.browser.open(self.student_path + '/cedit')
1278        self.assertMatches('...The requested form is locked...',
1279                           self.browser.contents)
1280        # Student can't add study level if not in state 'school fee paid'
1281        self.browser.open(self.student_path + '/studycourse/add')
1282        self.assertMatches('...The requested form is locked...',
1283                           self.browser.contents)
1284        # ... and must be transferred first
1285        IWorkflowInfo(self.student).fireTransition('clear')
1286        IWorkflowInfo(self.student).fireTransition('pay_first_school_fee')
1287        # Now students can add the current study level
1288        self.browser.getLink("Study Course").click()
1289        self.browser.getLink("Add course list").click()
1290        self.assertMatches('...Add current level 100 (Year 1)...',
1291                           self.browser.contents)
1292        self.browser.getControl("Create course list now").click()
1293        self.browser.getLink("100").click()
1294        self.browser.getLink("Edit course list").click()
1295        self.browser.getControl("Add course ticket").click()
1296        self.browser.getControl(name="form.course").value = ['COURSE1']
1297        self.browser.getControl("Add course ticket").click()
1298        self.assertMatches('...The ticket exists...',
1299                           self.browser.contents)
1300        self.student['studycourse'].current_level = 200
1301        self.browser.getLink("Study Course").click()
1302        self.browser.getLink("Add course list").click()
1303        self.assertMatches('...Add current level 200 (Year 2)...',
1304                           self.browser.contents)
1305        self.browser.getControl("Create course list now").click()
1306        self.browser.getLink("200").click()
1307        self.browser.getLink("Edit course list").click()
1308        self.browser.getControl("Add course ticket").click()
1309        self.browser.getControl(name="form.course").value = ['COURSE1']
1310        self.browser.getControl("Add course ticket").click()
1311        self.assertMatches('...The ticket exists...',
1312                           self.browser.contents)
1313        # Indeed the ticket exists as carry-over course from level 100
1314        # since its score was 0
1315        self.assertTrue(
1316            self.student['studycourse']['200']['COURSE1'].carry_over is True)
1317        # Students can open the pdf course registration slip
1318        self.browser.open(self.student_path + '/studycourse/200')
1319        self.browser.getLink("Download course registration slip").click()
1320        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1321        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1322        # Students can remove course tickets
1323        self.browser.open(self.student_path + '/studycourse/200/edit')
1324        self.browser.getControl("Remove selected", index=0).click()
1325        self.assertTrue('No ticket selected' in self.browser.contents)
1326        # No ticket can be selected since the carry-over course is a core course
1327        self.assertRaises(
1328            LookupError, self.browser.getControl, name='val_id')
1329        self.student['studycourse']['200']['COURSE1'].mandatory = False
1330        self.browser.open(self.student_path + '/studycourse/200/edit')
1331        # Course list can't be registered if total_credits exceeds max_credits
1332        self.student['studycourse']['200']['COURSE1'].credits = 60
1333        self.browser.getControl("Register course list").click()
1334        self.assertTrue('Maximum credits of 50 exceeded' in self.browser.contents)
1335        # Student can now remove the ticket
1336        ctrl = self.browser.getControl(name='val_id')
1337        ctrl.getControl(value='COURSE1').selected = True
1338        self.browser.getControl("Remove selected", index=0).click()
1339        self.assertTrue('Successfully removed' in self.browser.contents)
1340        # Course list can be registered, even if it's empty
1341        self.browser.getControl("Register course list").click()
1342        self.assertTrue('Course list has been registered' in self.browser.contents)
1343        self.assertEqual(self.student.state, 'courses registered')
1344        return
1345
1346    def test_student_clearance_wo_clrcode(self):
1347        IWorkflowState(self.student).setState('clearance started')
1348        self.browser.open(self.login_path)
1349        self.browser.getControl(name="form.login").value = self.student_id
1350        self.browser.getControl(name="form.password").value = 'spwd'
1351        self.browser.getControl("Login").click()
1352        self.student.clearance_locked = False
1353        self.browser.open(self.edit_clearance_path)
1354        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1355        self.browser.getControl("Save and request clearance").click()
1356        self.assertMatches('...Clearance has been requested...',
1357                           self.browser.contents)
1358
1359    def test_manage_payments(self):
1360        # Managers can add online school fee payment tickets
1361        # if certain requirements are met
1362        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1363        self.browser.open(self.payments_path)
1364        IWorkflowState(self.student).setState('cleared')
1365        self.browser.getControl("Add online payment ticket").click()
1366        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1367        self.browser.getControl("Create ticket").click()
1368        self.assertMatches('...ticket created...',
1369                           self.browser.contents)
1370        ctrl = self.browser.getControl(name='val_id')
1371        value = ctrl.options[0]
1372        self.browser.getLink(value).click()
1373        self.assertMatches('...Amount Authorized...',
1374                           self.browser.contents)
1375        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
1376        payment_url = self.browser.url
1377
1378        # The pdf payment slip can't yet be opened
1379        #self.browser.open(payment_url + '/payment_slip.pdf')
1380        #self.assertMatches('...Ticket not yet paid...',
1381        #                   self.browser.contents)
1382
1383        # The same payment (with same p_item, p_session and p_category)
1384        # can be initialized a second time if the former ticket is not yet paid.
1385        self.browser.open(self.payments_path)
1386        self.browser.getControl("Add online payment ticket").click()
1387        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1388        self.browser.getControl("Create ticket").click()
1389        self.assertMatches('...Payment ticket created...',
1390                           self.browser.contents)
1391
1392        # Managers can approve the payment
1393        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
1394        self.browser.open(payment_url)
1395        self.browser.getLink("Approve payment").click()
1396        self.assertMatches('...Payment approved...',
1397                          self.browser.contents)
1398
1399        # The authorized amount has been stored in the access code
1400        self.assertEqual(
1401            self.app['accesscodes']['SFE-0'].values()[0].cost,40000.0)
1402
1403        # Payments can't be approved twice
1404        self.browser.open(payment_url + '/approve')
1405        self.assertMatches('...This ticket has already been paid...',
1406                          self.browser.contents)
1407
1408        # Now the first ticket is paid and no more ticket of same type
1409        # (with same p_item, p_session and p_category) can be added
1410        self.browser.open(self.payments_path)
1411        self.browser.getControl("Add online payment ticket").click()
1412        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1413        self.browser.getControl("Create ticket").click()
1414        self.assertMatches(
1415            '...This type of payment has already been made...',
1416            self.browser.contents)
1417
1418        # Managers can open the pdf payment slip
1419        self.browser.open(payment_url)
1420        self.browser.getLink("Download payment slip").click()
1421        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1422        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1423
1424        # Managers can remove online school fee payment tickets
1425        self.browser.open(self.payments_path)
1426        self.browser.getControl("Remove selected").click()
1427        self.assertMatches('...No payment selected...', self.browser.contents)
1428        ctrl = self.browser.getControl(name='val_id')
1429        value = ctrl.options[0]
1430        ctrl.getControl(value=value).selected = True
1431        self.browser.getControl("Remove selected", index=0).click()
1432        self.assertTrue('Successfully removed' in self.browser.contents)
1433
1434        # Managers can add online clearance payment tickets
1435        self.browser.open(self.payments_path + '/addop')
1436        self.browser.getControl(name="form.p_category").value = ['clearance']
1437        self.browser.getControl("Create ticket").click()
1438        self.assertMatches('...ticket created...',
1439                           self.browser.contents)
1440
1441        # Managers can approve the payment
1442        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
1443        ctrl = self.browser.getControl(name='val_id')
1444        value = ctrl.options[1] # The clearance payment is the second in the table
1445        self.browser.getLink(value).click()
1446        self.browser.open(self.browser.url + '/approve')
1447        self.assertMatches('...Payment approved...',
1448                          self.browser.contents)
1449        expected = '''...
1450        <td>
1451          <span>Paid</span>
1452        </td>...'''
1453        self.assertMatches(expected,self.browser.contents)
1454        # The new CLR-0 pin has been created
1455        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
1456        pin = self.app['accesscodes']['CLR-0'].keys()[0]
1457        ac = self.app['accesscodes']['CLR-0'][pin]
1458        self.assertEqual(ac.owner, self.student_id)
1459        self.assertEqual(ac.cost, 3456.0)
1460        return
1461
1462    def test_student_payments(self):
1463        # Login
1464        self.browser.open(self.login_path)
1465        self.browser.getControl(name="form.login").value = self.student_id
1466        self.browser.getControl(name="form.password").value = 'spwd'
1467        self.browser.getControl("Login").click()
1468
1469        # Students can add online clearance payment tickets
1470        self.browser.open(self.payments_path + '/addop')
1471        self.browser.getControl(name="form.p_category").value = ['clearance']
1472        self.browser.getControl("Create ticket").click()
1473        self.assertMatches('...ticket created...',
1474                           self.browser.contents)
1475
1476        # Students can't approve the payment
1477        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
1478        ctrl = self.browser.getControl(name='val_id')
1479        value = ctrl.options[0]
1480        self.browser.getLink(value).click()
1481        payment_url = self.browser.url
1482        self.assertRaises(
1483            Unauthorized, self.browser.open, payment_url + '/approve')
1484        # In the base package they can 'use' a fake approval view
1485        self.browser.open(payment_url + '/fake_approve')
1486        self.assertMatches('...Payment approved...',
1487                          self.browser.contents)
1488        expected = '''...
1489        <td>
1490          <span>Paid</span>
1491        </td>...'''
1492        expected = '''...
1493        <td>
1494          <span>Paid</span>
1495        </td>...'''
1496        self.assertMatches(expected,self.browser.contents)
1497        payment_id = self.student['payments'].keys()[0]
1498        payment = self.student['payments'][payment_id]
1499        self.assertEqual(payment.p_state, 'paid')
1500        self.assertEqual(payment.r_amount_approved, 3456.0)
1501        self.assertEqual(payment.r_code, 'AP')
1502        self.assertEqual(payment.r_desc, u'Payment approved by Anna Tester')
1503        # The new CLR-0 pin has been created
1504        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
1505        pin = self.app['accesscodes']['CLR-0'].keys()[0]
1506        ac = self.app['accesscodes']['CLR-0'][pin]
1507        self.assertEqual(ac.owner, self.student_id)
1508        self.assertEqual(ac.cost, 3456.0)
1509
1510        # Students can open the pdf payment slip
1511        self.browser.open(payment_url + '/payment_slip.pdf')
1512        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1513        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1514
1515        # The new CLR-0 pin can be used for starting clearance
1516        # but they have to upload a passport picture first
1517        # which is only possible in state admitted
1518        self.browser.open(self.student_path + '/change_portrait')
1519        self.assertMatches('...form is locked...',
1520                          self.browser.contents)
1521        IWorkflowInfo(self.student).fireTransition('admit')
1522        self.browser.open(self.student_path + '/change_portrait')
1523        image = open(SAMPLE_IMAGE, 'rb')
1524        ctrl = self.browser.getControl(name='passportuploadedit')
1525        file_ctrl = ctrl.mech_control
1526        file_ctrl.add_file(image, filename='my_photo.jpg')
1527        self.browser.getControl(
1528            name='upload_passportuploadedit').click()
1529        self.browser.open(self.student_path + '/start_clearance')
1530        parts = pin.split('-')[1:]
1531        clrseries, clrnumber = parts
1532        self.browser.getControl(name="ac_series").value = clrseries
1533        self.browser.getControl(name="ac_number").value = clrnumber
1534        self.browser.getControl("Start clearance now").click()
1535        self.assertMatches('...Clearance process has been started...',
1536                           self.browser.contents)
1537
1538        # Students can add online school fee payment tickets.
1539        IWorkflowState(self.student).setState('returning')
1540        self.browser.open(self.payments_path)
1541        self.browser.getControl("Add online payment ticket").click()
1542        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1543        self.browser.getControl("Create ticket").click()
1544        self.assertMatches('...ticket created...',
1545                           self.browser.contents)
1546        ctrl = self.browser.getControl(name='val_id')
1547        value = ctrl.options[0]
1548        self.browser.getLink(value).click()
1549        self.assertMatches('...Amount Authorized...',
1550                           self.browser.contents)
1551        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
1552        # Payment session and will be calculated as defined
1553        # in w.k.students.utils because we set changed the state
1554        # to returning
1555        self.assertEqual(self.student['payments'][value].p_session, 2005)
1556        self.assertEqual(self.student['payments'][value].p_level, 200)
1557
1558        # Student is the payee of the payment ticket.
1559        webservice = IPaymentWebservice(self.student['payments'][value])
1560        self.assertEqual(webservice.display_fullname, 'Anna Tester')
1561        self.assertEqual(webservice.id, self.student_id)
1562        self.assertEqual(webservice.faculty, 'fac1')
1563        self.assertEqual(webservice.department, 'dep1')
1564
1565        # We simulate the approval
1566        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
1567        self.browser.open(self.browser.url + '/fake_approve')
1568        self.assertMatches('...Payment approved...',
1569                          self.browser.contents)
1570
1571        # Students can remove only online payment tickets which have
1572        # not received a valid callback
1573        self.browser.open(self.payments_path)
1574        self.assertRaises(
1575            LookupError, self.browser.getControl, name='val_id')
1576        self.browser.open(self.payments_path + '/addop')
1577        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1578        self.browser.getControl("Create ticket").click()
1579        self.browser.open(self.payments_path)
1580        ctrl = self.browser.getControl(name='val_id')
1581        value = ctrl.options[0]
1582        ctrl.getControl(value=value).selected = True
1583        self.browser.getControl("Remove selected", index=0).click()
1584        self.assertTrue('Successfully removed' in self.browser.contents)
1585
1586        # The new SFE-0 pin can be used for starting new session
1587        self.browser.open(self.studycourse_path)
1588        self.browser.getLink('Start new session').click()
1589        pin = self.app['accesscodes']['SFE-0'].keys()[0]
1590        parts = pin.split('-')[1:]
1591        sfeseries, sfenumber = parts
1592        self.browser.getControl(name="ac_series").value = sfeseries
1593        self.browser.getControl(name="ac_number").value = sfenumber
1594        self.browser.getControl("Start now").click()
1595        self.assertMatches('...Session started...',
1596                           self.browser.contents)
1597        self.assertTrue(self.student.state == 'school fee paid')
1598        return
1599
1600    def test_student_previous_payments(self):
1601        configuration = createObject('waeup.SessionConfiguration')
1602        configuration.academic_session = 2000
1603        configuration.clearance_fee = 3456.0
1604        configuration.booking_fee = 123.4
1605        self.student['studycourse'].entry_session = 2002
1606        self.app['configuration'].addSessionConfiguration(configuration)
1607        configuration2 = createObject('waeup.SessionConfiguration')
1608        configuration2.academic_session = 2003
1609        configuration2.clearance_fee = 3456.0
1610        configuration2.booking_fee = 123.4
1611        self.student['studycourse'].entry_session = 2002
1612        self.app['configuration'].addSessionConfiguration(configuration2)
1613        # Login
1614        self.browser.open(self.login_path)
1615        self.browser.getControl(name="form.login").value = self.student_id
1616        self.browser.getControl(name="form.password").value = 'spwd'
1617        self.browser.getControl("Login").click()
1618
1619        # Students can add previous school fee payment tickets in any state.
1620        IWorkflowState(self.student).setState('courses registered')
1621        self.browser.open(self.payments_path)
1622        self.browser.getControl("Add online payment ticket").click()
1623        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1624        self.browser.getControl("Create ticket").click()
1625
1626        # Amount cannot be determined since the state is not
1627        # 'cleared' or 'returning'
1628        self.assertMatches('...Amount could not be determined...',
1629                           self.browser.contents)
1630        self.assertMatches('...Would you like to pay for a previous session?...',
1631                           self.browser.contents)
1632
1633        # Previous session payment form is provided
1634        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1635        self.browser.getControl(name="form.p_session").value = ['2000']
1636        self.browser.getControl(name="form.p_level").value = ['300']
1637        self.browser.getControl("Create ticket").click()
1638        self.assertMatches('...The previous session must not fall below...',
1639                           self.browser.contents)
1640        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1641        self.browser.getControl(name="form.p_session").value = ['2004']
1642        self.browser.getControl(name="form.p_level").value = ['300']
1643        self.browser.getControl("Create ticket").click()
1644        self.assertMatches('...This is not a previous session...',
1645                           self.browser.contents)
1646        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1647        self.browser.getControl(name="form.p_session").value = ['2003']
1648        self.browser.getControl(name="form.p_level").value = ['300']
1649        self.browser.getControl("Create ticket").click()
1650        self.assertMatches('...ticket created...',
1651                           self.browser.contents)
1652        ctrl = self.browser.getControl(name='val_id')
1653        value = ctrl.options[0]
1654        self.browser.getLink(value).click()
1655        self.assertMatches('...Amount Authorized...',
1656                           self.browser.contents)
1657        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
1658
1659        # Payment session is properly set
1660        self.assertEqual(self.student['payments'][value].p_session, 2003)
1661        self.assertEqual(self.student['payments'][value].p_level, 300)
1662
1663        # We simulate the approval
1664        self.browser.open(self.browser.url + '/fake_approve')
1665        self.assertMatches('...Payment approved...',
1666                          self.browser.contents)
1667
1668        # No AC has been created
1669        self.assertEqual(len(self.app['accesscodes']['SFE-0'].keys()), 0)
1670        self.assertTrue(self.student['payments'][value].ac is None)
1671
1672        # Current payment flag is set False
1673        self.assertFalse(self.student['payments'][value].p_current)
1674        return
1675
1676    def test_student_postgraduate_payments(self):
1677        self.certificate.study_mode = 'pg_ft'
1678        self.certificate.start_level = 999
1679        self.certificate.end_level = 999
1680        self.student['studycourse'].current_level = 999
1681        # Login
1682        self.browser.open(self.login_path)
1683        self.browser.getControl(name="form.login").value = self.student_id
1684        self.browser.getControl(name="form.password").value = 'spwd'
1685        self.browser.getControl("Login").click()
1686        # Students can add online school fee payment tickets.
1687        IWorkflowState(self.student).setState('cleared')
1688        self.browser.open(self.payments_path)
1689        self.browser.getControl("Add online payment ticket").click()
1690        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1691        self.browser.getControl("Create ticket").click()
1692        self.assertMatches('...ticket created...',
1693                           self.browser.contents)
1694        ctrl = self.browser.getControl(name='val_id')
1695        value = ctrl.options[0]
1696        self.browser.getLink(value).click()
1697        self.assertMatches('...Amount Authorized...',
1698                           self.browser.contents)
1699        # Payment session and level are current ones.
1700        # Postgrads have to pay school_fee_1.
1701        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
1702        self.assertEqual(self.student['payments'][value].p_session, 2004)
1703        self.assertEqual(self.student['payments'][value].p_level, 999)
1704
1705        # We simulate the approval
1706        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
1707        self.browser.open(self.browser.url + '/fake_approve')
1708        self.assertMatches('...Payment approved...',
1709                          self.browser.contents)
1710
1711        # The new SFE-0 pin can be used for starting session
1712        self.browser.open(self.studycourse_path)
1713        self.browser.getLink('Start new session').click()
1714        pin = self.app['accesscodes']['SFE-0'].keys()[0]
1715        parts = pin.split('-')[1:]
1716        sfeseries, sfenumber = parts
1717        self.browser.getControl(name="ac_series").value = sfeseries
1718        self.browser.getControl(name="ac_number").value = sfenumber
1719        self.browser.getControl("Start now").click()
1720        self.assertMatches('...Session started...',
1721                           self.browser.contents)
1722        self.assertTrue(self.student.state == 'school fee paid')
1723
1724        # Postgrad students do not need to register courses the
1725        # can just pay for the next session.
1726        self.browser.open(self.payments_path)
1727        # Remove first payment to be sure that we access the right ticket
1728        del self.student['payments'][value]
1729        self.browser.getControl("Add online payment ticket").click()
1730        self.browser.getControl(name="form.p_category").value = ['schoolfee']
1731        self.browser.getControl("Create ticket").click()
1732        ctrl = self.browser.getControl(name='val_id')
1733        value = ctrl.options[0]
1734        self.browser.getLink(value).click()
1735        # Payment session has increased by one, payment level remains the same.
1736        # Returning Postgraduates have to pay school_fee_2.
1737        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
1738        self.assertEqual(self.student['payments'][value].p_session, 2005)
1739        self.assertEqual(self.student['payments'][value].p_level, 999)
1740
1741        # Student is still in old session
1742        self.assertEqual(self.student.current_session, 2004)
1743
1744        # We do not need to pay the ticket if any other
1745        # SFE pin is provided
1746        pin_container = self.app['accesscodes']
1747        pin_container.createBatch(
1748            datetime.utcnow(), 'some_userid', 'SFE', 9.99, 1)
1749        pin = pin_container['SFE-1'].values()[0].representation
1750        sfeseries, sfenumber = pin.split('-')[1:]
1751        # The new SFE-1 pin can be used for starting new session
1752        self.browser.open(self.studycourse_path)
1753        self.browser.getLink('Start new session').click()
1754        self.browser.getControl(name="ac_series").value = sfeseries
1755        self.browser.getControl(name="ac_number").value = sfenumber
1756        self.browser.getControl("Start now").click()
1757        self.assertMatches('...Session started...',
1758                           self.browser.contents)
1759        self.assertTrue(self.student.state == 'school fee paid')
1760        # Student is in new session
1761        self.assertEqual(self.student.current_session, 2005)
1762        self.assertEqual(self.student['studycourse'].current_level, 999)
1763        return
1764
1765    def test_manage_accommodation(self):
1766        # Managers can add online booking fee payment tickets and open the
1767        # callback view (see test_manage_payments)
1768        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1769        self.browser.open(self.payments_path)
1770        self.browser.getControl("Add online payment ticket").click()
1771        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1772        # If student is not in accommodation session, payment cannot be processed
1773        self.app['hostels'].accommodation_session = 2011
1774        self.browser.getControl("Create ticket").click()
1775        self.assertMatches('...Your current session does not match...',
1776                           self.browser.contents)
1777        self.app['hostels'].accommodation_session = 2004
1778        self.browser.getControl("Add online payment ticket").click()
1779        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1780        self.browser.getControl("Create ticket").click()
1781        ctrl = self.browser.getControl(name='val_id')
1782        value = ctrl.options[0]
1783        self.browser.getLink(value).click()
1784        self.browser.open(self.browser.url + '/approve')
1785        # The new HOS-0 pin has been created
1786        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1787        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1788        ac = self.app['accesscodes']['HOS-0'][pin]
1789        self.assertEqual(ac.owner, self.student_id)
1790        parts = pin.split('-')[1:]
1791        sfeseries, sfenumber = parts
1792        # Managers can use HOS code and book a bed space with it
1793        self.browser.open(self.acco_path)
1794        self.browser.getLink("Book accommodation").click()
1795        self.assertMatches('...You are in the wrong...',
1796                           self.browser.contents)
1797        IWorkflowInfo(self.student).fireTransition('admit')
1798        # An existing HOS code can only be used if students
1799        # are in accommodation session
1800        self.student['studycourse'].current_session = 2003
1801        self.browser.getLink("Book accommodation").click()
1802        self.assertMatches('...Your current session does not match...',
1803                           self.browser.contents)
1804        self.student['studycourse'].current_session = 2004
1805        # All requirements are met and ticket can be created
1806        self.browser.getLink("Book accommodation").click()
1807        self.assertMatches('...Activation Code:...',
1808                           self.browser.contents)
1809        self.browser.getControl(name="ac_series").value = sfeseries
1810        self.browser.getControl(name="ac_number").value = sfenumber
1811        self.browser.getControl("Create bed ticket").click()
1812        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1813                           self.browser.contents)
1814        # Bed has been allocated
1815        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
1816        self.assertTrue(bed1.owner == self.student_id)
1817        # BedTicketAddPage is now blocked
1818        self.browser.getLink("Book accommodation").click()
1819        self.assertMatches('...You already booked a bed space...',
1820            self.browser.contents)
1821        # The bed ticket displays the data correctly
1822        self.browser.open(self.acco_path + '/2004')
1823        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1824                           self.browser.contents)
1825        self.assertMatches('...2004/2005...', self.browser.contents)
1826        self.assertMatches('...regular_male_fr...', self.browser.contents)
1827        self.assertMatches('...%s...' % pin, self.browser.contents)
1828        # Managers can relocate students if the student's bed_type has changed
1829        self.browser.getLink("Relocate student").click()
1830        self.assertMatches(
1831            "...Student can't be relocated...", self.browser.contents)
1832        self.student.sex = u'f'
1833        self.browser.getLink("Relocate student").click()
1834        self.assertMatches(
1835            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1836        self.assertTrue(bed1.owner == NOT_OCCUPIED)
1837        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1838        self.assertTrue(bed2.owner == self.student_id)
1839        self.assertTrue(self.student['accommodation'][
1840            '2004'].bed_type == u'regular_female_fr')
1841        # The payment object still shows the original payment item
1842        payment_id = self.student['payments'].keys()[0]
1843        payment = self.student['payments'][payment_id]
1844        self.assertTrue(payment.p_item == u'regular_male_fr')
1845        # Managers can relocate students if the bed's bed_type has changed
1846        bed1.bed_type = u'regular_female_fr'
1847        bed2.bed_type = u'regular_male_fr'
1848        notify(grok.ObjectModifiedEvent(bed1))
1849        notify(grok.ObjectModifiedEvent(bed2))
1850        self.browser.getLink("Relocate student").click()
1851        self.assertMatches(
1852            "...Student relocated...", self.browser.contents)
1853        self.assertMatches(
1854            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
1855        self.assertMatches(bed1.owner, self.student_id)
1856        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1857        # Managers can't relocate students if bed is reserved
1858        self.student.sex = u'm'
1859        bed1.bed_type = u'regular_female_reserved'
1860        notify(grok.ObjectModifiedEvent(bed1))
1861        self.browser.getLink("Relocate student").click()
1862        self.assertMatches(
1863            "...Students in reserved beds can't be relocated...",
1864            self.browser.contents)
1865        # Managers can relocate students if booking has been cancelled but
1866        # other bed space has been manually allocated after cancellation
1867        old_owner = bed1.releaseBed()
1868        self.assertMatches(old_owner, self.student_id)
1869        bed2.owner = self.student_id
1870        self.browser.open(self.acco_path + '/2004')
1871        self.assertMatches(
1872            "...booking cancelled...", self.browser.contents)
1873        self.browser.getLink("Relocate student").click()
1874        # We didn't informed the catalog therefore the new owner is not found
1875        self.assertMatches(
1876            "...There is no free bed in your category regular_male_fr...",
1877            self.browser.contents)
1878        # Now we fire the event properly
1879        notify(grok.ObjectModifiedEvent(bed2))
1880        self.browser.getLink("Relocate student").click()
1881        self.assertMatches(
1882            "...Student relocated...", self.browser.contents)
1883        self.assertMatches(
1884            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1885          # Managers can delete bed tickets
1886        self.browser.open(self.acco_path)
1887        ctrl = self.browser.getControl(name='val_id')
1888        value = ctrl.options[0]
1889        ctrl.getControl(value=value).selected = True
1890        self.browser.getControl("Remove selected", index=0).click()
1891        self.assertMatches('...Successfully removed...', self.browser.contents)
1892        # The bed has been properly released by the event handler
1893        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1894        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1895        return
1896
1897    def test_student_accommodation(self):
1898        # Login
1899        self.browser.open(self.login_path)
1900        self.browser.getControl(name="form.login").value = self.student_id
1901        self.browser.getControl(name="form.password").value = 'spwd'
1902        self.browser.getControl("Login").click()
1903
1904        # Students can add online booking fee payment tickets and open the
1905        # callback view (see test_manage_payments)
1906        self.browser.getLink("Payments").click()
1907        self.browser.getControl("Add online payment ticket").click()
1908        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1909        self.browser.getControl("Create ticket").click()
1910        ctrl = self.browser.getControl(name='val_id')
1911        value = ctrl.options[0]
1912        self.browser.getLink(value).click()
1913        self.browser.open(self.browser.url + '/fake_approve')
1914        # The new HOS-0 pin has been created
1915        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1916        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1917        ac = self.app['accesscodes']['HOS-0'][pin]
1918        parts = pin.split('-')[1:]
1919        sfeseries, sfenumber = parts
1920
1921        # Students can use HOS code and book a bed space with it ...
1922        self.browser.open(self.acco_path)
1923        # ... but not if booking period has expired ...
1924        self.app['hostels'].enddate = datetime.now(pytz.utc)
1925        self.browser.getLink("Book accommodation").click()
1926        self.assertMatches('...Outside booking period: ...',
1927                           self.browser.contents)
1928        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
1929        # ... or student is not the an allowed state ...
1930        self.browser.getLink("Book accommodation").click()
1931        self.assertMatches('...You are in the wrong...',
1932                           self.browser.contents)
1933        IWorkflowInfo(self.student).fireTransition('admit')
1934        self.browser.getLink("Book accommodation").click()
1935        self.assertMatches('...Activation Code:...',
1936                           self.browser.contents)
1937        # Student can't used faked ACs ...
1938        self.browser.getControl(name="ac_series").value = u'nonsense'
1939        self.browser.getControl(name="ac_number").value = sfenumber
1940        self.browser.getControl("Create bed ticket").click()
1941        self.assertMatches('...Activation code is invalid...',
1942                           self.browser.contents)
1943        # ... or ACs owned by somebody else.
1944        ac.owner = u'Anybody'
1945        self.browser.getControl(name="ac_series").value = sfeseries
1946        self.browser.getControl(name="ac_number").value = sfenumber
1947        self.browser.getControl("Create bed ticket").click()
1948        self.assertMatches('...You are not the owner of this access code...',
1949                           self.browser.contents)
1950        ac.owner = self.student_id
1951        self.browser.getControl(name="ac_series").value = sfeseries
1952        self.browser.getControl(name="ac_number").value = sfenumber
1953        self.browser.getControl("Create bed ticket").click()
1954        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1955                           self.browser.contents)
1956
1957        # Bed has been allocated
1958        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
1959        self.assertTrue(bed.owner == self.student_id)
1960
1961        # BedTicketAddPage is now blocked
1962        self.browser.getLink("Book accommodation").click()
1963        self.assertMatches('...You already booked a bed space...',
1964            self.browser.contents)
1965
1966        # The bed ticket displays the data correctly
1967        self.browser.open(self.acco_path + '/2004')
1968        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
1969                           self.browser.contents)
1970        self.assertMatches('...2004/2005...', self.browser.contents)
1971        self.assertMatches('...regular_male_fr...', self.browser.contents)
1972        self.assertMatches('...%s...' % pin, self.browser.contents)
1973
1974        # Students can open the pdf slip
1975        self.browser.open(self.browser.url + '/bed_allocation.pdf')
1976        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1977        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1978
1979        # Students can't relocate themselves
1980        self.assertFalse('Relocate' in self.browser.contents)
1981        relocate_path = self.acco_path + '/2004/relocate'
1982        self.assertRaises(
1983            Unauthorized, self.browser.open, relocate_path)
1984
1985        # Students can't the Remove button and check boxes
1986        self.browser.open(self.acco_path)
1987        self.assertFalse('Remove' in self.browser.contents)
1988        self.assertFalse('val_id' in self.browser.contents)
1989        return
1990
1991    def test_change_password_request(self):
1992        self.browser.open('http://localhost/app/changepw')
1993        self.browser.getControl(name="form.identifier").value = '123'
1994        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1995        self.browser.getControl("Send login credentials").click()
1996        self.assertTrue('An email with' in self.browser.contents)
1997
1998    def test_change_current_mode(self):
1999        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
2000        self.browser.open(self.clearance_path)
2001        self.assertFalse('Employer' in self.browser.contents)
2002        self.browser.open(self.manage_clearance_path)
2003        self.assertFalse('Employer' in self.browser.contents)
2004        self.student.clearance_locked = False
2005        self.browser.open(self.edit_clearance_path)
2006        self.assertFalse('Employer' in self.browser.contents)
2007        # Now we change the study mode of the certificate and a different
2008        # interface is used by clearance views.
2009        self.certificate.study_mode = 'pg_ft'
2010        # Invariants are not being checked here?!
2011        self.certificate.end_level = 100
2012        self.browser.open(self.clearance_path)
2013        self.assertTrue('Employer' in self.browser.contents)
2014        self.browser.open(self.manage_clearance_path)
2015        self.assertTrue('Employer' in self.browser.contents)
2016        self.browser.open(self.edit_clearance_path)
2017        self.assertTrue('Employer' in self.browser.contents)
2018
2019    def test_activate_deactivate_buttons(self):
2020        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
2021        self.browser.open(self.student_path)
2022        self.browser.getLink("Deactivate").click()
2023        self.assertTrue(
2024            'Student account has been deactivated.' in self.browser.contents)
2025        self.assertTrue(
2026            'Base Data (account deactivated)' in self.browser.contents)
2027        self.assertTrue(self.student.suspended)
2028        self.browser.getLink("Activate").click()
2029        self.assertTrue(
2030            'Student account has been activated.' in self.browser.contents)
2031        self.assertFalse(
2032            'Base Data (account deactivated)' in self.browser.contents)
2033        self.assertFalse(self.student.suspended)
2034        # History messages have been added ...
2035        self.browser.getLink("History").click()
2036        self.assertTrue(
2037            'Student account deactivated by Manager<br />' in self.browser.contents)
2038        self.assertTrue(
2039            'Student account activated by Manager<br />' in self.browser.contents)
2040        # ... and actions have been logged.
2041        logfile = os.path.join(
2042            self.app['datacenter'].storage, 'logs', 'students.log')
2043        logcontent = open(logfile).read()
2044        self.assertTrue('zope.mgr - students.browser.StudentDeactivatePage - '
2045                        'K1000000 - account deactivated' in logcontent)
2046        self.assertTrue('zope.mgr - students.browser.StudentActivatePage - '
2047                        'K1000000 - account activated' in logcontent)
2048
2049    def test_student_transfer(self):
2050        # Add second certificate
2051        self.certificate2 = createObject('waeup.Certificate')
2052        self.certificate2.code = u'CERT2'
2053        self.certificate2.study_mode = 'ug_ft'
2054        self.certificate2.start_level = 999
2055        self.certificate2.end_level = 999
2056        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
2057            self.certificate2)
2058
2059        # Add study level to old study course
2060        studylevel = createObject(u'waeup.StudentStudyLevel')
2061        studylevel.level = 200
2062        self.student['studycourse'].addStudentStudyLevel(
2063            self.certificate, studylevel)
2064
2065        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
2066        self.browser.open(self.student_path)
2067        self.browser.getLink("Transfer").click()
2068        self.browser.getControl(name="form.certificate").value = ['CERT2']
2069        self.browser.getControl(name="form.current_session").value = ['2011']
2070        self.browser.getControl(name="form.current_level").value = ['200']
2071        self.browser.getControl("Transfer").click()
2072        self.assertTrue(
2073            'Current level does not match certificate levels'
2074            in self.browser.contents)
2075        self.browser.getControl(name="form.current_level").value = ['999']
2076        self.browser.getControl("Transfer").click()
2077        self.assertTrue('Successfully transferred' in self.browser.contents)
2078
2079        # Add study level to new study course
2080        studylevel = createObject(u'waeup.StudentStudyLevel')
2081        studylevel.level = 200
2082        self.student['studycourse'].addStudentStudyLevel(
2083            self.certificate, studylevel)
2084
2085        # Edit and add pages are locked for old study courses
2086        self.browser.open(self.student_path + '/studycourse/manage')
2087        self.assertFalse('The requested form is locked' in self.browser.contents)
2088        self.browser.open(self.student_path + '/studycourse_1/manage')
2089        self.assertTrue('The requested form is locked' in self.browser.contents)
2090
2091        self.browser.open(self.student_path + '/studycourse/start_session')
2092        self.assertFalse('The requested form is locked' in self.browser.contents)
2093        self.browser.open(self.student_path + '/studycourse_1/start_session')
2094        self.assertTrue('The requested form is locked' in self.browser.contents)
2095
2096        IWorkflowState(self.student).setState('school fee paid')
2097        self.browser.open(self.student_path + '/studycourse/add')
2098        self.assertFalse('The requested form is locked' in self.browser.contents)
2099        self.browser.open(self.student_path + '/studycourse_1/add')
2100        self.assertTrue('The requested form is locked' in self.browser.contents)
2101
2102        self.browser.open(self.student_path + '/studycourse/200/manage')
2103        self.assertFalse('The requested form is locked' in self.browser.contents)
2104        self.browser.open(self.student_path + '/studycourse_1/200/manage')
2105        self.assertTrue('The requested form is locked' in self.browser.contents)
2106
2107        self.browser.open(self.student_path + '/studycourse/200/validate_courses')
2108        self.assertFalse('The requested form is locked' in self.browser.contents)
2109        self.browser.open(self.student_path + '/studycourse_1/200/validate_courses')
2110        self.assertTrue('The requested form is locked' in self.browser.contents)
2111
2112        self.browser.open(self.student_path + '/studycourse/200/reject_courses')
2113        self.assertFalse('The requested form is locked' in self.browser.contents)
2114        self.browser.open(self.student_path + '/studycourse_1/200/reject_courses')
2115        self.assertTrue('The requested form is locked' in self.browser.contents)
2116
2117        self.browser.open(self.student_path + '/studycourse/200/add')
2118        self.assertFalse('The requested form is locked' in self.browser.contents)
2119        self.browser.open(self.student_path + '/studycourse_1/200/add')
2120        self.assertTrue('The requested form is locked' in self.browser.contents)
2121
2122        self.browser.open(self.student_path + '/studycourse/200/edit')
2123        self.assertFalse('The requested form is locked' in self.browser.contents)
2124        self.browser.open(self.student_path + '/studycourse_1/200/edit')
2125        self.assertTrue('The requested form is locked' in self.browser.contents)
2126
2127class StudentRequestPWTests(StudentsFullSetup):
2128    # Tests for student registration
2129
2130    layer = FunctionalLayer
2131
2132    def test_request_pw(self):
2133        # Student with wrong number can't be found.
2134        self.browser.open('http://localhost/app/requestpw')
2135        self.browser.getControl(name="form.firstname").value = 'Anna'
2136        self.browser.getControl(name="form.number").value = 'anynumber'
2137        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2138        self.browser.getControl("Send login credentials").click()
2139        self.assertTrue('No student record found.'
2140            in self.browser.contents)
2141        # Anonymous is not informed that firstname verification failed.
2142        # It seems that the record doesn't exist.
2143        self.browser.open('http://localhost/app/requestpw')
2144        self.browser.getControl(name="form.firstname").value = 'Johnny'
2145        self.browser.getControl(name="form.number").value = '123'
2146        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2147        self.browser.getControl("Send login credentials").click()
2148        self.assertTrue('No student record found.'
2149            in self.browser.contents)
2150        # Even with the correct firstname we can't register if a
2151        # password has been set and used.
2152        self.browser.getControl(name="form.firstname").value = 'Anna'
2153        self.browser.getControl(name="form.number").value = '123'
2154        self.browser.getControl("Send login credentials").click()
2155        self.assertTrue('Your password has already been set and used.'
2156            in self.browser.contents)
2157        self.browser.open('http://localhost/app/requestpw')
2158        self.app['students'][self.student_id].password = None
2159        # The firstname field, used for verification, is not case-sensitive.
2160        self.browser.getControl(name="form.firstname").value = 'aNNa'
2161        self.browser.getControl(name="form.number").value = '123'
2162        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2163        self.browser.getControl("Send login credentials").click()
2164        # Yeah, we succeded ...
2165        self.assertTrue('Your password request was successful.'
2166            in self.browser.contents)
2167        # We can also use the matric_number instead.
2168        self.browser.open('http://localhost/app/requestpw')
2169        self.browser.getControl(name="form.firstname").value = 'aNNa'
2170        self.browser.getControl(name="form.number").value = '234'
2171        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2172        self.browser.getControl("Send login credentials").click()
2173        self.assertTrue('Your password request was successful.'
2174            in self.browser.contents)
2175        # ... and  student can be found in the catalog via the email address
2176        cat = queryUtility(ICatalog, name='students_catalog')
2177        results = list(
2178            cat.searchResults(
2179            email=('new@yy.zz', 'new@yy.zz')))
2180        self.assertEqual(self.student,results[0])
2181        logfile = os.path.join(
2182            self.app['datacenter'].storage, 'logs', 'main.log')
2183        logcontent = open(logfile).read()
2184        self.assertTrue('zope.anybody - students.browser.StudentRequestPasswordPage - '
2185                        '234 (K1000000) - new@yy.zz' in logcontent)
2186        return
Note: See TracBrowser for help on using the repository browser.