source: main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py @ 6801

Last change on this file since 6801 was 6801, checked in by Henrik Bettermann, 13 years ago

Complete student workflow.

  • Property svn:keywords set to Id
File size: 25.2 KB
Line 
1##
2## test_browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Tue Mar 29 11:31:11 2011 Uli Fouquet
5## $Id: test_browser.py 6801 2011-09-20 06:07:14Z henrik $
6##
7## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""
23Test the student-related UI components.
24"""
25import shutil
26import tempfile
27from datetime import datetime
28from zope.component import createObject
29from zope.component.hooks import setSite, clearSite
30from zope.security.interfaces import Unauthorized
31from zope.testbrowser.testing import Browser
32from hurry.workflow.interfaces import IWorkflowInfo
33from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
34from waeup.sirp.app import University
35from waeup.sirp.students.student import Student
36from waeup.sirp.university.faculty import Faculty
37from waeup.sirp.university.department import Department
38from waeup.sirp.interfaces import IUserAccount
39
40PH_LEN = 2059  # Length of placeholder file
41
42class StudentsFullSetup(FunctionalTestCase):
43    # A test case that only contains a setup and teardown
44    #
45    # Complete setup for students handlings is rather complex and
46    # requires lots of things created before we can start. This is a
47    # setup that does all this, creates a university, creates PINs,
48    # etc.  so that we do not have to bother with that in different
49    # test cases.
50
51    layer = FunctionalLayer
52
53    def setUp(self):
54        super(StudentsFullSetup, self).setUp()
55
56        # Setup a sample site for each test
57        app = University()
58        self.dc_root = tempfile.mkdtemp()
59        app['datacenter'].setStoragePath(self.dc_root)
60
61        # Prepopulate the ZODB...
62        self.getRootFolder()['app'] = app
63        # we add the site immediately after creation to the
64        # ZODB. Catalogs and other local utilities are not setup
65        # before that step.
66        self.app = self.getRootFolder()['app']
67        # Set site here. Some of the following setup code might need
68        # to access grok.getSite() and should get our new app then
69        setSite(app)
70
71        # Add student with subobjects (done by addStudent)
72        student = Student()
73        student.name = u'Anna Tester'
74        student.reg_number = u'123'
75        student.matric_number = u'234'
76        self.test_student_id = self.app['students'].addStudent(student)
77        # Set password
78        IUserAccount(
79            self.app['students'][self.test_student_id]).setPassword('spwd')
80
81        self.login_path = 'http://localhost/app/login'
82        self.container_path = 'http://localhost/app/students'
83        self.manage_container_path = self.container_path + '/@@manage'
84        self.add_student_path = self.container_path + '/addstudent'
85        self.student_path = self.container_path + '/' + self.test_student_id
86        self.manage_student_path = self.student_path + '/edit_base'
87        self.clearance_student_path = self.student_path + '/view_clearance'
88        self.personal_student_path = self.student_path + '/view_personal'
89        self.edit_clearance_student_path = self.student_path + '/edit_clearance'
90        self.edit_personal_student_path = self.student_path + '/edit_personal'
91
92        self.studycourse_student_path = self.student_path + '/studycourse'
93        self.payments_student_path = self.student_path + '/payments'
94        self.accommodation_student_path = self.student_path + '/accommodation'
95        self.history_student_path = self.student_path + '/history'
96
97        # Create 5 access codes with prefix'PWD'
98        pin_container = self.app['accesscodes']
99        pin_container.createBatch(
100            datetime.now(), 'some_userid', 'PWD', 9.99, 5)
101        pins = pin_container[pin_container.keys()[0]].values()
102        self.pwdpins = [x.representation for x in pins]
103        self.existing_pwdpin = self.pwdpins[0]
104        parts = self.existing_pwdpin.split('-')[1:]
105        self.existing_pwdseries, self.existing_pwdnumber = parts
106        # Create 5 access codes with prefix 'CLR'
107        pin_container.createBatch(
108            datetime.now(), 'some_userid', 'CLR', 9.99, 5)
109        pins = pin_container[pin_container.keys()[0]].values()
110        self.clrpins = [x.representation for x in pins]
111        self.existing_clrpin = self.clrpins[0]
112        self.existing_clrpin
113        parts = self.existing_clrpin.split('-')[1:]
114        self.existing_clrseries, self.existing_clrnumber = parts
115
116        # Populate university
117        self.certificate = createObject('waeup.Certificate')
118        self.certificate.code = 'CERT1'
119        self.certificate.application_category = 'basic'
120        self.certificate.start_level = 100
121        self.certificate.end_level = 500
122        self.app['faculties']['fac1'] = Faculty()
123        self.app['faculties']['fac1']['dep1'] = Department()
124        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
125            self.certificate)
126        self.course = createObject('waeup.Course')
127        self.course.code = 'COURSE1'
128        self.course.semester = 1
129        self.course.credits = 10
130        self.course.passmark = 40
131        self.app['faculties']['fac1']['dep1'].courses.addCourse(
132            self.course)
133        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCourseRef(
134            self.course, level=100)
135
136        # Put the prepopulated site into test ZODB and prepare test
137        # browser
138        self.browser = Browser()
139        self.browser.handleErrors = False
140
141    def tearDown(self):
142        super(StudentsFullSetup, self).tearDown()
143        clearSite()
144        shutil.rmtree(self.dc_root)
145
146
147
148class StudentsContainerUITests(StudentsFullSetup):
149    # Tests for StudentsContainer class views and pages
150
151    layer = FunctionalLayer
152
153    def test_anonymous_access(self):
154        # Anonymous users can't access students containers
155        self.assertRaises(
156            Unauthorized, self.browser.open, self.container_path)
157        self.assertRaises(
158            Unauthorized, self.browser.open, self.manage_container_path)
159        return
160
161    def test_manage_access(self):
162        # Managers can access the view page of students
163        # containers and can perform actions
164        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
165        self.browser.open(self.container_path)
166        self.assertEqual(self.browser.headers['Status'], '200 Ok')
167        self.assertEqual(self.browser.url, self.container_path)
168        self.browser.getLink("Manage student section").click()
169        self.assertEqual(self.browser.headers['Status'], '200 Ok')
170        self.assertEqual(self.browser.url, self.manage_container_path)
171        return
172
173    def test_add_search_delete_students(self):
174        # Managers can add search and remove students
175        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
176        self.browser.open(self.manage_container_path)
177        self.browser.getLink("Add student").click()
178        self.assertEqual(self.browser.headers['Status'], '200 Ok')
179        self.assertEqual(self.browser.url, self.add_student_path)
180        self.browser.getControl(name="form.name").value = 'Bob Tester'
181        self.browser.getControl("Create student record").click()
182        self.assertTrue('Student record created' in self.browser.contents)
183
184        # Registration and matric numbers must be unique
185        self.browser.getLink("Manage").click()
186        self.browser.getControl(name="form.reg_number").value = '123'
187        self.browser.getControl("Save").click()
188        self.assertMatches('...Registration number exists...',
189                           self.browser.contents)
190        self.browser.getControl(name="form.reg_number").value = '789'
191        self.browser.getControl(name="form.matric_number").value = '234'
192        self.browser.getControl("Save").click()
193        self.assertMatches('...Matriculation number exists...',
194                           self.browser.contents)
195
196        self.browser.open(self.container_path)
197        self.browser.getControl("Search").click()
198        self.assertTrue('Empty search string' in self.browser.contents)
199        self.browser.getControl(name="searchtype").value = ['student_id']
200        self.browser.getControl(name="searchterm").value = self.test_student_id
201        self.browser.getControl("Search").click()
202        self.assertTrue('Anna Tester' in self.browser.contents)
203
204        self.browser.open(self.manage_container_path)
205        self.browser.getControl("Search").click()
206        self.assertTrue('Empty search string' in self.browser.contents)
207        self.browser.getControl(name="searchtype").value = ['name']
208        self.browser.getControl(name="searchterm").value = 'Anna Tester'
209        self.browser.getControl("Search").click()
210        self.assertTrue('Anna Tester' in self.browser.contents)
211        # The old searchterm will be used again
212        self.browser.getControl("Search").click()
213        self.assertTrue('Anna Tester' in self.browser.contents)
214
215        ctrl = self.browser.getControl(name='entries')
216        ctrl.getControl(value=self.test_student_id).selected = True
217        self.browser.getControl("Remove selected", index=0).click()
218        self.assertTrue('Successfully removed' in self.browser.contents)
219        self.browser.getControl(name="searchtype").value = ['student_id']
220        self.browser.getControl(name="searchterm").value = self.test_student_id
221        self.browser.getControl("Search").click()
222        self.assertTrue('No student found' in self.browser.contents)
223
224        self.browser.open(self.container_path)
225        self.browser.getControl(name="searchtype").value = ['student_id']
226        self.browser.getControl(name="searchterm").value = self.test_student_id
227        self.browser.getControl("Search").click()
228        self.assertTrue('No student found' in self.browser.contents)
229        return
230
231class StudentUITests(StudentsFullSetup):
232    # Tests for Student class views and pages
233
234    layer = FunctionalLayer
235
236    def test_manage_access(self):
237        # Managers can access the pages of students
238        # and can perform actions
239        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
240
241        self.browser.open(self.student_path)
242        self.assertEqual(self.browser.headers['Status'], '200 Ok')
243        self.assertEqual(self.browser.url, self.student_path)
244        self.browser.getLink("Manage").click()
245        self.assertEqual(self.browser.headers['Status'], '200 Ok')
246        self.assertEqual(self.browser.url, self.manage_student_path)
247        # Managers can edit base data and fire transitions
248        self.browser.getControl(name="transition").value = ['admit']
249        self.browser.getControl(name="form.name").value = 'John Tester'
250        self.browser.getControl(name="form.reg_number").value = '345'
251        self.browser.getControl(name="password").value = 'secret'
252        self.browser.getControl(name="control_password").value = 'secret'
253        self.browser.getControl("Save").click()
254        self.assertMatches('...Form has been saved...',
255                           self.browser.contents)
256        self.browser.open(self.student_path)
257        self.browser.getLink("Clearance Data").click()
258        self.assertEqual(self.browser.headers['Status'], '200 Ok')
259        self.assertEqual(self.browser.url, self.clearance_student_path)
260        self.browser.getLink("Manage").click()
261        self.assertEqual(self.browser.headers['Status'], '200 Ok')
262        self.assertEqual(self.browser.url, self.edit_clearance_student_path)
263        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
264        self.browser.getControl("Save").click()
265        self.assertMatches('...Form has been saved...',
266                           self.browser.contents)
267
268        self.browser.open(self.student_path)
269        self.browser.getLink("Personal Data").click()
270        self.assertEqual(self.browser.headers['Status'], '200 Ok')
271        self.assertEqual(self.browser.url, self.personal_student_path)
272        self.browser.getLink("Manage").click()
273        self.assertEqual(self.browser.headers['Status'], '200 Ok')
274        self.assertEqual(self.browser.url, self.edit_personal_student_path)
275        self.browser.getControl("Save").click()
276        self.assertMatches('...Form has been saved...',
277                           self.browser.contents)
278
279        # Managers can browse all subobjects
280        self.browser.open(self.student_path)
281        self.browser.getLink("Payments").click()
282        self.assertEqual(self.browser.headers['Status'], '200 Ok')
283        self.assertEqual(self.browser.url, self.payments_student_path)
284        self.browser.open(self.student_path)
285        self.browser.getLink("Accommodation").click()
286        self.assertEqual(self.browser.headers['Status'], '200 Ok')
287        self.assertEqual(self.browser.url, self.accommodation_student_path)
288        self.browser.open(self.student_path)
289        self.browser.getLink("History").click()
290        self.assertEqual(self.browser.headers['Status'], '200 Ok')
291        self.assertEqual(self.browser.url, self.history_student_path)
292        self.assertMatches('...Student admitted by zope.mgr...',
293                           self.browser.contents)
294        return
295
296    def test_manage_course_lists(self):
297        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
298        self.browser.open(self.student_path)
299        self.browser.getLink("Study Course").click()
300        self.assertEqual(self.browser.headers['Status'], '200 Ok')
301        self.assertEqual(self.browser.url, self.studycourse_student_path)
302        self.browser.getLink("Manage").click()
303        self.assertTrue('Manage study course' in self.browser.contents)
304        # Before we can select a level, the certificate must be selected and saved
305        self.browser.getControl(name="form.certificate").value = ['CERT1']
306        self.browser.getControl(name="form.current_session").value = ['2004']
307        self.browser.getControl(name="form.current_verdict").value = ['A']
308        self.browser.getControl("Save").click()
309        # Now we can save also the current level which depends on start and end
310        # level of the certificate
311        self.browser.getControl(name="form.current_level").value = ['100']
312        self.browser.getControl("Save").click()
313        # Managers can add and remove any study level (course list)
314        self.browser.getControl(name="addlevel").value = ['100']
315        self.browser.getControl("Add study level").click()
316        self.assertMatches('...<span>100</span>...', self.browser.contents)
317        self.browser.getControl(name="val_id").value = ['100']
318        self.browser.getControl("Remove selected").click()
319        self.assertMatches('...Successfully removed...', self.browser.contents)
320        # Add again level again
321        self.browser.getControl(name="addlevel").value = ['100']
322        self.browser.getControl("Add study level").click()
323
324        # Managers can view and manage course lists
325        self.browser.getLink("100").click()
326        self.assertMatches('...: Study Level 100 (Year 1)...', self.browser.contents)
327        self.browser.getLink("Manage").click()
328        self.browser.getControl(name="form.level_session").value = ['2002']
329        self.browser.getControl("Save").click()
330        ctrl = self.browser.getControl(name='val_id')
331        ctrl.getControl(value='COURSE1').selected = True
332        self.browser.getControl("Remove selected", index=0).click()
333        self.assertTrue('Successfully removed' in self.browser.contents)
334        self.browser.getControl("Add course ticket").click()
335        self.browser.getControl(name="form.course").value = ['COURSE1']
336        self.browser.getControl("Add course ticket").click()
337        self.assertTrue('Successfully added' in self.browser.contents)
338        self.browser.getLink("COURSE1").click()
339        self.browser.getLink("Manage").click()
340        self.browser.getControl(name="form.score").value = '10'
341        self.browser.getControl("Save").click()
342        self.assertTrue('Form has been saved' in self.browser.contents)
343        return
344
345    def test_manage_workflow(self):
346        # Managers can pass through the whole workflow
347        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
348        student = self.app['students'][self.test_student_id]
349        self.browser.open(self.manage_student_path)
350        self.assertTrue(student.clearance_locked)
351        self.browser.getControl(name="transition").value = ['admit']
352        self.browser.getControl("Save").click()
353        self.assertTrue(student.clearance_locked)
354        self.browser.getControl(name="transition").value = ['start_clearance']
355        self.browser.getControl("Save").click()
356        self.assertFalse(student.clearance_locked)
357        self.browser.getControl(name="transition").value = ['request_clearance']
358        self.browser.getControl("Save").click()
359        self.assertTrue(student.clearance_locked)
360        self.browser.getControl(name="transition").value = ['clear']
361        self.browser.getControl("Save").click()
362        self.browser.getControl(name="transition").value = ['pay_first_school_fee']
363        self.browser.getControl("Save").click()
364        self.browser.getControl(name="transition").value = ['reset6']
365        self.browser.getControl("Save").click()
366        # In state returning the pay_school_fee transition triggers some
367        # changes of attributes
368        student['studycourse'].certificate = self.certificate
369        student['studycourse'].current_session = 2004
370        student['studycourse'].current_verdict = 'A'
371        student['studycourse'].current_level = 100
372        self.browser.getControl(name="transition").value = ['pay_school_fee']
373        self.browser.getControl("Save").click()
374        self.assertEqual(student['studycourse'].current_session, 2005) # +1
375        self.assertEqual(student['studycourse'].current_level, 200) # +100
376        self.assertEqual(student['studycourse'].current_verdict, '') # empty
377        self.assertEqual(student['studycourse'].previous_verdict, 'A')
378        self.browser.getControl(name="transition").value = ['register_courses']
379        self.browser.getControl("Save").click()
380        self.browser.getControl(name="transition").value = ['validate_courses']
381        self.browser.getControl("Save").click()
382        self.browser.getControl(name="transition").value = ['return']
383        self.browser.getControl("Save").click()
384        return
385
386    def test_student_change_password(self):
387        # Students can change the password
388        self.browser.open(self.login_path)
389        self.browser.getControl(name="form.login").value = self.test_student_id
390        self.browser.getControl(name="form.password").value = 'spwd'
391        self.browser.getControl("Login").click()
392        self.assertEqual(self.browser.url, self.student_path)
393        self.assertTrue('You logged in' in self.browser.contents)
394        # Change password
395        self.browser.getLink("Change password").click()
396        self.browser.getControl(name="form.password").value = 'new_password'
397        self.browser.getControl(
398            name="form.password_repeat").value = 'new_passssword'
399        self.browser.getControl("Save").click()
400        self.assertTrue('passwords do not match' in self.browser.contents)
401        self.browser.getControl(name="form.password").value = 'new_password'
402        self.browser.getControl(
403            name="form.password_repeat").value = 'new_password'
404        self.browser.getControl("Save").click()
405        self.assertTrue('Form has been saved' in self.browser.contents)
406        # We are still logged in. Changing the password hasn't thrown us out.
407        self.browser.getLink("My Data").click()
408        self.assertEqual(self.browser.url, self.student_path)
409        # We can logout
410        self.browser.getLink("Logout").click()
411        self.assertTrue('You have been logged out' in self.browser.contents)
412        self.assertEqual(self.browser.url, 'http://localhost/app')
413        # We can login again with the new password
414        self.browser.getLink("Login").click()
415        self.browser.open(self.login_path)
416        self.browser.getControl(name="form.login").value = self.test_student_id
417        self.browser.getControl(name="form.password").value = 'new_password'
418        self.browser.getControl("Login").click()
419        self.assertEqual(self.browser.url, self.student_path)
420        self.assertTrue('You logged in' in self.browser.contents)
421        return
422
423    def test_setpassword(self):
424        # Set password for first-time access
425        student = Student()
426        student.reg_number = u'123456'
427        student.name = u'Klaus Tester'
428        test_student_id = self.app['students'].addStudent(student)
429        setpassword_path = 'http://localhost/app/setpassword'
430        student_path = 'http://localhost/app/students/%s' % test_student_id
431        self.browser.open(setpassword_path)
432        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
433        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
434        self.browser.getControl(name="reg_number").value = '223456'
435        self.browser.getControl("Show").click()
436        self.assertMatches('...No student found...',
437                           self.browser.contents)
438        self.browser.getControl(name="reg_number").value = '123456'
439        self.browser.getControl(name="ac_number").value = '999999'
440        self.browser.getControl("Show").click()
441        self.assertMatches('...Access code is invalid...',
442                           self.browser.contents)
443        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
444        self.browser.getControl("Show").click()
445        self.assertMatches('...Password has been set. Your Student Id is...',
446                           self.browser.contents)
447        self.browser.getControl("Show").click()
448        self.assertMatches(
449            '...Password has already been set. Your Student Id is...',
450            self.browser.contents)
451        existing_pwdpin = self.pwdpins[1]
452        parts = existing_pwdpin.split('-')[1:]
453        existing_pwdseries, existing_pwdnumber = parts
454        self.browser.getControl(name="ac_series").value = existing_pwdseries
455        self.browser.getControl(name="ac_number").value = existing_pwdnumber
456        self.browser.getControl(name="reg_number").value = '123456'
457        self.browser.getControl("Show").click()
458        self.assertMatches(
459            '...You are using the wrong Access Code...',
460            self.browser.contents)
461        # The student can login with the new credentials
462        self.browser.open(self.login_path)
463        self.browser.getControl(name="form.login").value = test_student_id
464        self.browser.getControl(
465            name="form.password").value = self.existing_pwdnumber
466        self.browser.getControl("Login").click()
467        self.assertEqual(self.browser.url, student_path)
468        self.assertTrue('You logged in' in self.browser.contents)
469        return
470
471    def test_student_access(self):
472        # Students can access their own objects
473        # and can perform actions
474        student = self.app['students'][self.test_student_id]
475        IWorkflowInfo(student).fireTransition('admit')
476        self.browser.open(self.login_path)
477        self.browser.getControl(name="form.login").value = self.test_student_id
478        self.browser.getControl(name="form.password").value = 'spwd'
479        self.browser.getControl("Login").click()
480        # Student can view the clearance data
481        self.browser.getLink("Clearance Data").click()
482        # Student can't open clearance edit form before starting clearance
483        self.browser.open(self.student_path + '/cedit')
484        self.assertMatches('...The requested form is locked...',
485                           self.browser.contents)
486        self.browser.getLink("Clearance Data").click()
487        self.browser.getLink("Start clearance").click()
488        self.browser.getControl(name="ac_series").value = self.existing_clrseries
489        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
490        self.browser.getControl("Start clearance now").click()
491        self.assertMatches('...Clearance process has been started...',
492                           self.browser.contents)
493        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
494        self.browser.getControl("Save", index=0).click()
495        # Student can view the clearance data
496        self.browser.getLink("Clearance Data").click()
497        # and go back to the edit form
498        self.browser.getLink("Edit").click()
499        self.browser.getControl("Save and request clearance").click()
500        self.browser.getControl(name="ac_series").value = self.existing_clrseries
501        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
502        self.browser.getControl("Request clearance now").click()
503        self.assertMatches('...Clearance has been requested...',
504                           self.browser.contents)
505        # Student can't reopen clearance form after requesting clearance
506        self.browser.open(self.student_path + '/cedit')
507        self.assertMatches('...The requested form is locked...',
508                           self.browser.contents)
509        return
Note: See TracBrowser for help on using the repository browser.