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

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

Enable previous session payments. Do not create activation code after successful previous session payment. Show previous session payment form only if current session ticket creation failed.

Attention: setPaymentDetails method has changed. Two additional parameters expected.

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