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

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

In contrast to the comment of the last revision: We need to store validation_date and validated_by also in Kofa. They have to be set by the workflow transition events. They must not be editable (but importable and exportable).

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