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

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

Rework pagetemplates.

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