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

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

Export pdf admission letter.

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