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

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

Activate personal data expiration checker.

  • Property svn:keywords set to Id
File size: 134.5 KB
Line 
1## $Id: test_browser.py 9569 2012-11-07 14:09:50Z 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.trigtrans_path = self.student_path + '/trigtrans'
116        self.clearance_path = self.student_path + '/view_clearance'
117        self.personal_path = self.student_path + '/view_personal'
118        self.edit_clearance_path = self.student_path + '/cedit'
119        self.manage_clearance_path = self.student_path + '/manage_clearance'
120        self.edit_personal_path = self.student_path + '/edit_personal'
121        self.manage_personal_path = self.student_path + '/manage_personal'
122        self.studycourse_path = self.student_path + '/studycourse'
123        self.payments_path = self.student_path + '/payments'
124        self.acco_path = self.student_path + '/accommodation'
125        self.history_path = self.student_path + '/history'
126
127        # Create 5 access codes with prefix'PWD'
128        pin_container = self.app['accesscodes']
129        pin_container.createBatch(
130            datetime.utcnow(), 'some_userid', 'PWD', 9.99, 5)
131        pins = pin_container['PWD-1'].values()
132        self.pwdpins = [x.representation for x in pins]
133        self.existing_pwdpin = self.pwdpins[0]
134        parts = self.existing_pwdpin.split('-')[1:]
135        self.existing_pwdseries, self.existing_pwdnumber = parts
136        # Create 5 access codes with prefix 'CLR'
137        pin_container.createBatch(
138            datetime.now(), 'some_userid', 'CLR', 9.99, 5)
139        pins = pin_container['CLR-1'].values()
140        pins[0].owner = u'Hans Wurst'
141        self.existing_clrac = pins[0]
142        self.existing_clrpin = pins[0].representation
143        parts = self.existing_clrpin.split('-')[1:]
144        self.existing_clrseries, self.existing_clrnumber = parts
145        # Create 2 access codes with prefix 'HOS'
146        pin_container.createBatch(
147            datetime.now(), 'some_userid', 'HOS', 9.99, 2)
148        pins = pin_container['HOS-1'].values()
149        self.existing_hosac = pins[0]
150        self.existing_hospin = pins[0].representation
151        parts = self.existing_hospin.split('-')[1:]
152        self.existing_hosseries, self.existing_hosnumber = parts
153
154        # Populate university
155        self.certificate = createObject('waeup.Certificate')
156        self.certificate.code = u'CERT1'
157        self.certificate.application_category = 'basic'
158        self.certificate.study_mode = 'ug_ft'
159        self.certificate.start_level = 100
160        self.certificate.end_level = 500
161        self.certificate.school_fee_1 = 40000.0
162        self.certificate.school_fee_2 = 20000.0
163        self.app['faculties']['fac1'] = Faculty(code=u'fac1')
164        self.app['faculties']['fac1']['dep1'] = Department(code=u'dep1')
165        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
166            self.certificate)
167        self.course = createObject('waeup.Course')
168        self.course.code = 'COURSE1'
169        self.course.semester = 1
170        self.course.credits = 10
171        self.course.passmark = 40
172        self.app['faculties']['fac1']['dep1'].courses.addCourse(
173            self.course)
174        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
175            self.course, level=100)
176
177        # Configure university and hostels
178        self.app['hostels'].accommodation_states = ['admitted']
179        self.app['hostels'].accommodation_session = 2004
180        delta = timedelta(days=10)
181        self.app['hostels'].startdate = datetime.now(pytz.utc) - delta
182        self.app['hostels'].enddate = datetime.now(pytz.utc) + delta
183        self.app['configuration'].carry_over = True
184        configuration = createObject('waeup.SessionConfiguration')
185        configuration.academic_session = 2004
186        configuration.clearance_fee = 3456.0
187        configuration.booking_fee = 123.4
188        configuration.maint_fee = 987.0
189        self.app['configuration'].addSessionConfiguration(configuration)
190
191        # Create a hostel with two beds
192        hostel = Hostel()
193        hostel.hostel_id = u'hall-1'
194        hostel.hostel_name = u'Hall 1'
195        self.app['hostels'].addHostel(hostel)
196        bed = Bed()
197        bed.bed_id = u'hall-1_A_101_A'
198        bed.bed_number = 1
199        bed.owner = NOT_OCCUPIED
200        bed.bed_type = u'regular_male_fr'
201        self.app['hostels'][hostel.hostel_id].addBed(bed)
202        bed = Bed()
203        bed.bed_id = u'hall-1_A_101_B'
204        bed.bed_number = 2
205        bed.owner = NOT_OCCUPIED
206        bed.bed_type = u'regular_female_fr'
207        self.app['hostels'][hostel.hostel_id].addBed(bed)
208
209        # Set study course attributes of test student
210        self.student['studycourse'].certificate = self.certificate
211        self.student['studycourse'].current_session = 2004
212        self.student['studycourse'].entry_session = 2004
213        self.student['studycourse'].current_verdict = 'A'
214        self.student['studycourse'].current_level = 100
215        # Update the catalog
216        notify(grok.ObjectModifiedEvent(self.student))
217
218        # Put the prepopulated site into test ZODB and prepare test
219        # browser
220        self.browser = Browser()
221        self.browser.handleErrors = False
222
223    def tearDown(self):
224        super(StudentsFullSetup, self).tearDown()
225        clearSite()
226        shutil.rmtree(self.dc_root)
227
228
229
230class StudentsContainerUITests(StudentsFullSetup):
231    # Tests for StudentsContainer class views and pages
232
233    layer = FunctionalLayer
234
235    def test_anonymous_access(self):
236        # Anonymous users can't access students containers
237        self.assertRaises(
238            Unauthorized, self.browser.open, self.container_path)
239        self.assertRaises(
240            Unauthorized, self.browser.open, self.manage_container_path)
241        return
242
243    def test_manage_access(self):
244        # Managers can access the view page of students
245        # containers and can perform actions
246        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
247        self.browser.open(self.container_path)
248        self.assertEqual(self.browser.headers['Status'], '200 Ok')
249        self.assertEqual(self.browser.url, self.container_path)
250        self.browser.getLink("Manage student section").click()
251        self.assertEqual(self.browser.headers['Status'], '200 Ok')
252        self.assertEqual(self.browser.url, self.manage_container_path)
253        return
254
255    def test_add_search_delete_students(self):
256        # Managers can add search and remove students
257        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
258        self.browser.open(self.manage_container_path)
259        self.browser.getLink("Add student").click()
260        self.assertEqual(self.browser.headers['Status'], '200 Ok')
261        self.assertEqual(self.browser.url, self.add_student_path)
262        self.browser.getControl(name="form.firstname").value = 'Bob'
263        self.browser.getControl(name="form.lastname").value = 'Tester'
264        self.browser.getControl(name="form.reg_number").value = '123'
265        self.browser.getControl("Create student record").click()
266        self.assertTrue('Registration number exists already'
267            in self.browser.contents)
268        self.browser.getControl(name="form.reg_number").value = '1234'
269        self.browser.getControl("Create student record").click()
270        self.assertTrue('Student record created' in self.browser.contents)
271
272        # Registration and matric numbers must be unique
273        self.browser.getLink("Manage").click()
274        self.browser.getControl(name="form.reg_number").value = '123'
275        self.browser.getControl("Save").click()
276        self.assertMatches('...Registration number exists...',
277                           self.browser.contents)
278        self.browser.getControl(name="form.reg_number").value = '789'
279        self.browser.getControl(name="form.matric_number").value = '234'
280        self.browser.getControl("Save").click()
281        self.assertMatches('...Matriculation number exists...',
282                           self.browser.contents)
283
284        # We can find a student with a certain student_id
285        self.browser.open(self.container_path)
286        self.browser.getControl("Search").click()
287        self.assertTrue('Empty search string' in self.browser.contents)
288        self.browser.getControl(name="searchtype").value = ['student_id']
289        self.browser.getControl(name="searchterm").value = self.student_id
290        self.browser.getControl("Search").click()
291        self.assertTrue('Anna Tester' in self.browser.contents)
292
293        # We can find a student in a certain session
294        self.browser.open(self.container_path)
295        self.browser.getControl(name="searchtype").value = ['current_session']
296        self.browser.getControl(name="searchterm").value = '2004'
297        self.browser.getControl("Search").click()
298        self.assertTrue('Anna Tester' in self.browser.contents)
299        # Session fileds require integer values
300        self.browser.open(self.container_path)
301        self.browser.getControl(name="searchtype").value = ['current_session']
302        self.browser.getControl(name="searchterm").value = '2004/2005'
303        self.browser.getControl("Search").click()
304        self.assertTrue('Only year dates allowed' in self.browser.contents)
305        self.browser.open(self.manage_container_path)
306        self.browser.getControl(name="searchtype").value = ['current_session']
307        self.browser.getControl(name="searchterm").value = '2004/2005'
308        self.browser.getControl("Search").click()
309        self.assertTrue('Only year dates allowed' in self.browser.contents)
310
311        # We can find a student in a certain study_mode
312        self.browser.open(self.container_path)
313        self.browser.getControl(name="searchtype").value = ['current_mode']
314        self.browser.getControl(name="searchterm").value = 'ug_ft'
315        self.browser.getControl("Search").click()
316        self.assertTrue('Anna Tester' in self.browser.contents)
317
318        # We can find a student in a certain department
319        self.browser.open(self.container_path)
320        self.browser.getControl(name="searchtype").value = ['depcode']
321        self.browser.getControl(name="searchterm").value = 'dep1'
322        self.browser.getControl("Search").click()
323        self.assertTrue('Anna Tester' in self.browser.contents)
324
325        # We can find a student by searching for all kind of name parts
326        self.browser.open(self.manage_container_path)
327        self.browser.getControl("Search").click()
328        self.assertTrue('Empty search string' in self.browser.contents)
329        self.browser.getControl(name="searchtype").value = ['fullname']
330        self.browser.getControl(name="searchterm").value = 'Anna Tester'
331        self.browser.getControl("Search").click()
332        self.assertTrue('Anna Tester' in self.browser.contents)
333        self.browser.open(self.manage_container_path)
334        self.browser.getControl(name="searchtype").value = ['fullname']
335        self.browser.getControl(name="searchterm").value = 'Anna'
336        self.browser.getControl("Search").click()
337        self.assertTrue('Anna Tester' in self.browser.contents)
338        self.browser.open(self.manage_container_path)
339        self.browser.getControl(name="searchtype").value = ['fullname']
340        self.browser.getControl(name="searchterm").value = 'Tester'
341        self.browser.getControl("Search").click()
342        self.assertTrue('Anna Tester' in self.browser.contents)
343        self.browser.open(self.manage_container_path)
344        self.browser.getControl(name="searchtype").value = ['fullname']
345        self.browser.getControl(name="searchterm").value = 'An'
346        self.browser.getControl("Search").click()
347        self.assertFalse('Anna Tester' in self.browser.contents)
348        self.browser.open(self.manage_container_path)
349        self.browser.getControl(name="searchtype").value = ['fullname']
350        self.browser.getControl(name="searchterm").value = 'An*'
351        self.browser.getControl("Search").click()
352        self.assertTrue('Anna Tester' in self.browser.contents)
353        self.browser.open(self.manage_container_path)
354        self.browser.getControl(name="searchtype").value = ['fullname']
355        self.browser.getControl(name="searchterm").value = 'tester'
356        self.browser.getControl("Search").click()
357        self.assertTrue('Anna Tester' in self.browser.contents)
358        self.browser.open(self.manage_container_path)
359        self.browser.getControl(name="searchtype").value = ['fullname']
360        self.browser.getControl(name="searchterm").value = 'Tester Ana'
361        self.browser.getControl("Search").click()
362        self.assertFalse('Anna Tester' in self.browser.contents)
363        self.browser.open(self.manage_container_path)
364        self.browser.getControl(name="searchtype").value = ['fullname']
365        self.browser.getControl(name="searchterm").value = 'Tester Anna'
366        self.browser.getControl("Search").click()
367        self.assertTrue('Anna Tester' in self.browser.contents)
368        # The old searchterm will be used again
369        self.browser.getControl("Search").click()
370        self.assertTrue('Anna Tester' in self.browser.contents)
371
372        # The catalog is informed when studycourse objects have been
373        # edited
374        self.browser.open(self.studycourse_path + '/manage')
375        self.browser.getControl(name="form.current_session").value = ['2010']
376        self.browser.getControl(name="form.entry_session").value = ['2010']
377        self.browser.getControl(name="form.entry_mode").value = ['ug_ft']
378        self.browser.getControl("Save").click()
379
380        # We can find the student in the new session
381        self.browser.open(self.manage_container_path)
382        self.browser.getControl(name="searchtype").value = ['current_session']
383        self.browser.getControl(name="searchterm").value = '2010'
384        self.browser.getControl("Search").click()
385        self.assertTrue('Anna Tester' in self.browser.contents)
386
387        ctrl = self.browser.getControl(name='entries')
388        ctrl.getControl(value=self.student_id).selected = True
389        self.browser.getControl("Remove selected", index=0).click()
390        self.assertTrue('Successfully removed' in self.browser.contents)
391        self.browser.getControl(name="searchtype").value = ['student_id']
392        self.browser.getControl(name="searchterm").value = self.student_id
393        self.browser.getControl("Search").click()
394        self.assertTrue('No student found' in self.browser.contents)
395
396        self.browser.open(self.container_path)
397        self.browser.getControl(name="searchtype").value = ['student_id']
398        self.browser.getControl(name="searchterm").value = self.student_id
399        self.browser.getControl("Search").click()
400        self.assertTrue('No student found' in self.browser.contents)
401        return
402
403class OfficerUITests(StudentsFullSetup):
404    # Tests for Student class views and pages
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_studylevelmanagepage(self):
416        studylevel = StudentStudyLevel()
417        studylevel.level = 100
418        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
419        self.student['studycourse'].addStudentStudyLevel(
420            cert,studylevel)
421        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
422        self.browser.open(self.studycourse_path + '/100/manage')
423        self.assertEqual(self.browser.url, self.studycourse_path + '/100/manage')
424        self.assertEqual(self.browser.headers['Status'], '200 Ok')
425
426    def test_basic_auth(self):
427        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
428        self.browser.open('http://localhost/app')
429        self.browser.getLink("Logout").click()
430        self.assertTrue('You have been logged out' in self.browser.contents)
431        # But we are still logged in since we've used basic authentication here.
432        # Wikipedia says: Existing browsers retain authentication information
433        # until the tab or browser is closed or the user clears the history.
434        # HTTP does not provide a method for a server to direct clients to
435        # discard these cached credentials. This means that there is no
436        # effective way for a server to "log out" the user without closing
437        # the browser. This is a significant defect that requires browser
438        # manufacturers to support a "logout" user interface element ...
439        self.assertTrue('Manager' in self.browser.contents)
440
441    def test_manage_access(self):
442        # Managers can access the pages of students
443        # and can perform actions
444        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
445        self.browser.open(self.student_path)
446        self.assertEqual(self.browser.headers['Status'], '200 Ok')
447        self.assertEqual(self.browser.url, self.student_path)
448        self.browser.getLink("Trigger").click()
449        self.assertEqual(self.browser.headers['Status'], '200 Ok')
450        # Managers can trigger transitions
451        self.browser.getControl(name="transition").value = ['admit']
452        self.browser.getControl("Save").click()
453        # Managers can edit base
454        self.browser.open(self.student_path)
455        self.browser.getLink("Manage").click()
456        self.assertEqual(self.browser.url, self.manage_student_path)
457        self.assertEqual(self.browser.headers['Status'], '200 Ok')
458        self.browser.getControl(name="form.firstname").value = 'John'
459        self.browser.getControl(name="form.lastname").value = 'Tester'
460        self.browser.getControl(name="form.reg_number").value = '345'
461        self.browser.getControl(name="password").value = 'secret'
462        self.browser.getControl(name="control_password").value = 'secret'
463        self.browser.getControl("Save").click()
464        self.assertMatches('...Form has been saved...',
465                           self.browser.contents)
466        self.browser.open(self.student_path)
467        self.browser.getLink("Clearance Data").click()
468        self.assertEqual(self.browser.headers['Status'], '200 Ok')
469        self.assertEqual(self.browser.url, self.clearance_path)
470        self.browser.getLink("Manage").click()
471        self.assertEqual(self.browser.headers['Status'], '200 Ok')
472        self.assertEqual(self.browser.url, self.manage_clearance_path)
473        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
474        self.browser.getControl("Save").click()
475        self.assertMatches('...Form has been saved...',
476                           self.browser.contents)
477
478        self.browser.open(self.student_path)
479        self.browser.getLink("Personal Data").click()
480        self.assertEqual(self.browser.headers['Status'], '200 Ok')
481        self.assertEqual(self.browser.url, self.personal_path)
482        self.browser.getLink("Manage").click()
483        self.assertEqual(self.browser.headers['Status'], '200 Ok')
484        self.assertEqual(self.browser.url, self.manage_personal_path)
485        self.browser.open(self.personal_path)
486        self.assertTrue('Updated' in self.browser.contents)
487        self.browser.getLink("Edit").click()
488        self.assertEqual(self.browser.headers['Status'], '200 Ok')
489        self.assertEqual(self.browser.url, self.edit_personal_path)
490        self.browser.getControl("Save").click()
491        # perm_address is required in IStudentPersonalEdit
492        self.assertMatches('...Required input is missing...',
493                           self.browser.contents)
494        self.browser.getControl(name="form.perm_address").value = 'My address!'
495        self.browser.getControl("Save").click()
496        self.assertMatches('...Form has been saved...',
497                           self.browser.contents)
498
499        # Managers can browse all subobjects
500        self.browser.open(self.student_path)
501        self.browser.getLink("Payments").click()
502        self.assertEqual(self.browser.headers['Status'], '200 Ok')
503        self.assertEqual(self.browser.url, self.payments_path)
504        self.browser.open(self.student_path)
505        self.browser.getLink("Accommodation").click()
506        self.assertEqual(self.browser.headers['Status'], '200 Ok')
507        self.assertEqual(self.browser.url, self.acco_path)
508        self.browser.open(self.student_path)
509        self.browser.getLink("History").click()
510        self.assertEqual(self.browser.headers['Status'], '200 Ok')
511        self.assertEqual(self.browser.url, self.history_path)
512        self.assertMatches('...Admitted by Manager...',
513                           self.browser.contents)
514        # Only the Application Slip does not exist
515        self.assertFalse('Application Slip' in self.browser.contents)
516        return
517
518    def test_manage_contact_student(self):
519        # Managers can contact student
520        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
521        self.student.email = None
522        self.browser.open(self.student_path)
523        self.browser.getLink("Send email").click()
524        self.browser.getControl(name="form.subject").value = 'Important subject'
525        self.browser.getControl(name="form.body").value = 'Hello!'
526        self.browser.getControl("Send message now").click()
527        self.assertTrue('An smtp server error occurred' in self.browser.contents)
528        self.student.email = 'xx@yy.zz'
529        self.browser.getControl("Send message now").click()
530        self.assertTrue('Your message has been sent' in self.browser.contents)
531        return
532
533    def test_manage_remove_department(self):
534        # Lazy student is studying CERT1
535        lazystudent = Student()
536        lazystudent.firstname = u'Lazy'
537        lazystudent.lastname = u'Student'
538        self.app['students'].addStudent(lazystudent)
539        student_id = lazystudent.student_id
540        student_path = self.container_path + '/' + student_id
541        lazystudent['studycourse'].certificate = self.certificate
542        notify(grok.ObjectModifiedEvent(lazystudent))
543        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
544        self.browser.open(student_path + '/studycourse')
545        self.assertTrue('CERT1' in self.browser.contents)
546        # After some years the department is removed
547        del self.app['faculties']['fac1']['dep1']
548        # So CERT1 does no longer exist and lazy student's
549        # certificate reference is removed too
550        self.browser.open(student_path + '/studycourse')
551        self.assertEqual(self.browser.headers['Status'], '200 Ok')
552        self.assertEqual(self.browser.url, student_path + '/studycourse')
553        self.assertFalse('CERT1' in self.browser.contents)
554        self.assertMatches('...<div>--</div>...',
555                           self.browser.contents)
556
557    def test_manage_upload_file(self):
558        # Managers can upload a file via the StudentClearanceManageFormPage
559        # The image is stored even if form has errors
560        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
561        self.browser.open(self.manage_clearance_path)
562        # No birth certificate has been uploaded yet
563        # Browsing the link shows a placerholder image
564        self.browser.open('birth_certificate')
565        self.assertEqual(
566            self.browser.headers['content-type'], 'image/jpeg')
567        self.assertEqual(len(self.browser.contents), PH_LEN)
568        # Create a pseudo image file and select it to be uploaded in form
569        # as birth certificate
570        self.browser.open(self.manage_clearance_path)
571        image = open(SAMPLE_IMAGE, 'rb')
572        ctrl = self.browser.getControl(name='birthcertificateupload')
573        file_ctrl = ctrl.mech_control
574        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
575        # The Save action does not upload files
576        self.browser.getControl("Save").click() # submit form
577        self.assertFalse(
578            '<a target="image" href="birth_certificate">'
579            in self.browser.contents)
580        # ... but the correct upload submit button does
581        image = open(SAMPLE_IMAGE)
582        ctrl = self.browser.getControl(name='birthcertificateupload')
583        file_ctrl = ctrl.mech_control
584        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
585        self.browser.getControl(
586            name='upload_birthcertificateupload').click()
587        # There is a correct <img> link included
588        self.assertTrue(
589            '<a target="image" href="birth_certificate">'
590            in self.browser.contents)
591        # Browsing the link shows a real image
592        self.browser.open('birth_certificate')
593        self.assertEqual(
594            self.browser.headers['content-type'], 'image/jpeg')
595        self.assertEqual(len(self.browser.contents), 2787)
596        # Reuploading a file which is bigger than 150k will raise an error
597        self.browser.open(self.manage_clearance_path)
598        # An image > 150K
599        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
600        ctrl = self.browser.getControl(name='birthcertificateupload')
601        file_ctrl = ctrl.mech_control
602        file_ctrl.add_file(big_image, filename='my_birth_certificate.jpg')
603        self.browser.getControl(
604            name='upload_birthcertificateupload').click()
605        self.assertTrue(
606            'Uploaded file is too big' in self.browser.contents)
607        # we do not rely on filename extensions given by uploaders
608        image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
609        ctrl = self.browser.getControl(name='birthcertificateupload')
610        file_ctrl = ctrl.mech_control
611        # tell uploaded file is bmp
612        file_ctrl.add_file(image, filename='my_birth_certificate.bmp')
613        self.browser.getControl(
614            name='upload_birthcertificateupload').click()
615        self.assertTrue(
616            # jpg file was recognized
617            'File birth_certificate.jpg uploaded.' in self.browser.contents)
618        # File names must meet several conditions
619        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
620        ctrl = self.browser.getControl(name='birthcertificateupload')
621        file_ctrl = ctrl.mech_control
622        file_ctrl.add_file(bmp_image, filename='my_birth_certificate.bmp')
623        self.browser.getControl(
624            name='upload_birthcertificateupload').click()
625        self.assertTrue('Only the following extensions are allowed'
626            in self.browser.contents)
627        # Managers can delete files
628        self.browser.getControl(name='delete_birthcertificateupload').click()
629        self.assertTrue(
630            'birth_certificate deleted' in self.browser.contents)
631
632        # Managers can upload a file via the StudentBaseManageFormPage
633        self.browser.open(self.manage_student_path)
634        image = open(SAMPLE_IMAGE_BMP, 'rb')
635        ctrl = self.browser.getControl(name='passportuploadmanage')
636        file_ctrl = ctrl.mech_control
637        file_ctrl.add_file(image, filename='my_photo.bmp')
638        self.browser.getControl(
639            name='upload_passportuploadmanage').click()
640        self.assertTrue('jpg file extension expected'
641            in self.browser.contents)
642        ctrl = self.browser.getControl(name='passportuploadmanage')
643        file_ctrl = ctrl.mech_control
644        image = open(SAMPLE_IMAGE, 'rb')
645        file_ctrl.add_file(image, filename='my_photo.jpg')
646        self.browser.getControl(
647            name='upload_passportuploadmanage').click()
648        self.assertTrue(
649            '<img align="middle" height="125px" src="passport.jpg" />'
650            in self.browser.contents)
651        # We remove the passport file again
652        self.browser.open(self.manage_student_path)
653        self.browser.getControl('Delete').click()
654        self.browser.open(self.student_path + '/clearance_slip.pdf')
655        self.assertEqual(self.browser.headers['Status'], '200 Ok')
656        self.assertEqual(self.browser.headers['Content-Type'],
657                         'application/pdf')
658
659    def test_manage_course_lists(self):
660        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
661        self.browser.open(self.student_path)
662        self.browser.getLink("Study Course").click()
663        self.assertEqual(self.browser.headers['Status'], '200 Ok')
664        self.assertEqual(self.browser.url, self.studycourse_path)
665        self.assertTrue('Undergraduate Full-Time' in self.browser.contents)
666        self.browser.getLink("Manage").click()
667        self.assertTrue('Manage study course' in self.browser.contents)
668        # Before we can select a level, the certificate must
669        # be selected and saved
670        self.browser.getControl(name="form.certificate").value = ['CERT1']
671        self.browser.getControl(name="form.current_session").value = ['2004']
672        self.browser.getControl(name="form.current_verdict").value = ['A']
673        self.browser.getControl(name="form.entry_mode").value = ['ug_ft']
674        self.browser.getControl("Save").click()
675        # Now we can save also the current level which depends on start and end
676        # level of the certificate
677        self.browser.getControl(name="form.current_level").value = ['100']
678        self.browser.getControl("Save").click()
679        # Managers can add and remove any study level (course list)
680        self.browser.getControl(name="addlevel").value = ['100']
681        self.browser.getControl("Add study level").click()
682        self.assertMatches(
683            '...You must select a session...', self.browser.contents)
684        self.browser.getControl(name="addlevel").value = ['100']
685        self.browser.getControl(name="level_session").value = ['2004']
686        self.browser.getControl("Add study level").click()
687        self.assertMatches('...<span>100</span>...', self.browser.contents)
688        self.assertEqual(self.student['studycourse']['100'].level, 100)
689        self.assertEqual(self.student['studycourse']['100'].level_session, 2004)
690        self.browser.getControl(name="addlevel").value = ['100']
691        self.browser.getControl(name="level_session").value = ['2004']
692        self.browser.getControl("Add study level").click()
693        self.assertMatches('...This level exists...', self.browser.contents)
694        self.browser.getControl("Remove selected").click()
695        self.assertMatches(
696            '...No study level selected...', self.browser.contents)
697        self.browser.getControl(name="val_id").value = ['100']
698        self.browser.getControl(name="level_session").value = ['2004']
699        self.browser.getControl("Remove selected").click()
700        self.assertMatches('...Successfully removed...', self.browser.contents)
701        # Removing levels is properly logged
702        logfile = os.path.join(
703            self.app['datacenter'].storage, 'logs', 'students.log')
704        logcontent = open(logfile).read()
705        self.assertTrue('zope.mgr - students.browser.StudyCourseManageFormPage '
706                        '- K1000000 - removed: 100' in logcontent)
707        # Add level again
708        self.browser.getControl(name="addlevel").value = ['100']
709        self.browser.getControl(name="level_session").value = ['2004']
710        self.browser.getControl("Add study level").click()
711
712        # Managers can view and manage course lists
713        self.browser.getLink("100").click()
714        self.assertMatches(
715            '...: Study Level 100 (Year 1)...', self.browser.contents)
716        self.browser.getLink("Manage").click()
717        self.browser.getControl(name="form.level_session").value = ['2002']
718        self.browser.getControl("Save").click()
719        self.browser.getControl("Remove selected").click()
720        self.assertMatches('...No ticket selected...', self.browser.contents)
721        ctrl = self.browser.getControl(name='val_id')
722        ctrl.getControl(value='COURSE1').selected = True
723        self.browser.getControl("Remove selected", index=0).click()
724        self.assertTrue('Successfully removed' in self.browser.contents)
725        # Removing course tickets is properly logged
726        logfile = os.path.join(
727            self.app['datacenter'].storage, 'logs', 'students.log')
728        logcontent = open(logfile).read()
729        self.assertTrue('zope.mgr - students.browser.StudyLevelManageFormPage '
730        '- K1000000 - removed: COURSE1' in logcontent)
731        self.browser.getControl("Add course ticket").click()
732        self.browser.getControl(name="form.course").value = ['COURSE1']
733        self.course.credits = 100
734        self.browser.getControl("Add course ticket").click()
735        self.assertMatches(
736            '...Total credits exceed 58...', self.browser.contents)
737        self.course.credits = 10
738        self.browser.getControl("Add course ticket").click()
739        self.assertTrue('Successfully added' in self.browser.contents)
740        self.browser.getControl("Add course ticket").click()
741        self.browser.getControl(name="form.course").value = ['COURSE1']
742        self.browser.getControl("Add course ticket").click()
743        self.assertTrue('The ticket exists' in self.browser.contents)
744        self.browser.getControl("Cancel").click()
745        self.browser.getLink("COURSE1").click()
746        self.browser.getLink("Manage").click()
747        self.browser.getControl(name="form.score").value = '10'
748        self.browser.getControl("Save").click()
749        self.assertTrue('Form has been saved' in self.browser.contents)
750        # Carry-over courses will be collected when next level is created
751        self.browser.open(self.student_path + '/studycourse/manage')
752        # Add next level
753        self.browser.getControl(name="addlevel").value = ['200']
754        self.browser.getControl(name="level_session").value = ['2005']
755        self.browser.getControl("Add study level").click()
756        self.browser.getLink("200").click()
757        self.assertMatches(
758            '...: Study Level 200 (Year 2)...', self.browser.contents)
759        # COURSE1 has score 0 and thus will become a carry-over course
760        # in level 200
761        self.assertEqual(
762            sorted(self.student['studycourse']['200'].keys()), [u'COURSE1'])
763        self.assertTrue(
764            self.student['studycourse']['200']['COURSE1'].carry_over)
765        return
766
767    def test_manage_payments(self):
768        # Managers can add online school fee payment tickets
769        # if certain requirements are met
770        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
771        self.browser.open(self.payments_path)
772        IWorkflowState(self.student).setState('cleared')
773        self.browser.getLink("Add current session payment ticket").click()
774        self.browser.getControl(name="form.p_category").value = ['schoolfee']
775        self.browser.getControl("Create ticket").click()
776        self.assertMatches('...ticket created...',
777                           self.browser.contents)
778        ctrl = self.browser.getControl(name='val_id')
779        value = ctrl.options[0]
780        self.browser.getLink(value).click()
781        self.assertMatches('...Amount Authorized...',
782                           self.browser.contents)
783        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
784        payment_url = self.browser.url
785
786        # The pdf payment slip can't yet be opened
787        #self.browser.open(payment_url + '/payment_slip.pdf')
788        #self.assertMatches('...Ticket not yet paid...',
789        #                   self.browser.contents)
790
791        # The same payment (with same p_item, p_session and p_category)
792        # can be initialized a second time if the former ticket is not yet paid.
793        self.browser.open(self.payments_path)
794        self.browser.getLink("Add current session payment ticket").click()
795        self.browser.getControl(name="form.p_category").value = ['schoolfee']
796        self.browser.getControl("Create ticket").click()
797        self.assertMatches('...Payment ticket created...',
798                           self.browser.contents)
799
800        # The ticket can be found in the payments_catalog
801        cat = queryUtility(ICatalog, name='payments_catalog')
802        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
803        self.assertTrue(len(results), 1)
804        self.assertTrue(results[0] is self.student['payments'][value])
805
806        # Managers can approve the payment
807        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
808        self.browser.open(payment_url)
809        self.browser.getLink("Approve payment").click()
810        self.assertMatches('...Payment approved...',
811                          self.browser.contents)
812        # Approval is logged
813        logfile = os.path.join(
814            self.app['datacenter'].storage, 'logs', 'students.log')
815        logcontent = open(logfile).read()
816        self.assertTrue(
817            'zope.mgr - students.browser.OnlinePaymentApprovePage '
818            '- K1000000 - schoolfee payment approved'
819            in logcontent)
820
821        # The authorized amount has been stored in the access code
822        self.assertEqual(
823            self.app['accesscodes']['SFE-0'].values()[0].cost,40000.0)
824
825        # The catalog has been updated
826        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
827        self.assertTrue(len(results), 0)
828        results = list(cat.searchResults(p_state=('paid', 'paid')))
829        self.assertTrue(len(results), 1)
830        self.assertTrue(results[0] is self.student['payments'][value])
831
832        # Payments can't be approved twice
833        self.browser.open(payment_url + '/approve')
834        self.assertMatches('...This ticket has already been paid...',
835                          self.browser.contents)
836
837        # Now the first ticket is paid and no more ticket of same type
838        # (with same p_item, p_session and p_category) can be added
839        self.browser.open(self.payments_path)
840        self.browser.getLink("Add current session payment ticket").click()
841        self.browser.getControl(name="form.p_category").value = ['schoolfee']
842        self.browser.getControl("Create ticket").click()
843        self.assertMatches(
844            '...This type of payment has already been made...',
845            self.browser.contents)
846
847        # Managers can open the pdf payment slip
848        self.browser.open(payment_url)
849        self.browser.getLink("Download payment slip").click()
850        self.assertEqual(self.browser.headers['Status'], '200 Ok')
851        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
852
853        # Managers can remove online school fee payment tickets
854        self.browser.open(self.payments_path)
855        self.browser.getControl("Remove selected").click()
856        self.assertMatches('...No payment selected...', self.browser.contents)
857        ctrl = self.browser.getControl(name='val_id')
858        value = ctrl.options[0]
859        ctrl.getControl(value=value).selected = True
860        self.browser.getControl("Remove selected", index=0).click()
861        self.assertTrue('Successfully removed' in self.browser.contents)
862
863        # Managers can add online clearance payment tickets
864        self.browser.open(self.payments_path + '/addop')
865        self.browser.getControl(name="form.p_category").value = ['clearance']
866        self.browser.getControl("Create ticket").click()
867        self.assertMatches('...ticket created...',
868                           self.browser.contents)
869
870        # Managers can approve the payment
871        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
872        ctrl = self.browser.getControl(name='val_id')
873        value = ctrl.options[1] # The clearance payment is the second in the table
874        self.browser.getLink(value).click()
875        self.browser.open(self.browser.url + '/approve')
876        self.assertMatches('...Payment approved...',
877                          self.browser.contents)
878        expected = '''...
879        <td>
880          <span>Paid</span>
881        </td>...'''
882        self.assertMatches(expected,self.browser.contents)
883        # The new CLR-0 pin has been created
884        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
885        pin = self.app['accesscodes']['CLR-0'].keys()[0]
886        ac = self.app['accesscodes']['CLR-0'][pin]
887        self.assertEqual(ac.owner, self.student_id)
888        self.assertEqual(ac.cost, 3456.0)
889        return
890
891    def test_manage_accommodation(self):
892        logfile = os.path.join(
893            self.app['datacenter'].storage, 'logs', 'students.log')
894        # Managers can add online booking fee payment tickets and open the
895        # callback view (see test_manage_payments)
896        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
897        self.browser.open(self.payments_path)
898        self.browser.getLink("Add current session payment ticket").click()
899        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
900        # If student is not in accommodation session, payment cannot be processed
901        self.app['hostels'].accommodation_session = 2011
902        self.browser.getControl("Create ticket").click()
903        self.assertMatches('...Your current session does not match...',
904                           self.browser.contents)
905        self.app['hostels'].accommodation_session = 2004
906        self.browser.getLink("Add current session payment ticket").click()
907        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
908        self.browser.getControl("Create ticket").click()
909        ctrl = self.browser.getControl(name='val_id')
910        value = ctrl.options[0]
911        self.browser.getLink(value).click()
912        self.browser.open(self.browser.url + '/approve')
913        # The new HOS-0 pin has been created
914        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
915        pin = self.app['accesscodes']['HOS-0'].keys()[0]
916        ac = self.app['accesscodes']['HOS-0'][pin]
917        self.assertEqual(ac.owner, self.student_id)
918        parts = pin.split('-')[1:]
919        sfeseries, sfenumber = parts
920        # Managers can use HOS code and book a bed space with it
921        self.browser.open(self.acco_path)
922        self.browser.getLink("Book accommodation").click()
923        self.assertMatches('...You are in the wrong...',
924                           self.browser.contents)
925        IWorkflowInfo(self.student).fireTransition('admit')
926        # An existing HOS code can only be used if students
927        # are in accommodation session
928        self.student['studycourse'].current_session = 2003
929        self.browser.getLink("Book accommodation").click()
930        self.assertMatches('...Your current session does not match...',
931                           self.browser.contents)
932        self.student['studycourse'].current_session = 2004
933        # All requirements are met and ticket can be created
934        self.browser.getLink("Book accommodation").click()
935        self.assertMatches('...Activation Code:...',
936                           self.browser.contents)
937        self.browser.getControl(name="ac_series").value = sfeseries
938        self.browser.getControl(name="ac_number").value = sfenumber
939        self.browser.getControl("Create bed ticket").click()
940        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
941                           self.browser.contents)
942        # Bed has been allocated
943        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
944        self.assertTrue(bed1.owner == self.student_id)
945        # BedTicketAddPage is now blocked
946        self.browser.getLink("Book accommodation").click()
947        self.assertMatches('...You already booked a bed space...',
948            self.browser.contents)
949        # The bed ticket displays the data correctly
950        self.browser.open(self.acco_path + '/2004')
951        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
952                           self.browser.contents)
953        self.assertMatches('...2004/2005...', self.browser.contents)
954        self.assertMatches('...regular_male_fr...', self.browser.contents)
955        self.assertMatches('...%s...' % pin, self.browser.contents)
956        # Booking is properly logged
957        logcontent = open(logfile).read()
958        self.assertTrue('zope.mgr - students.browser.BedTicketAddPage '
959            '- K1000000 - booked: hall-1_A_101_A' in logcontent)
960        # Managers can relocate students if the student's bed_type has changed
961        self.browser.getLink("Relocate student").click()
962        self.assertMatches(
963            "...Student can't be relocated...", self.browser.contents)
964        self.student.sex = u'f'
965        self.browser.getLink("Relocate student").click()
966        self.assertMatches(
967            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
968        self.assertTrue(bed1.owner == NOT_OCCUPIED)
969        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
970        self.assertTrue(bed2.owner == self.student_id)
971        self.assertTrue(self.student['accommodation'][
972            '2004'].bed_type == u'regular_female_fr')
973        # Relocation is properly logged
974        logcontent = open(logfile).read()
975        self.assertTrue('zope.mgr - students.browser.BedTicketRelocationPage '
976            '- K1000000 - relocated: hall-1_A_101_B' in logcontent)
977        # The payment object still shows the original payment item
978        payment_id = self.student['payments'].keys()[0]
979        payment = self.student['payments'][payment_id]
980        self.assertTrue(payment.p_item == u'regular_male_fr')
981        # Managers can relocate students if the bed's bed_type has changed
982        bed1.bed_type = u'regular_female_fr'
983        bed2.bed_type = u'regular_male_fr'
984        notify(grok.ObjectModifiedEvent(bed1))
985        notify(grok.ObjectModifiedEvent(bed2))
986        self.browser.getLink("Relocate student").click()
987        self.assertMatches(
988            "...Student relocated...", self.browser.contents)
989        self.assertMatches(
990            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
991        self.assertMatches(bed1.owner, self.student_id)
992        self.assertMatches(bed2.owner, NOT_OCCUPIED)
993        # Managers can't relocate students if bed is reserved
994        self.student.sex = u'm'
995        bed1.bed_type = u'regular_female_reserved'
996        notify(grok.ObjectModifiedEvent(bed1))
997        self.browser.getLink("Relocate student").click()
998        self.assertMatches(
999            "...Students in reserved beds can't be relocated...",
1000            self.browser.contents)
1001        # Managers can relocate students if booking has been cancelled but
1002        # other bed space has been manually allocated after cancellation
1003        old_owner = bed1.releaseBed()
1004        self.assertMatches(old_owner, self.student_id)
1005        bed2.owner = self.student_id
1006        self.browser.open(self.acco_path + '/2004')
1007        self.assertMatches(
1008            "...booking cancelled...", self.browser.contents)
1009        self.browser.getLink("Relocate student").click()
1010        # We didn't informed the catalog therefore the new owner is not found
1011        self.assertMatches(
1012            "...There is no free bed in your category regular_male_fr...",
1013            self.browser.contents)
1014        # Now we fire the event properly
1015        notify(grok.ObjectModifiedEvent(bed2))
1016        self.browser.getLink("Relocate student").click()
1017        self.assertMatches(
1018            "...Student relocated...", self.browser.contents)
1019        self.assertMatches(
1020            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1021          # Managers can delete bed tickets
1022        self.browser.open(self.acco_path)
1023        ctrl = self.browser.getControl(name='val_id')
1024        value = ctrl.options[0]
1025        ctrl.getControl(value=value).selected = True
1026        self.browser.getControl("Remove selected", index=0).click()
1027        self.assertMatches('...Successfully removed...', self.browser.contents)
1028        # The bed has been properly released by the event handler
1029        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1030        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1031        return
1032
1033    def test_manage_workflow(self):
1034        # Managers can pass through the whole workflow
1035        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1036        student = self.app['students'][self.student_id]
1037        self.browser.open(self.trigtrans_path)
1038        self.assertTrue(student.clearance_locked)
1039        self.browser.getControl(name="transition").value = ['admit']
1040        self.browser.getControl("Save").click()
1041        self.assertTrue(student.clearance_locked)
1042        self.browser.getControl(name="transition").value = ['start_clearance']
1043        self.browser.getControl("Save").click()
1044        self.assertFalse(student.clearance_locked)
1045        self.browser.getControl(name="transition").value = ['request_clearance']
1046        self.browser.getControl("Save").click()
1047        self.assertTrue(student.clearance_locked)
1048        self.browser.getControl(name="transition").value = ['clear']
1049        self.browser.getControl("Save").click()
1050        # Managers approve payment, they do not pay
1051        self.assertFalse('pay_first_school_fee' in self.browser.contents)
1052        self.browser.getControl(
1053            name="transition").value = ['approve_first_school_fee']
1054        self.browser.getControl("Save").click()
1055        self.browser.getControl(name="transition").value = ['reset6']
1056        self.browser.getControl("Save").click()
1057        # In state returning the pay_school_fee transition triggers some
1058        # changes of attributes
1059        self.browser.getControl(name="transition").value = ['approve_school_fee']
1060        self.browser.getControl("Save").click()
1061        self.assertEqual(student['studycourse'].current_session, 2005) # +1
1062        self.assertEqual(student['studycourse'].current_level, 200) # +100
1063        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = Zero = not set
1064        self.assertEqual(student['studycourse'].previous_verdict, 'A')
1065        self.browser.getControl(name="transition").value = ['register_courses']
1066        self.browser.getControl("Save").click()
1067        self.browser.getControl(name="transition").value = ['validate_courses']
1068        self.browser.getControl("Save").click()
1069        self.browser.getControl(name="transition").value = ['return']
1070        self.browser.getControl("Save").click()
1071        return
1072
1073    def test_manage_pg_workflow(self):
1074        # Managers can pass through the whole workflow
1075        IWorkflowState(self.student).setState('school fee paid')
1076        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1077        student = self.app['students'][self.student_id]
1078        self.browser.open(self.trigtrans_path)
1079        self.assertTrue('<option value="reset6">' in self.browser.contents)
1080        self.assertTrue('<option value="register_courses">' in self.browser.contents)
1081        self.assertTrue('<option value="reset5">' in self.browser.contents)
1082        self.certificate.study_mode = 'pg_ft'
1083        self.browser.open(self.trigtrans_path)
1084        self.assertFalse('<option value="reset6">' in self.browser.contents)
1085        self.assertFalse('<option value="register_courses">' in self.browser.contents)
1086        self.assertTrue('<option value="reset5">' in self.browser.contents)
1087        return
1088
1089    def test_manage_import(self):
1090        # Managers can import student data files
1091        datacenter_path = 'http://localhost/app/datacenter'
1092        # Prepare a csv file for students
1093        open('students.csv', 'wb').write(
1094"""firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,password
1095Aaren,Pieri,1,1990-01-02,100000,aa@aa.ng,1234,m,mypwd1
1096Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,m,mypwd1
1097Brit,Berson,3,1990-01-04,100001,aa@aa.ng,1234,m,mypwd1
1098""")
1099        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1100        self.browser.open(datacenter_path)
1101        self.browser.getLink('Upload data').click()
1102        filecontents = StringIO(open('students.csv', 'rb').read())
1103        filewidget = self.browser.getControl(name='uploadfile:file')
1104        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
1105        self.browser.getControl(name='SUBMIT').click()
1106        self.browser.getLink('Process data').click()
1107        button = lookup_submit_value(
1108            'select', 'students_zope.mgr.csv', self.browser)
1109        button.click()
1110        importerselect = self.browser.getControl(name='importer')
1111        modeselect = self.browser.getControl(name='mode')
1112        importerselect.getControl('Student Processor').selected = True
1113        modeselect.getControl(value='create').selected = True
1114        self.browser.getControl('Proceed to step 3').click()
1115        self.assertTrue('Header fields OK' in self.browser.contents)
1116        self.browser.getControl('Perform import').click()
1117        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1118        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
1119        self.assertTrue('Batch processing finished' in self.browser.contents)
1120        open('studycourses.csv', 'wb').write(
1121"""reg_number,matric_number,certificate,current_session,current_level
11221,,CERT1,2008,100
1123,100001,CERT1,2008,100
1124,100002,CERT1,2008,100
1125""")
1126        self.browser.open(datacenter_path)
1127        self.browser.getLink('Upload data').click()
1128        filecontents = StringIO(open('studycourses.csv', 'rb').read())
1129        filewidget = self.browser.getControl(name='uploadfile:file')
1130        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
1131        self.browser.getControl(name='SUBMIT').click()
1132        self.browser.getLink('Process data').click()
1133        button = lookup_submit_value(
1134            'select', 'studycourses_zope.mgr.csv', self.browser)
1135        button.click()
1136        importerselect = self.browser.getControl(name='importer')
1137        modeselect = self.browser.getControl(name='mode')
1138        importerselect.getControl(
1139            'StudentStudyCourse Processor (update only)').selected = True
1140        modeselect.getControl(value='create').selected = True
1141        self.browser.getControl('Proceed to step 3').click()
1142        self.assertTrue('Update mode only' in self.browser.contents)
1143        self.browser.getControl('Proceed to step 3').click()
1144        self.assertTrue('Header fields OK' in self.browser.contents)
1145        self.browser.getControl('Perform import').click()
1146        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1147        self.assertTrue('Successfully processed 2 rows'
1148                        in self.browser.contents)
1149        # The students are properly indexed and we can
1150        # thus find a student in  the department
1151        self.browser.open(self.manage_container_path)
1152        self.browser.getControl(name="searchtype").value = ['depcode']
1153        self.browser.getControl(name="searchterm").value = 'dep1'
1154        self.browser.getControl("Search").click()
1155        self.assertTrue('Aaren Pieri' in self.browser.contents)
1156        # We can search for a new student by name ...
1157        self.browser.getControl(name="searchtype").value = ['fullname']
1158        self.browser.getControl(name="searchterm").value = 'Claus'
1159        self.browser.getControl("Search").click()
1160        self.assertTrue('Claus Finau' in self.browser.contents)
1161        # ... and check if the imported password has been properly set
1162        ctrl = self.browser.getControl(name='entries')
1163        value = ctrl.options[0]
1164        claus = self.app['students'][value]
1165        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
1166        return
1167
1168    def test_handle_clearance_by_co(self):
1169        # Create clearance officer
1170        self.app['users'].addUser('mrclear', 'mrclearsecret')
1171        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
1172        self.app['users']['mrclear'].title = 'Carlo Pitter'
1173        # Clearance officers need not necessarily to get
1174        # the StudentsOfficer site role
1175        #prmglobal = IPrincipalRoleManager(self.app)
1176        #prmglobal.assignRoleToPrincipal('waeup.StudentsOfficer', 'mrclear')
1177        # Assign local ClearanceOfficer role
1178        department = self.app['faculties']['fac1']['dep1']
1179        prmlocal = IPrincipalRoleManager(department)
1180        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
1181        IWorkflowState(self.student).setState('clearance started')
1182        # Login as clearance officer
1183        self.browser.open(self.login_path)
1184        self.browser.getControl(name="form.login").value = 'mrclear'
1185        self.browser.getControl(name="form.password").value = 'mrclearsecret'
1186        self.browser.getControl("Login").click()
1187        self.assertMatches('...You logged in...', self.browser.contents)
1188        # CO can see his roles
1189        self.browser.getLink("My Roles").click()
1190        self.assertMatches(
1191            '...<div>Academics Officer (view only)</div>...',
1192            self.browser.contents)
1193        #self.assertMatches(
1194        #    '...<div>Students Officer (view only)</div>...',
1195        #    self.browser.contents)
1196        # But not his local role ...
1197        self.assertFalse('Clearance Officer' in self.browser.contents)
1198        # ... because we forgot to notify the department that the local role
1199        # has changed
1200        notify(LocalRoleSetEvent(
1201            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
1202        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1203        self.assertTrue('Clearance Officer' in self.browser.contents)
1204        self.assertMatches(
1205            '...<a href="http://localhost/app/faculties/fac1/dep1">...',
1206            self.browser.contents)
1207        # CO can view the student ...
1208        self.browser.open(self.clearance_path)
1209        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1210        self.assertEqual(self.browser.url, self.clearance_path)
1211        # ... but not other students
1212        other_student = Student()
1213        other_student.firstname = u'Dep2'
1214        other_student.lastname = u'Student'
1215        self.app['students'].addStudent(other_student)
1216        other_student_path = (
1217            'http://localhost/app/students/%s' % other_student.student_id)
1218        self.assertRaises(
1219            Unauthorized, self.browser.open, other_student_path)
1220        # Only in state clearance requested the CO does see the 'Clear' button
1221        self.browser.open(self.clearance_path)
1222        self.assertFalse('Clear student' in self.browser.contents)
1223        IWorkflowInfo(self.student).fireTransition('request_clearance')
1224        self.browser.open(self.clearance_path)
1225        self.assertTrue('Clear student' in self.browser.contents)
1226        self.browser.getLink("Clear student").click()
1227        self.assertTrue('Student has been cleared' in self.browser.contents)
1228        self.assertTrue('cleared' in self.browser.contents)
1229        self.browser.open(self.history_path)
1230        self.assertTrue('Cleared by Carlo Pitter' in self.browser.contents)
1231        # Hide real name.
1232        self.app['users']['mrclear'].public_name = 'My Public Name'
1233        self.browser.open(self.clearance_path)
1234        self.browser.getLink("Reject clearance").click()
1235        self.assertEqual(
1236            self.browser.url, self.student_path + '/reject_clearance')
1237        # Type comment why
1238        self.browser.getControl(name="form.officer_comment").value = """Dear Student,
1239You did not fill properly.
1240"""
1241        self.browser.getControl("Save comment").click()
1242        self.assertTrue('Clearance has been annulled' in self.browser.contents)
1243        url = ('http://localhost/app/students/K1000000/'
1244              'contactstudent?body=Dear+Student%2C%0AYou+did+not+fill+properly.'
1245              '%0A&subject=Clearance+has+been+annulled.')
1246        # CO does now see the prefilled contact form and can send a message
1247        self.assertEqual(self.browser.url, url)
1248        self.assertTrue('clearance started' in self.browser.contents)
1249        self.assertTrue('name="form.subject" size="20" type="text" '
1250            'value="Clearance has been annulled."'
1251            in self.browser.contents)
1252        self.assertTrue('name="form.body" rows="10" >Dear Student,'
1253            in self.browser.contents)
1254        self.browser.getControl("Send message now").click()
1255        self.assertTrue('Your message has been sent' in self.browser.contents)
1256        # The comment has been stored ...
1257        self.assertEqual(self.student.officer_comment,
1258            u'Dear Student,\nYou did not fill properly.\n')
1259        # ... and logged
1260        logfile = os.path.join(
1261            self.app['datacenter'].storage, 'logs', 'students.log')
1262        logcontent = open(logfile).read()
1263        self.assertTrue(
1264            'INFO - mrclear - students.browser.StudentRejectClearancePage - '
1265            'K1000000 - comment: Dear Student,<br>You did not fill '
1266            'properly.<br>\n' in logcontent)
1267        self.browser.open(self.history_path)
1268        self.assertTrue("Reset to 'clearance started' by My Public Name" in
1269            self.browser.contents)
1270        IWorkflowInfo(self.student).fireTransition('request_clearance')
1271        self.browser.open(self.clearance_path)
1272        self.browser.getLink("Reject clearance").click()
1273        self.browser.getControl("Save comment").click()
1274        self.assertTrue('Clearance request has been rejected'
1275            in self.browser.contents)
1276        self.assertTrue('clearance started' in self.browser.contents)
1277        # The CO can't clear students if not in state
1278        # clearance requested
1279        self.browser.open(self.student_path + '/clear')
1280        self.assertTrue('Student is in wrong state'
1281            in self.browser.contents)
1282        # The CO can go to his department throug the my_roles page ...
1283        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1284        self.browser.getLink("http://localhost/app/faculties/fac1/dep1").click()
1285        # ... and view the list of students
1286        self.browser.getLink("Show students").click()
1287        self.assertTrue(self.student_id in self.browser.contents)
1288        # The comment is indicated by 'yes'
1289        self.assertTrue('<td><span>yes</span></td>' in self.browser.contents)
1290        # When a student is cleared the comment is automatically deleted
1291        IWorkflowInfo(self.student).fireTransition('request_clearance')
1292        IWorkflowInfo(self.student).fireTransition('clear')
1293        self.assertEqual(self.student.officer_comment, None)
1294
1295    def test_handle_courses_by_ca(self):
1296        # Create course adviser
1297        self.app['users'].addUser('mrsadvise', 'mrsadvisesecret')
1298        self.app['users']['mrsadvise'].email = 'mradvise@foo.ng'
1299        self.app['users']['mrsadvise'].title = u'Helen Procter'
1300        # Assign local CourseAdviser100 role for a certificate
1301        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1302        prmlocal = IPrincipalRoleManager(cert)
1303        prmlocal.assignRoleToPrincipal('waeup.local.CourseAdviser100', 'mrsadvise')
1304        IWorkflowState(self.student).setState('school fee paid')
1305        # Login as course adviser
1306        self.browser.open(self.login_path)
1307        self.browser.getControl(name="form.login").value = 'mrsadvise'
1308        self.browser.getControl(name="form.password").value = 'mrsadvisesecret'
1309        self.browser.getControl("Login").click()
1310        self.assertMatches('...You logged in...', self.browser.contents)
1311        # CO can see his roles
1312        self.browser.getLink("My Roles").click()
1313        self.assertMatches(
1314            '...<div>Academics Officer (view only)</div>...',
1315            self.browser.contents)
1316        # But not his local role ...
1317        self.assertFalse('Course Adviser' in self.browser.contents)
1318        # ... because we forgot to notify the certificate that the local role
1319        # has changed
1320        notify(LocalRoleSetEvent(
1321            cert, 'waeup.local.CourseAdviser100', 'mrsadvise', granted=True))
1322        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1323        self.assertTrue('Course Adviser 100L' in self.browser.contents)
1324        self.assertMatches(
1325            '...<a href="http://localhost/app/faculties/fac1/dep1/certificates/CERT1">...',
1326            self.browser.contents)
1327        # CA can view the student ...
1328        self.browser.open(self.student_path)
1329        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1330        self.assertEqual(self.browser.url, self.student_path)
1331        # ... but not other students
1332        other_student = Student()
1333        other_student.firstname = u'Dep2'
1334        other_student.lastname = u'Student'
1335        self.app['students'].addStudent(other_student)
1336        other_student_path = (
1337            'http://localhost/app/students/%s' % other_student.student_id)
1338        self.assertRaises(
1339            Unauthorized, self.browser.open, other_student_path)
1340        # We add study level 110 to the student's studycourse
1341        studylevel = StudentStudyLevel()
1342        studylevel.level = 110
1343        self.student['studycourse'].addStudentStudyLevel(
1344            cert,studylevel)
1345        L110_student_path = self.studycourse_path + '/110'
1346        # Only in state courses registered and only if the current level
1347        # corresponds with the name of the study level object
1348        # the 100L CA does see the 'Validate' button
1349        self.browser.open(L110_student_path)
1350        self.assertFalse('Validate courses' in self.browser.contents)
1351        IWorkflowInfo(self.student).fireTransition('register_courses')
1352        self.browser.open(L110_student_path)
1353        self.assertFalse('Validate courses' in self.browser.contents)
1354        self.student['studycourse'].current_level = 110
1355        self.browser.open(L110_student_path)
1356        self.assertTrue('Validate courses' in self.browser.contents)
1357        # ... but a 100L CA does not see the button on other levels
1358        studylevel2 = StudentStudyLevel()
1359        studylevel2.level = 200
1360        self.student['studycourse'].addStudentStudyLevel(
1361            cert,studylevel2)
1362        L200_student_path = self.studycourse_path + '/200'
1363        self.browser.open(L200_student_path)
1364        self.assertFalse('Validate courses' in self.browser.contents)
1365        self.browser.open(L110_student_path)
1366        self.browser.getLink("Validate courses").click()
1367        self.assertTrue('Course list has been validated' in self.browser.contents)
1368        self.assertTrue('courses validated' in self.browser.contents)
1369        self.assertEqual(self.student['studycourse']['110'].validated_by,
1370            'Helen Procter')
1371        self.assertMatches(
1372            '<YYYY-MM-DD hh:mm:ss>',
1373            self.student['studycourse']['110'].validation_date.strftime(
1374                "%Y-%m-%d %H:%M:%S"))
1375        self.browser.getLink("Reject courses").click()
1376        self.assertTrue('Course list request has been annulled.'
1377            in self.browser.contents)
1378        urlmessage = 'Course+list+request+has+been+annulled.'
1379        self.assertEqual(self.browser.url, self.student_path +
1380            '/contactstudent?subject=%s' % urlmessage)
1381        self.assertTrue('school fee paid' in self.browser.contents)
1382        self.assertTrue(self.student['studycourse']['110'].validated_by is None)
1383        self.assertTrue(self.student['studycourse']['110'].validation_date is None)
1384        IWorkflowInfo(self.student).fireTransition('register_courses')
1385        self.browser.open(L110_student_path)
1386        self.browser.getLink("Reject courses").click()
1387        self.assertTrue('Course list request has been rejected'
1388            in self.browser.contents)
1389        self.assertTrue('school fee paid' in self.browser.contents)
1390        # CA does now see the contact form and can send a message
1391        self.browser.getControl(name="form.subject").value = 'Important subject'
1392        self.browser.getControl(name="form.body").value = 'Course list rejected'
1393        self.browser.getControl("Send message now").click()
1394        self.assertTrue('Your message has been sent' in self.browser.contents)
1395        # The CA can't validate courses if not in state
1396        # courses registered
1397        self.browser.open(L110_student_path + '/validate_courses')
1398        self.assertTrue('Student is in the wrong state'
1399            in self.browser.contents)
1400        # The CA can go to his certificate through the my_roles page
1401        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1402        self.browser.getLink(
1403            "http://localhost/app/faculties/fac1/dep1/certificates/CERT1").click()
1404        # and view the list of students
1405        self.browser.getLink("Show students").click()
1406        self.assertTrue(self.student_id in self.browser.contents)
1407
1408    def test_handle_courses_by_lecturer(self):
1409        # Create course lecturer
1410        self.app['users'].addUser('mrslecturer', 'mrslecturersecret')
1411        self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'
1412        self.app['users']['mrslecturer'].title = u'Mercedes Benz'
1413        # Assign local Courselecturer100 role for a certificate
1414        course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']
1415        prmlocal = IPrincipalRoleManager(course)
1416        prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
1417        # Login as lecturer
1418        self.browser.open(self.login_path)
1419        self.browser.getControl(name="form.login").value = 'mrslecturer'
1420        self.browser.getControl(name="form.password").value = 'mrslecturersecret'
1421        self.browser.getControl("Login").click()
1422        self.assertMatches('...You logged in...', self.browser.contents)
1423        # CO can see her roles
1424        self.browser.getLink("My Roles").click()
1425        self.assertMatches(
1426            '...<div>Academics Officer (view only)</div>...',
1427            self.browser.contents)
1428        # But not her local role ...
1429        self.assertFalse('Lecturer' in self.browser.contents)
1430        # ... because we forgot to notify the course that the local role
1431        # has changed
1432        notify(LocalRoleSetEvent(
1433            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
1434        self.browser.open('http://localhost/app/users/mrslecturer/my_roles')
1435        self.assertTrue('Lecturer' in self.browser.contents)
1436        self.assertMatches(
1437            '...<a href="http://localhost/app/faculties/fac1/dep1/courses/COURSE1">...',
1438            self.browser.contents)
1439        # The lecturer can go to her course
1440        self.browser.getLink(
1441            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1").click()
1442        # and view the list of students
1443        self.browser.getLink("Show students").click()
1444        self.assertTrue('<th>Fullname</th>' in self.browser.contents)
1445        # No student in course so far
1446        self.assertFalse(self.student_id in self.browser.contents)
1447        studylevel = createObject(u'waeup.StudentStudyLevel')
1448        studylevel.level = 100
1449        self.student['studycourse'].addStudentStudyLevel(
1450            self.certificate, studylevel)
1451        # Now the student has registered the course and can
1452        # be seen by the lecturer.
1453        self.browser.open("http://localhost/app/faculties/fac1/dep1/courses/COURSE1/showcoursestudents")
1454        self.assertTrue(self.student_id in self.browser.contents)
1455        # XXX: So far the lecturer can neither access ths student ...
1456        self.assertRaises(
1457            Unauthorized, self.browser.open, self.student_path)
1458        # ... nor the respective course ticket since a
1459        # CourseTicketPrincipalRoleManager does not yet exist.
1460        self.assertTrue('COURSE1' in self.student['studycourse']['100'].keys())
1461        course_ticket_path = self.student_path + '/studycourse/100/COURSE1'
1462        self.assertRaises(
1463            Unauthorized, self.browser.open, course_ticket_path)
1464
1465    def test_change_current_mode(self):
1466        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1467        self.browser.open(self.clearance_path)
1468        self.assertFalse('Employer' in self.browser.contents)
1469        self.browser.open(self.manage_clearance_path)
1470        self.assertFalse('Employer' in self.browser.contents)
1471        self.student.clearance_locked = False
1472        self.browser.open(self.edit_clearance_path)
1473        self.assertFalse('Employer' in self.browser.contents)
1474        # Now we change the study mode of the certificate and a different
1475        # interface is used by clearance views.
1476        self.certificate.study_mode = 'pg_ft'
1477        # Invariants are not being checked here?!
1478        self.certificate.end_level = 100
1479        self.browser.open(self.clearance_path)
1480        self.assertTrue('Employer' in self.browser.contents)
1481        self.browser.open(self.manage_clearance_path)
1482        self.assertTrue('Employer' in self.browser.contents)
1483        self.browser.open(self.edit_clearance_path)
1484        self.assertTrue('Employer' in self.browser.contents)
1485
1486    def test_activate_deactivate_buttons(self):
1487        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1488        self.browser.open(self.student_path)
1489        self.browser.getLink("Deactivate").click()
1490        self.assertTrue(
1491            'Student account has been deactivated.' in self.browser.contents)
1492        self.assertTrue(
1493            'Base Data (account deactivated)' in self.browser.contents)
1494        self.assertTrue(self.student.suspended)
1495        self.browser.getLink("Activate").click()
1496        self.assertTrue(
1497            'Student account has been activated.' in self.browser.contents)
1498        self.assertFalse(
1499            'Base Data (account deactivated)' in self.browser.contents)
1500        self.assertFalse(self.student.suspended)
1501        # History messages have been added ...
1502        self.browser.getLink("History").click()
1503        self.assertTrue(
1504            'Student account deactivated by Manager<br />' in self.browser.contents)
1505        self.assertTrue(
1506            'Student account activated by Manager<br />' in self.browser.contents)
1507        # ... and actions have been logged.
1508        logfile = os.path.join(
1509            self.app['datacenter'].storage, 'logs', 'students.log')
1510        logcontent = open(logfile).read()
1511        self.assertTrue('zope.mgr - students.browser.StudentDeactivatePage - '
1512                        'K1000000 - account deactivated' in logcontent)
1513        self.assertTrue('zope.mgr - students.browser.StudentActivatePage - '
1514                        'K1000000 - account activated' in logcontent)
1515
1516    def test_manage_student_transfer(self):
1517        # Add second certificate
1518        self.certificate2 = createObject('waeup.Certificate')
1519        self.certificate2.code = u'CERT2'
1520        self.certificate2.study_mode = 'ug_ft'
1521        self.certificate2.start_level = 999
1522        self.certificate2.end_level = 999
1523        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1524            self.certificate2)
1525
1526        # Add study level to old study course
1527        studylevel = createObject(u'waeup.StudentStudyLevel')
1528        studylevel.level = 200
1529        self.student['studycourse'].addStudentStudyLevel(
1530            self.certificate, studylevel)
1531        studylevel = createObject(u'waeup.StudentStudyLevel')
1532        studylevel.level = 999
1533        self.student['studycourse'].addStudentStudyLevel(
1534            self.certificate, studylevel)
1535
1536        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1537        self.browser.open(self.student_path)
1538        self.browser.getLink("Transfer").click()
1539        self.browser.getControl(name="form.certificate").value = ['CERT2']
1540        self.browser.getControl(name="form.current_session").value = ['2011']
1541        self.browser.getControl(name="form.current_level").value = ['200']
1542        self.browser.getControl("Transfer").click()
1543        self.assertTrue(
1544            'Current level does not match certificate levels'
1545            in self.browser.contents)
1546        self.browser.getControl(name="form.current_level").value = ['999']
1547        self.browser.getControl("Transfer").click()
1548        self.assertTrue('Successfully transferred' in self.browser.contents)
1549        # The catalog has been updated
1550        cat = queryUtility(ICatalog, name='students_catalog')
1551        results = list(
1552            cat.searchResults(
1553            certcode=('CERT2', 'CERT2')))
1554        self.assertTrue(results[0] is self.student)
1555        results = list(
1556            cat.searchResults(
1557            current_session=(2011, 2011)))
1558        self.assertTrue(results[0] is self.student)
1559        # Add study level to new study course
1560        studylevel = createObject(u'waeup.StudentStudyLevel')
1561        studylevel.level = 999
1562        self.student['studycourse'].addStudentStudyLevel(
1563            self.certificate, studylevel)
1564
1565        # Edit and add pages are locked for old study courses
1566        self.browser.open(self.student_path + '/studycourse/manage')
1567        self.assertFalse('The requested form is locked' in self.browser.contents)
1568        self.browser.open(self.student_path + '/studycourse_1/manage')
1569        self.assertTrue('The requested form is locked' in self.browser.contents)
1570
1571        self.browser.open(self.student_path + '/studycourse/start_session')
1572        self.assertFalse('The requested form is locked' in self.browser.contents)
1573        self.browser.open(self.student_path + '/studycourse_1/start_session')
1574        self.assertTrue('The requested form is locked' in self.browser.contents)
1575
1576        IWorkflowState(self.student).setState('school fee paid')
1577        self.browser.open(self.student_path + '/studycourse/add')
1578        self.assertFalse('The requested form is locked' in self.browser.contents)
1579        self.browser.open(self.student_path + '/studycourse_1/add')
1580        self.assertTrue('The requested form is locked' in self.browser.contents)
1581
1582        self.browser.open(self.student_path + '/studycourse/999/manage')
1583        self.assertFalse('The requested form is locked' in self.browser.contents)
1584        self.browser.open(self.student_path + '/studycourse_1/999/manage')
1585        self.assertTrue('The requested form is locked' in self.browser.contents)
1586
1587        self.browser.open(self.student_path + '/studycourse/999/validate_courses')
1588        self.assertFalse('The requested form is locked' in self.browser.contents)
1589        self.browser.open(self.student_path + '/studycourse_1/999/validate_courses')
1590        self.assertTrue('The requested form is locked' in self.browser.contents)
1591
1592        self.browser.open(self.student_path + '/studycourse/999/reject_courses')
1593        self.assertFalse('The requested form is locked' in self.browser.contents)
1594        self.browser.open(self.student_path + '/studycourse_1/999/reject_courses')
1595        self.assertTrue('The requested form is locked' in self.browser.contents)
1596
1597        self.browser.open(self.student_path + '/studycourse/999/add')
1598        self.assertFalse('The requested form is locked' in self.browser.contents)
1599        self.browser.open(self.student_path + '/studycourse_1/999/add')
1600        self.assertTrue('The requested form is locked' in self.browser.contents)
1601
1602        self.browser.open(self.student_path + '/studycourse/999/edit')
1603        self.assertFalse('The requested form is locked' in self.browser.contents)
1604        self.browser.open(self.student_path + '/studycourse_1/999/edit')
1605        self.assertTrue('The requested form is locked' in self.browser.contents)
1606
1607    def test_login_as_student(self):
1608        # StudentImpersonators can login as student
1609        # Create clearance officer
1610        self.app['users'].addUser('mrofficer', 'mrofficersecret')
1611        self.app['users']['mrofficer'].email = 'mrofficer@foo.ng'
1612        self.app['users']['mrofficer'].title = 'Harry Actor'
1613        prmglobal = IPrincipalRoleManager(self.app)
1614        prmglobal.assignRoleToPrincipal('waeup.StudentImpersonator', 'mrofficer')
1615        prmglobal.assignRoleToPrincipal('waeup.StudentsManager', 'mrofficer')
1616        # Login as student impersonator
1617        self.browser.open(self.login_path)
1618        self.browser.getControl(name="form.login").value = 'mrofficer'
1619        self.browser.getControl(name="form.password").value = 'mrofficersecret'
1620        self.browser.getControl("Login").click()
1621        self.assertMatches('...You logged in...', self.browser.contents)
1622        self.browser.open(self.student_path)
1623        self.browser.getLink("Login as").click()
1624        self.browser.getControl("Set password now").click()
1625        temp_password = self.browser.getControl(name='form.password').value
1626        self.browser.getControl("Login now").click()
1627        self.assertMatches(
1628            '...You successfully logged in as...', self.browser.contents)
1629        # We are logged in as student and can see the 'My Data' tab
1630        self.assertMatches(
1631            '...<a href="#" class="dropdown-toggle">My Data</a>...',
1632            self.browser.contents)
1633        self.browser.getLink("Logout").click()
1634        # The student can't login with the original password ...
1635        self.browser.open(self.login_path)
1636        self.browser.getControl(name="form.login").value = self.student_id
1637        self.browser.getControl(name="form.password").value = 'spwd'
1638        self.browser.getControl("Login").click()
1639        self.assertMatches(
1640            '...Your account has been temporarily deactivated...',
1641            self.browser.contents)
1642        # ... but with the temporary password
1643        self.browser.open(self.login_path)
1644        self.browser.getControl(name="form.login").value = self.student_id
1645        self.browser.getControl(name="form.password").value = temp_password
1646        self.browser.getControl("Login").click()
1647        self.assertMatches('...You logged in...', self.browser.contents)
1648        # Creation of temp_password is properly logged
1649        logfile = os.path.join(
1650            self.app['datacenter'].storage, 'logs', 'students.log')
1651        logcontent = open(logfile).read()
1652        self.assertTrue(
1653            'mrofficer - students.browser.LoginAsStudentStep1 - K1000000 - '
1654            'temp_password generated: %s' % temp_password in logcontent)
1655
1656class StudentUITests(StudentsFullSetup):
1657    # Tests for Student class views and pages
1658
1659    def test_student_change_password(self):
1660        # Students can change the password
1661        self.student.personal_updated = datetime.utcnow()
1662        self.browser.open(self.login_path)
1663        self.browser.getControl(name="form.login").value = self.student_id
1664        self.browser.getControl(name="form.password").value = 'spwd'
1665        self.browser.getControl("Login").click()
1666        self.assertEqual(self.browser.url, self.student_path)
1667        self.assertTrue('You logged in' in self.browser.contents)
1668        # Change password
1669        self.browser.getLink("Change password").click()
1670        self.browser.getControl(name="change_password").value = 'pw'
1671        self.browser.getControl(
1672            name="change_password_repeat").value = 'pw'
1673        self.browser.getControl("Save").click()
1674        self.assertTrue('Password must have at least' in self.browser.contents)
1675        self.browser.getControl(name="change_password").value = 'new_password'
1676        self.browser.getControl(
1677            name="change_password_repeat").value = 'new_passssword'
1678        self.browser.getControl("Save").click()
1679        self.assertTrue('Passwords do not match' in self.browser.contents)
1680        self.browser.getControl(name="change_password").value = 'new_password'
1681        self.browser.getControl(
1682            name="change_password_repeat").value = 'new_password'
1683        self.browser.getControl("Save").click()
1684        self.assertTrue('Password changed' in self.browser.contents)
1685        # We are still logged in. Changing the password hasn't thrown us out.
1686        self.browser.getLink("Base Data").click()
1687        self.assertEqual(self.browser.url, self.student_path)
1688        # We can logout
1689        self.browser.getLink("Logout").click()
1690        self.assertTrue('You have been logged out' in self.browser.contents)
1691        self.assertEqual(self.browser.url, 'http://localhost/app')
1692        # We can login again with the new password
1693        self.browser.getLink("Login").click()
1694        self.browser.open(self.login_path)
1695        self.browser.getControl(name="form.login").value = self.student_id
1696        self.browser.getControl(name="form.password").value = 'new_password'
1697        self.browser.getControl("Login").click()
1698        self.assertEqual(self.browser.url, self.student_path)
1699        self.assertTrue('You logged in' in self.browser.contents)
1700        return
1701
1702    def test_setpassword(self):
1703        # Set password for first-time access
1704        student = Student()
1705        student.reg_number = u'123456'
1706        student.firstname = u'Klaus'
1707        student.lastname = u'Tester'
1708        self.app['students'].addStudent(student)
1709        setpassword_path = 'http://localhost/app/setpassword'
1710        student_path = 'http://localhost/app/students/%s' % student.student_id
1711        self.browser.open(setpassword_path)
1712        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
1713        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1714        self.browser.getControl(name="reg_number").value = '223456'
1715        self.browser.getControl("Set").click()
1716        self.assertMatches('...No student found...',
1717                           self.browser.contents)
1718        self.browser.getControl(name="reg_number").value = '123456'
1719        self.browser.getControl(name="ac_number").value = '999999'
1720        self.browser.getControl("Set").click()
1721        self.assertMatches('...Access code is invalid...',
1722                           self.browser.contents)
1723        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1724        self.browser.getControl("Set").click()
1725        self.assertMatches('...Password has been set. Your Student Id is...',
1726                           self.browser.contents)
1727        self.browser.getControl("Set").click()
1728        self.assertMatches(
1729            '...Password has already been set. Your Student Id is...',
1730            self.browser.contents)
1731        existing_pwdpin = self.pwdpins[1]
1732        parts = existing_pwdpin.split('-')[1:]
1733        existing_pwdseries, existing_pwdnumber = parts
1734        self.browser.getControl(name="ac_series").value = existing_pwdseries
1735        self.browser.getControl(name="ac_number").value = existing_pwdnumber
1736        self.browser.getControl(name="reg_number").value = '123456'
1737        self.browser.getControl("Set").click()
1738        self.assertMatches(
1739            '...You are using the wrong Access Code...',
1740            self.browser.contents)
1741        # The student can login with the new credentials
1742        self.browser.open(self.login_path)
1743        self.browser.getControl(name="form.login").value = student.student_id
1744        self.browser.getControl(
1745            name="form.password").value = self.existing_pwdnumber
1746        self.browser.getControl("Login").click()
1747        self.assertEqual(self.browser.url, student_path)
1748        self.assertTrue('You logged in' in self.browser.contents)
1749        return
1750
1751    def test_student_login(self):
1752        # Student cant login if their password is not set
1753        self.student.password = None
1754        self.browser.open(self.login_path)
1755        self.browser.getControl(name="form.login").value = self.student_id
1756        self.browser.getControl(name="form.password").value = 'spwd'
1757        self.browser.getControl("Login").click()
1758        self.assertTrue(
1759            'You entered invalid credentials.' in self.browser.contents)
1760        # We set the password again
1761        IUserAccount(
1762            self.app['students'][self.student_id]).setPassword('spwd')
1763        # Students can't login if their account is suspended/deactivated
1764        self.student.suspended = True
1765        self.browser.open(self.login_path)
1766        self.browser.getControl(name="form.login").value = self.student_id
1767        self.browser.getControl(name="form.password").value = 'spwd'
1768        self.browser.getControl("Login").click()
1769        self.assertMatches(
1770            '...Your account has been deactivated...', self.browser.contents)
1771        self.student.suspended = False
1772        # Students can't login if a temporary password has been set and
1773        # is not expired
1774        self.app['students'][self.student_id].setTempPassword(
1775            'anybody', 'temp_spwd')
1776        self.browser.open(self.login_path)
1777        self.browser.getControl(name="form.login").value = self.student_id
1778        self.browser.getControl(name="form.password").value = 'spwd'
1779        self.browser.getControl("Login").click()
1780        self.assertMatches(
1781            '...Your account has been temporarily deactivated...',
1782            self.browser.contents)
1783        # The student can login with the temporary password
1784        self.browser.open(self.login_path)
1785        self.browser.getControl(name="form.login").value = self.student_id
1786        self.browser.getControl(name="form.password").value = 'temp_spwd'
1787        self.browser.getControl("Login").click()
1788        self.assertMatches(
1789            '...You logged in...', self.browser.contents)
1790        # Student can view the base data
1791        self.browser.open(self.student_path)
1792        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1793        self.assertEqual(self.browser.url, self.student_path)
1794        # When the password expires ...
1795        delta = timedelta(minutes=11)
1796        self.app['students'][self.student_id].temp_password[
1797            'timestamp'] = datetime.utcnow() - delta
1798        self.app['students'][self.student_id]._p_changed = True
1799        # ... the student will be automatically logged out
1800        self.assertRaises(
1801            Unauthorized, self.browser.open, self.student_path)
1802        # Then the student can login with the original password
1803        self.browser.open(self.login_path)
1804        self.browser.getControl(name="form.login").value = self.student_id
1805        self.browser.getControl(name="form.password").value = 'spwd'
1806        self.browser.getControl("Login").click()
1807        self.assertMatches(
1808            '...You logged in...', self.browser.contents)
1809
1810    def test_student_clearance(self):
1811        # Student cant login if their password is not set
1812        IWorkflowInfo(self.student).fireTransition('admit')
1813        self.browser.open(self.login_path)
1814        self.browser.getControl(name="form.login").value = self.student_id
1815        self.browser.getControl(name="form.password").value = 'spwd'
1816        self.browser.getControl("Login").click()
1817        self.assertMatches(
1818            '...You logged in...', self.browser.contents)
1819        # Admitted student can upload a passport picture
1820        self.browser.open(self.student_path + '/change_portrait')
1821        ctrl = self.browser.getControl(name='passportuploadedit')
1822        file_obj = open(SAMPLE_IMAGE, 'rb')
1823        file_ctrl = ctrl.mech_control
1824        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1825        self.browser.getControl(
1826            name='upload_passportuploadedit').click()
1827        self.assertTrue(
1828            '<img align="middle" height="125px" src="passport.jpg" />'
1829            in self.browser.contents)
1830        # Students can open admission letter
1831        self.browser.getLink("Base Data").click()
1832        self.browser.getLink("Download admission letter").click()
1833        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1834        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1835        # Student can view the clearance data
1836        self.browser.open(self.student_path)
1837        self.browser.getLink("Clearance Data").click()
1838        # Student can't open clearance edit form before starting clearance
1839        self.browser.open(self.student_path + '/cedit')
1840        self.assertMatches('...The requested form is locked...',
1841                           self.browser.contents)
1842        self.browser.getLink("Clearance Data").click()
1843        self.browser.getLink("Start clearance").click()
1844        self.student.email = None
1845        # Uups, we forgot to fill the email fields
1846        self.browser.getControl("Start clearance").click()
1847        self.assertMatches('...Not all required fields filled...',
1848                           self.browser.contents)
1849        self.browser.open(self.student_path + '/edit_base')
1850        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1851        self.browser.getControl("Save").click()
1852        self.browser.open(self.student_path + '/start_clearance')
1853        self.browser.getControl(name="ac_series").value = '3'
1854        self.browser.getControl(name="ac_number").value = '4444444'
1855        self.browser.getControl("Start clearance now").click()
1856        self.assertMatches('...Activation code is invalid...',
1857                           self.browser.contents)
1858        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1859        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1860        # Owner is Hans Wurst, AC can't be invalidated
1861        self.browser.getControl("Start clearance now").click()
1862        self.assertMatches('...You are not the owner of this access code...',
1863                           self.browser.contents)
1864        # Set the correct owner
1865        self.existing_clrac.owner = self.student_id
1866        # clr_code might be set (and thus returns None) due importing
1867        # an empty clr_code column.
1868        self.student.clr_code = None
1869        self.browser.getControl("Start clearance now").click()
1870        self.assertMatches('...Clearance process has been started...',
1871                           self.browser.contents)
1872        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1873        self.browser.getControl("Save", index=0).click()
1874        # Student can view the clearance data
1875        self.browser.getLink("Clearance Data").click()
1876        # and go back to the edit form
1877        self.browser.getLink("Edit").click()
1878        # Students can upload documents
1879        ctrl = self.browser.getControl(name='birthcertificateupload')
1880        file_obj = open(SAMPLE_IMAGE, 'rb')
1881        file_ctrl = ctrl.mech_control
1882        file_ctrl.add_file(file_obj, filename='my_birth_certificate.jpg')
1883        self.browser.getControl(
1884            name='upload_birthcertificateupload').click()
1885        self.assertTrue(
1886            '<a target="image" href="birth_certificate">Birth Certificate Scan</a>'
1887            in self.browser.contents)
1888        # Students can open clearance slip
1889        self.browser.getLink("View").click()
1890        self.browser.getLink("Download clearance slip").click()
1891        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1892        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1893        # Students can request clearance
1894        self.browser.open(self.edit_clearance_path)
1895        self.browser.getControl("Save and request clearance").click()
1896        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1897        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1898        self.browser.getControl("Request clearance now").click()
1899        self.assertMatches('...Clearance has been requested...',
1900                           self.browser.contents)
1901        # Student can't reopen clearance form after requesting clearance
1902        self.browser.open(self.student_path + '/cedit')
1903        self.assertMatches('...The requested form is locked...',
1904                           self.browser.contents)
1905
1906    def test_student_course_registration(self):
1907        # Student cant login if their password is not set
1908        IWorkflowInfo(self.student).fireTransition('admit')
1909        self.browser.open(self.login_path)
1910        self.browser.getControl(name="form.login").value = self.student_id
1911        self.browser.getControl(name="form.password").value = 'spwd'
1912        self.browser.getControl("Login").click()
1913        # Student can't add study level if not in state 'school fee paid'
1914        self.browser.open(self.student_path + '/studycourse/add')
1915        self.assertMatches('...The requested form is locked...',
1916                           self.browser.contents)
1917        # ... and must be transferred first
1918        IWorkflowState(self.student).setState('school fee paid')
1919        # Now students can add the current study level
1920        self.browser.getLink("Study Course").click()
1921        self.browser.getLink("Add course list").click()
1922        self.assertMatches('...Add current level 100 (Year 1)...',
1923                           self.browser.contents)
1924        self.browser.getControl("Create course list now").click()
1925        # A level with one course ticket was created
1926        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1927        self.browser.getLink("100").click()
1928        self.browser.getLink("Edit course list").click()
1929        self.browser.getControl("Add course ticket").click()
1930        self.browser.getControl(name="form.course").value = ['COURSE1']
1931        self.browser.getControl("Add course ticket").click()
1932        self.assertMatches('...The ticket exists...',
1933                           self.browser.contents)
1934        self.student['studycourse'].current_level = 200
1935        self.browser.getLink("Study Course").click()
1936        self.browser.getLink("Add course list").click()
1937        self.assertMatches('...Add current level 200 (Year 2)...',
1938                           self.browser.contents)
1939        self.browser.getControl("Create course list now").click()
1940        self.browser.getLink("200").click()
1941        self.browser.getLink("Edit course list").click()
1942        self.browser.getControl("Add course ticket").click()
1943        self.browser.getControl(name="form.course").value = ['COURSE1']
1944        self.course.credits = 100
1945        self.browser.getControl("Add course ticket").click()
1946        self.assertMatches(
1947            '...Your total credits exceed 58...', self.browser.contents)
1948        self.course.credits = 10
1949        self.browser.getControl("Add course ticket").click()
1950        self.assertMatches('...The ticket exists...',
1951                           self.browser.contents)
1952        # Indeed the ticket exists as carry-over course from level 100
1953        # since its score was 0
1954        self.assertTrue(
1955            self.student['studycourse']['200']['COURSE1'].carry_over is True)
1956        # Students can open the pdf course registration slip
1957        self.browser.open(self.student_path + '/studycourse/200')
1958        self.browser.getLink("Download course registration slip").click()
1959        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1960        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1961        # Students can remove course tickets
1962        self.browser.open(self.student_path + '/studycourse/200/edit')
1963        self.browser.getControl("Remove selected", index=0).click()
1964        self.assertTrue('No ticket selected' in self.browser.contents)
1965        # No ticket can be selected since the carry-over course is a core course
1966        self.assertRaises(
1967            LookupError, self.browser.getControl, name='val_id')
1968        self.student['studycourse']['200']['COURSE1'].mandatory = False
1969        self.browser.open(self.student_path + '/studycourse/200/edit')
1970        # Course list can't be registered if total_credits exceeds max_credits
1971        self.student['studycourse']['200']['COURSE1'].credits = 60
1972        self.browser.getControl("Register course list").click()
1973        self.assertTrue('Maximum credits of 50 exceeded' in self.browser.contents)
1974        # Student can now remove the ticket
1975        ctrl = self.browser.getControl(name='val_id')
1976        ctrl.getControl(value='COURSE1').selected = True
1977        self.browser.getControl("Remove selected", index=0).click()
1978        self.assertTrue('Successfully removed' in self.browser.contents)
1979        # Course list can be registered, even if it's empty
1980        self.browser.getControl("Register course list").click()
1981        self.assertTrue('Course list has been registered' in self.browser.contents)
1982        self.assertEqual(self.student.state, 'courses registered')
1983        return
1984
1985    def test_postgraduate_student_access(self):
1986        self.certificate.study_mode = 'pg_ft'
1987        self.certificate.start_level = 999
1988        self.certificate.end_level = 999
1989        self.student['studycourse'].current_level = 999
1990        IWorkflowState(self.student).setState('school fee paid')
1991        self.browser.open(self.login_path)
1992        self.browser.getControl(name="form.login").value = self.student_id
1993        self.browser.getControl(name="form.password").value = 'spwd'
1994        self.browser.getControl("Login").click()
1995        self.assertTrue(
1996            'You logged in.' in self.browser.contents)
1997        # Now students can add the current study level
1998        self.browser.getLink("Study Course").click()
1999        self.browser.getLink("Add course list").click()
2000        self.assertMatches('...Add current level Postgraduate Level...',
2001                           self.browser.contents)
2002        self.browser.getControl("Create course list now").click()
2003        # A level with one course ticket was created
2004        self.assertEqual(self.student['studycourse']['999'].number_of_tickets, 0)
2005        self.browser.getLink("999").click()
2006        self.browser.getLink("Edit course list").click()
2007        self.browser.getControl("Add course ticket").click()
2008        self.browser.getControl(name="form.course").value = ['COURSE1']
2009        self.browser.getControl("Add course ticket").click()
2010        self.assertMatches('...Successfully added COURSE1...',
2011                           self.browser.contents)
2012        # Postgraduate students can't register course lists
2013        self.browser.getControl("Register course list").click()
2014        self.assertTrue("your course list can't bee registered"
2015            in self.browser.contents)
2016        self.assertEqual(self.student.state, 'school fee paid')
2017        return
2018
2019    def test_student_clearance_wo_clrcode(self):
2020        IWorkflowState(self.student).setState('clearance started')
2021        self.browser.open(self.login_path)
2022        self.browser.getControl(name="form.login").value = self.student_id
2023        self.browser.getControl(name="form.password").value = 'spwd'
2024        self.browser.getControl("Login").click()
2025        self.student.clearance_locked = False
2026        self.browser.open(self.edit_clearance_path)
2027        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
2028        self.browser.getControl("Save and request clearance").click()
2029        self.assertMatches('...Clearance has been requested...',
2030                           self.browser.contents)
2031
2032    def test_student_clearance_payment(self):
2033        # Login
2034        self.browser.open(self.login_path)
2035        self.browser.getControl(name="form.login").value = self.student_id
2036        self.browser.getControl(name="form.password").value = 'spwd'
2037        self.browser.getControl("Login").click()
2038
2039        # Students can add online clearance payment tickets
2040        self.browser.open(self.payments_path + '/addop')
2041        self.browser.getControl(name="form.p_category").value = ['clearance']
2042        self.browser.getControl("Create ticket").click()
2043        self.assertMatches('...ticket created...',
2044                           self.browser.contents)
2045
2046        # Students can't approve the payment
2047        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
2048        ctrl = self.browser.getControl(name='val_id')
2049        value = ctrl.options[0]
2050        self.browser.getLink(value).click()
2051        payment_url = self.browser.url
2052        self.assertRaises(
2053            Unauthorized, self.browser.open, payment_url + '/approve')
2054        # In the base package they can 'use' a fake approval view
2055        self.browser.open(payment_url + '/fake_approve')
2056        self.assertMatches('...Payment approved...',
2057                          self.browser.contents)
2058        expected = '''...
2059        <td>
2060          <span>Paid</span>
2061        </td>...'''
2062        expected = '''...
2063        <td>
2064          <span>Paid</span>
2065        </td>...'''
2066        self.assertMatches(expected,self.browser.contents)
2067        payment_id = self.student['payments'].keys()[0]
2068        payment = self.student['payments'][payment_id]
2069        self.assertEqual(payment.p_state, 'paid')
2070        self.assertEqual(payment.r_amount_approved, 3456.0)
2071        self.assertEqual(payment.r_code, 'AP')
2072        self.assertEqual(payment.r_desc, u'Payment approved by Anna Tester')
2073        # The new CLR-0 pin has been created
2074        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
2075        pin = self.app['accesscodes']['CLR-0'].keys()[0]
2076        ac = self.app['accesscodes']['CLR-0'][pin]
2077        self.assertEqual(ac.owner, self.student_id)
2078        self.assertEqual(ac.cost, 3456.0)
2079
2080        # Students can open the pdf payment slip
2081        self.browser.open(payment_url + '/payment_slip.pdf')
2082        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2083        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2084
2085        # The new CLR-0 pin can be used for starting clearance
2086        # but they have to upload a passport picture first
2087        # which is only possible in state admitted
2088        self.browser.open(self.student_path + '/change_portrait')
2089        self.assertMatches('...form is locked...',
2090                          self.browser.contents)
2091        IWorkflowInfo(self.student).fireTransition('admit')
2092        self.browser.open(self.student_path + '/change_portrait')
2093        image = open(SAMPLE_IMAGE, 'rb')
2094        ctrl = self.browser.getControl(name='passportuploadedit')
2095        file_ctrl = ctrl.mech_control
2096        file_ctrl.add_file(image, filename='my_photo.jpg')
2097        self.browser.getControl(
2098            name='upload_passportuploadedit').click()
2099        self.browser.open(self.student_path + '/start_clearance')
2100        parts = pin.split('-')[1:]
2101        clrseries, clrnumber = parts
2102        self.browser.getControl(name="ac_series").value = clrseries
2103        self.browser.getControl(name="ac_number").value = clrnumber
2104        self.browser.getControl("Start clearance now").click()
2105        self.assertMatches('...Clearance process has been started...',
2106                           self.browser.contents)
2107
2108    def test_student_schoolfee_payment(self):
2109        configuration = createObject('waeup.SessionConfiguration')
2110        configuration.academic_session = 2005
2111        self.app['configuration'].addSessionConfiguration(configuration)
2112        # Login
2113        self.browser.open(self.login_path)
2114        self.browser.getControl(name="form.login").value = self.student_id
2115        self.browser.getControl(name="form.password").value = 'spwd'
2116        self.browser.getControl("Login").click()
2117
2118        # Students can add online school fee payment tickets.
2119        IWorkflowState(self.student).setState('returning')
2120        self.browser.open(self.payments_path)
2121        self.assertRaises(
2122            LookupError, self.browser.getControl, name='val_id')
2123        self.browser.getLink("Add current session payment ticket").click()
2124        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2125        self.browser.getControl("Create ticket").click()
2126        self.assertMatches('...ticket created...',
2127                           self.browser.contents)
2128        ctrl = self.browser.getControl(name='val_id')
2129        value = ctrl.options[0]
2130        self.browser.getLink(value).click()
2131        self.assertMatches('...Amount Authorized...',
2132                           self.browser.contents)
2133        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2134        # Payment session and will be calculated as defined
2135        # in w.k.students.utils because we set changed the state
2136        # to returning
2137        self.assertEqual(self.student['payments'][value].p_session, 2005)
2138        self.assertEqual(self.student['payments'][value].p_level, 200)
2139
2140        # Student is the payee of the payment ticket.
2141        webservice = IPaymentWebservice(self.student['payments'][value])
2142        self.assertEqual(webservice.display_fullname, 'Anna Tester')
2143        self.assertEqual(webservice.id, self.student_id)
2144        self.assertEqual(webservice.faculty, 'fac1')
2145        self.assertEqual(webservice.department, 'dep1')
2146
2147        # We simulate the approval
2148        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2149        self.browser.open(self.browser.url + '/fake_approve')
2150        self.assertMatches('...Payment approved...',
2151                          self.browser.contents)
2152
2153        # The new SFE-0 pin can be used for starting new session
2154        self.browser.open(self.studycourse_path)
2155        self.browser.getLink('Start new session').click()
2156        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2157        parts = pin.split('-')[1:]
2158        sfeseries, sfenumber = parts
2159        self.browser.getControl(name="ac_series").value = sfeseries
2160        self.browser.getControl(name="ac_number").value = sfenumber
2161        self.browser.getControl("Start now").click()
2162        self.assertMatches('...Session started...',
2163                           self.browser.contents)
2164        self.assertTrue(self.student.state == 'school fee paid')
2165        return
2166
2167    def test_student_bedallocation_payment(self):
2168        # Login
2169        self.browser.open(self.login_path)
2170        self.browser.getControl(name="form.login").value = self.student_id
2171        self.browser.getControl(name="form.password").value = 'spwd'
2172        self.browser.getControl("Login").click()
2173        self.browser.open(self.payments_path)
2174        self.browser.open(self.payments_path + '/addop')
2175        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2176        self.browser.getControl("Create ticket").click()
2177        self.assertMatches('...ticket created...',
2178                           self.browser.contents)
2179        # Students can remove only online payment tickets which have
2180        # not received a valid callback
2181        self.browser.open(self.payments_path)
2182        ctrl = self.browser.getControl(name='val_id')
2183        value = ctrl.options[0]
2184        ctrl.getControl(value=value).selected = True
2185        self.browser.getControl("Remove selected", index=0).click()
2186        self.assertTrue('Successfully removed' in self.browser.contents)
2187
2188    def test_student_maintenance_payment(self):
2189        # Login
2190        self.browser.open(self.login_path)
2191        self.browser.getControl(name="form.login").value = self.student_id
2192        self.browser.getControl(name="form.password").value = 'spwd'
2193        self.browser.getControl("Login").click()
2194        self.browser.open(self.payments_path)
2195        self.browser.open(self.payments_path + '/addop')
2196        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2197        self.browser.getControl("Create ticket").click()
2198        self.assertMatches('...You have not yet booked accommodation...',
2199                           self.browser.contents)
2200        # We continue this test in test_student_accommodation
2201
2202    def test_student_previous_payments(self):
2203        configuration = createObject('waeup.SessionConfiguration')
2204        configuration.academic_session = 2000
2205        configuration.clearance_fee = 3456.0
2206        configuration.booking_fee = 123.4
2207        self.app['configuration'].addSessionConfiguration(configuration)
2208        configuration2 = createObject('waeup.SessionConfiguration')
2209        configuration2.academic_session = 2003
2210        configuration2.clearance_fee = 3456.0
2211        configuration2.booking_fee = 123.4
2212        self.app['configuration'].addSessionConfiguration(configuration2)
2213        configuration3 = createObject('waeup.SessionConfiguration')
2214        configuration3.academic_session = 2005
2215        configuration3.clearance_fee = 3456.0
2216        configuration3.booking_fee = 123.4
2217        self.app['configuration'].addSessionConfiguration(configuration3)
2218        self.student['studycourse'].entry_session = 2002
2219
2220        # Login
2221        self.browser.open(self.login_path)
2222        self.browser.getControl(name="form.login").value = self.student_id
2223        self.browser.getControl(name="form.password").value = 'spwd'
2224        self.browser.getControl("Login").click()
2225
2226        # Students can add previous school fee payment tickets in any state.
2227        IWorkflowState(self.student).setState('courses registered')
2228        self.browser.open(self.payments_path)
2229        self.browser.getLink("Add previous session payment ticket").click()
2230
2231        # Previous session payment form is provided
2232        self.assertEqual(self.student.current_session, 2004)
2233        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2234        self.browser.getControl(name="form.p_session").value = ['2000']
2235        self.browser.getControl(name="form.p_level").value = ['300']
2236        self.browser.getControl("Create ticket").click()
2237        self.assertMatches('...The previous session must not fall below...',
2238                           self.browser.contents)
2239        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2240        self.browser.getControl(name="form.p_session").value = ['2005']
2241        self.browser.getControl(name="form.p_level").value = ['300']
2242        self.browser.getControl("Create ticket").click()
2243        self.assertMatches('...This is not a previous session...',
2244                           self.browser.contents)
2245        # Students can pay current session school fee.
2246        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2247        self.browser.getControl(name="form.p_session").value = ['2004']
2248        self.browser.getControl(name="form.p_level").value = ['300']
2249        self.browser.getControl("Create ticket").click()
2250        self.assertMatches('...ticket created...',
2251                           self.browser.contents)
2252        ctrl = self.browser.getControl(name='val_id')
2253        value = ctrl.options[0]
2254        self.browser.getLink(value).click()
2255        self.assertMatches('...Amount Authorized...',
2256                           self.browser.contents)
2257        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2258
2259        # Payment session is properly set
2260        self.assertEqual(self.student['payments'][value].p_session, 2004)
2261        self.assertEqual(self.student['payments'][value].p_level, 300)
2262
2263        # We simulate the approval
2264        self.browser.open(self.browser.url + '/fake_approve')
2265        self.assertMatches('...Payment approved...',
2266                          self.browser.contents)
2267
2268        # No AC has been created
2269        self.assertEqual(len(self.app['accesscodes']['SFE-0'].keys()), 0)
2270        self.assertTrue(self.student['payments'][value].ac is None)
2271
2272        # Current payment flag is set False
2273        self.assertFalse(self.student['payments'][value].p_current)
2274
2275        # Button and form are not available for students who are in
2276        # states up to cleared
2277        self.student['studycourse'].entry_session = 2004
2278        IWorkflowState(self.student).setState('cleared')
2279        self.browser.open(self.payments_path)
2280        self.assertFalse(
2281            "Add previous session payment ticket" in self.browser.contents)
2282        self.browser.open(self.payments_path + '/addpp')
2283        self.assertTrue(
2284            "No previous payment to be made" in self.browser.contents)
2285        return
2286
2287    def test_postgraduate_student_payments(self):
2288        configuration = createObject('waeup.SessionConfiguration')
2289        configuration.academic_session = 2005
2290        self.app['configuration'].addSessionConfiguration(configuration)
2291        self.certificate.study_mode = 'pg_ft'
2292        self.certificate.start_level = 999
2293        self.certificate.end_level = 999
2294        self.student['studycourse'].current_level = 999
2295        # Login
2296        self.browser.open(self.login_path)
2297        self.browser.getControl(name="form.login").value = self.student_id
2298        self.browser.getControl(name="form.password").value = 'spwd'
2299        self.browser.getControl("Login").click()
2300        # Students can add online school fee payment tickets.
2301        IWorkflowState(self.student).setState('cleared')
2302        self.browser.open(self.payments_path)
2303        self.browser.getLink("Add current session payment ticket").click()
2304        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2305        self.browser.getControl("Create ticket").click()
2306        self.assertMatches('...ticket created...',
2307                           self.browser.contents)
2308        ctrl = self.browser.getControl(name='val_id')
2309        value = ctrl.options[0]
2310        self.browser.getLink(value).click()
2311        self.assertMatches('...Amount Authorized...',
2312                           self.browser.contents)
2313        # Payment session and level are current ones.
2314        # Postgrads have to pay school_fee_1.
2315        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
2316        self.assertEqual(self.student['payments'][value].p_session, 2004)
2317        self.assertEqual(self.student['payments'][value].p_level, 999)
2318
2319        # We simulate the approval
2320        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2321        self.browser.open(self.browser.url + '/fake_approve')
2322        self.assertMatches('...Payment approved...',
2323                          self.browser.contents)
2324
2325        # The new SFE-0 pin can be used for starting session
2326        self.browser.open(self.studycourse_path)
2327        self.browser.getLink('Start new session').click()
2328        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2329        parts = pin.split('-')[1:]
2330        sfeseries, sfenumber = parts
2331        self.browser.getControl(name="ac_series").value = sfeseries
2332        self.browser.getControl(name="ac_number").value = sfenumber
2333        self.browser.getControl("Start now").click()
2334        self.assertMatches('...Session started...',
2335                           self.browser.contents)
2336        self.assertTrue(self.student.state == 'school fee paid')
2337
2338        # Postgrad students do not need to register courses the
2339        # can just pay for the next session.
2340        self.browser.open(self.payments_path)
2341        # Remove first payment to be sure that we access the right ticket
2342        del self.student['payments'][value]
2343        self.browser.getLink("Add current session payment ticket").click()
2344        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2345        self.browser.getControl("Create ticket").click()
2346        ctrl = self.browser.getControl(name='val_id')
2347        value = ctrl.options[0]
2348        self.browser.getLink(value).click()
2349        # Payment session has increased by one, payment level remains the same.
2350        # Returning Postgraduates have to pay school_fee_2.
2351        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2352        self.assertEqual(self.student['payments'][value].p_session, 2005)
2353        self.assertEqual(self.student['payments'][value].p_level, 999)
2354
2355        # Student is still in old session
2356        self.assertEqual(self.student.current_session, 2004)
2357
2358        # We do not need to pay the ticket if any other
2359        # SFE pin is provided
2360        pin_container = self.app['accesscodes']
2361        pin_container.createBatch(
2362            datetime.utcnow(), 'some_userid', 'SFE', 9.99, 1)
2363        pin = pin_container['SFE-1'].values()[0].representation
2364        sfeseries, sfenumber = pin.split('-')[1:]
2365        # The new SFE-1 pin can be used for starting new session
2366        self.browser.open(self.studycourse_path)
2367        self.browser.getLink('Start new session').click()
2368        self.browser.getControl(name="ac_series").value = sfeseries
2369        self.browser.getControl(name="ac_number").value = sfenumber
2370        self.browser.getControl("Start now").click()
2371        self.assertMatches('...Session started...',
2372                           self.browser.contents)
2373        self.assertTrue(self.student.state == 'school fee paid')
2374        # Student is in new session
2375        self.assertEqual(self.student.current_session, 2005)
2376        self.assertEqual(self.student['studycourse'].current_level, 999)
2377        return
2378
2379    def test_student_accommodation(self):
2380        # Login
2381        self.browser.open(self.login_path)
2382        self.browser.getControl(name="form.login").value = self.student_id
2383        self.browser.getControl(name="form.password").value = 'spwd'
2384        self.browser.getControl("Login").click()
2385
2386        # Students can add online booking fee payment tickets and open the
2387        # callback view (see test_manage_payments)
2388        self.browser.getLink("Payments").click()
2389        self.browser.getLink("Add current session payment ticket").click()
2390        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2391        self.browser.getControl("Create ticket").click()
2392        ctrl = self.browser.getControl(name='val_id')
2393        value = ctrl.options[0]
2394        self.browser.getLink(value).click()
2395        self.browser.open(self.browser.url + '/fake_approve')
2396        # The new HOS-0 pin has been created
2397        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
2398        pin = self.app['accesscodes']['HOS-0'].keys()[0]
2399        ac = self.app['accesscodes']['HOS-0'][pin]
2400        parts = pin.split('-')[1:]
2401        sfeseries, sfenumber = parts
2402
2403        # Students can use HOS code and book a bed space with it ...
2404        self.browser.open(self.acco_path)
2405        # ... but not if booking period has expired ...
2406        self.app['hostels'].enddate = datetime.now(pytz.utc)
2407        self.browser.getLink("Book accommodation").click()
2408        self.assertMatches('...Outside booking period: ...',
2409                           self.browser.contents)
2410        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
2411        # ... or student is not the an allowed state ...
2412        self.browser.getLink("Book accommodation").click()
2413        self.assertMatches('...You are in the wrong...',
2414                           self.browser.contents)
2415        IWorkflowInfo(self.student).fireTransition('admit')
2416        self.browser.getLink("Book accommodation").click()
2417        self.assertMatches('...Activation Code:...',
2418                           self.browser.contents)
2419        # Student can't used faked ACs ...
2420        self.browser.getControl(name="ac_series").value = u'nonsense'
2421        self.browser.getControl(name="ac_number").value = sfenumber
2422        self.browser.getControl("Create bed ticket").click()
2423        self.assertMatches('...Activation code is invalid...',
2424                           self.browser.contents)
2425        # ... or ACs owned by somebody else.
2426        ac.owner = u'Anybody'
2427        self.browser.getControl(name="ac_series").value = sfeseries
2428        self.browser.getControl(name="ac_number").value = sfenumber
2429        self.browser.getControl("Create bed ticket").click()
2430        self.assertMatches('...You are not the owner of this access code...',
2431                           self.browser.contents)
2432        ac.owner = self.student_id
2433        self.browser.getControl(name="ac_series").value = sfeseries
2434        self.browser.getControl(name="ac_number").value = sfenumber
2435        self.browser.getControl("Create bed ticket").click()
2436        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2437                           self.browser.contents)
2438
2439        # Bed has been allocated
2440        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
2441        self.assertTrue(bed.owner == self.student_id)
2442
2443        # BedTicketAddPage is now blocked
2444        self.browser.getLink("Book accommodation").click()
2445        self.assertMatches('...You already booked a bed space...',
2446            self.browser.contents)
2447
2448        # The bed ticket displays the data correctly
2449        self.browser.open(self.acco_path + '/2004')
2450        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2451                           self.browser.contents)
2452        self.assertMatches('...2004/2005...', self.browser.contents)
2453        self.assertMatches('...regular_male_fr...', self.browser.contents)
2454        self.assertMatches('...%s...' % pin, self.browser.contents)
2455
2456        # Students can open the pdf slip
2457        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
2458        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2459        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2460
2461        # Students can't relocate themselves
2462        self.assertFalse('Relocate' in self.browser.contents)
2463        relocate_path = self.acco_path + '/2004/relocate'
2464        self.assertRaises(
2465            Unauthorized, self.browser.open, relocate_path)
2466
2467        # Students can't the Remove button and check boxes
2468        self.browser.open(self.acco_path)
2469        self.assertFalse('Remove' in self.browser.contents)
2470        self.assertFalse('val_id' in self.browser.contents)
2471
2472        # Students can pay maintenance fee now
2473        self.browser.open(self.payments_path)
2474        self.browser.open(self.payments_path + '/addop')
2475        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2476        self.browser.getControl("Create ticket").click()
2477        self.assertMatches('...Payment ticket created...',
2478                           self.browser.contents)
2479        return
2480
2481    def test_change_password_request(self):
2482        self.browser.open('http://localhost/app/changepw')
2483        self.browser.getControl(name="form.identifier").value = '123'
2484        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
2485        self.browser.getControl("Send login credentials").click()
2486        self.assertTrue('An email with' in self.browser.contents)
2487
2488    def test_student_expired_personal_data(self):
2489        # Login
2490        IWorkflowState(self.student).setState('school fee paid')
2491        delta = timedelta(days=180)
2492        self.student.personal_updated = datetime.utcnow() - delta
2493        self.browser.open(self.login_path)
2494        self.browser.getControl(name="form.login").value = self.student_id
2495        self.browser.getControl(name="form.password").value = 'spwd'
2496        self.browser.getControl("Login").click()
2497        self.assertEqual(self.browser.url, self.student_path)
2498        self.assertTrue(
2499            'You logged in' in self.browser.contents)
2500        # Students don't see personal_updated field in edit form
2501        self.browser.open(self.edit_personal_path)
2502        self.assertFalse('Updated' in self.browser.contents)
2503        self.browser.open(self.personal_path)
2504        self.assertTrue('Updated' in self.browser.contents)
2505        self.browser.getLink("Logout").click()
2506        delta = timedelta(days=181)
2507        self.student.personal_updated = datetime.utcnow() - delta
2508        self.browser.open(self.login_path)
2509        self.browser.getControl(name="form.login").value = self.student_id
2510        self.browser.getControl(name="form.password").value = 'spwd'
2511        self.browser.getControl("Login").click()
2512        self.assertEqual(self.browser.url, self.edit_personal_path)
2513        self.assertTrue(
2514            'Your personal data record is outdated.' in self.browser.contents)
2515
2516class StudentRequestPWTests(StudentsFullSetup):
2517    # Tests for student registration
2518
2519    layer = FunctionalLayer
2520
2521    def test_request_pw(self):
2522        # Student with wrong number can't be found.
2523        self.browser.open('http://localhost/app/requestpw')
2524        self.browser.getControl(name="form.firstname").value = 'Anna'
2525        self.browser.getControl(name="form.number").value = 'anynumber'
2526        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2527        self.browser.getControl("Send login credentials").click()
2528        self.assertTrue('No student record found.'
2529            in self.browser.contents)
2530        # Anonymous is not informed that firstname verification failed.
2531        # It seems that the record doesn't exist.
2532        self.browser.open('http://localhost/app/requestpw')
2533        self.browser.getControl(name="form.firstname").value = 'Johnny'
2534        self.browser.getControl(name="form.number").value = '123'
2535        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2536        self.browser.getControl("Send login credentials").click()
2537        self.assertTrue('No student record found.'
2538            in self.browser.contents)
2539        # Even with the correct firstname we can't register if a
2540        # password has been set and used.
2541        self.browser.getControl(name="form.firstname").value = 'Anna'
2542        self.browser.getControl(name="form.number").value = '123'
2543        self.browser.getControl("Send login credentials").click()
2544        self.assertTrue('Your password has already been set and used.'
2545            in self.browser.contents)
2546        self.browser.open('http://localhost/app/requestpw')
2547        self.app['students'][self.student_id].password = None
2548        # The firstname field, used for verification, is not case-sensitive.
2549        self.browser.getControl(name="form.firstname").value = 'aNNa'
2550        self.browser.getControl(name="form.number").value = '123'
2551        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2552        self.browser.getControl("Send login credentials").click()
2553        # Yeah, we succeded ...
2554        self.assertTrue('Your password request was successful.'
2555            in self.browser.contents)
2556        # We can also use the matric_number instead.
2557        self.browser.open('http://localhost/app/requestpw')
2558        self.browser.getControl(name="form.firstname").value = 'aNNa'
2559        self.browser.getControl(name="form.number").value = '234'
2560        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2561        self.browser.getControl("Send login credentials").click()
2562        self.assertTrue('Your password request was successful.'
2563            in self.browser.contents)
2564        # ... and  student can be found in the catalog via the email address
2565        cat = queryUtility(ICatalog, name='students_catalog')
2566        results = list(
2567            cat.searchResults(
2568            email=('new@yy.zz', 'new@yy.zz')))
2569        self.assertEqual(self.student,results[0])
2570        logfile = os.path.join(
2571            self.app['datacenter'].storage, 'logs', 'main.log')
2572        logcontent = open(logfile).read()
2573        self.assertTrue('zope.anybody - students.browser.StudentRequestPasswordPage - '
2574                        '234 (K1000000) - new@yy.zz' in logcontent)
2575        return
2576
2577    def test_student_locked_level_forms(self):
2578
2579        # Add two study levels, one current and one previous
2580        studylevel = createObject(u'waeup.StudentStudyLevel')
2581        studylevel.level = 100
2582        self.student['studycourse'].addStudentStudyLevel(
2583            self.certificate, studylevel)
2584        studylevel = createObject(u'waeup.StudentStudyLevel')
2585        studylevel.level = 200
2586        self.student['studycourse'].addStudentStudyLevel(
2587            self.certificate, studylevel)
2588        IWorkflowState(self.student).setState('school fee paid')
2589        self.student['studycourse'].current_level = 200
2590
2591        self.browser.open(self.login_path)
2592        self.browser.getControl(name="form.login").value = self.student_id
2593        self.browser.getControl(name="form.password").value = 'spwd'
2594        self.browser.getControl("Login").click()
2595
2596        self.browser.open(self.student_path + '/studycourse/200/edit')
2597        self.assertFalse('The requested form is locked' in self.browser.contents)
2598        self.browser.open(self.student_path + '/studycourse/100/edit')
2599        self.assertTrue('The requested form is locked' in self.browser.contents)
2600
2601        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2602        self.assertFalse('The requested form is locked' in self.browser.contents)
2603        self.browser.open(self.student_path + '/studycourse/100/ctadd')
2604        self.assertTrue('The requested form is locked' in self.browser.contents)
2605
2606        IWorkflowState(self.student).setState('courses registered')
2607        self.browser.open(self.student_path + '/studycourse/200/edit')
2608        self.assertTrue('The requested form is locked' in self.browser.contents)
2609        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2610        self.assertTrue('The requested form is locked' in self.browser.contents)
2611
2612
2613class PublicPagesTests(StudentsFullSetup):
2614    # Tests for swebservices
2615
2616    layer = FunctionalLayer
2617
2618    def test_paymentrequest(self):
2619        payment = createObject('waeup.StudentOnlinePayment')
2620        payment.p_category = u'schoolfee'
2621        payment.p_session = self.student.current_session
2622        payment.p_item = u'My Certificate'
2623        payment.p_id = u'anyid'
2624        self.student['payments']['anykey'] = payment
2625        # Request information about unpaid payment ticket
2626        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2627        self.assertEqual(self.browser.contents, '-1')
2628        # Request information about paid payment ticket
2629        payment.p_state = u'paid'
2630        notify(grok.ObjectModifiedEvent(payment))
2631        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2632        self.assertEqual(self.browser.contents,
2633            'FULL_NAME=Anna Tester&FACULTY=fac1&DEPARTMENT=dep1'
2634            '&PAYMENT_ITEM=My Certificate&PAYMENT_CATEGORY=School Fee'
2635            '&ACADEMIC_SESSION=2004/2005&MATRIC_NUMBER=234&FEE_AMOUNT=0.0')
2636        self.browser.open('http://localhost/app/paymentrequest?NONSENSE=nonsense')
2637        self.assertEqual(self.browser.contents, '-1')
2638        self.browser.open('http://localhost/app/paymentrequest?P_ID=nonsense')
2639        self.assertEqual(self.browser.contents, '-1')
Note: See TracBrowser for help on using the repository browser.