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

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

Postgraduate students must not register their course lists.

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