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

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

Do it right and add test.

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