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

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

Add test for previous revision.

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