# -*- coding: utf-8 -*- ## $Id: test_browser.py 15055 2018-06-18 05:20:14Z henrik $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """ Test the student-related UI components. """ import shutil import tempfile import pytz import base64 from datetime import datetime, timedelta, date from StringIO import StringIO import os import grok from zc.async.testing import wait_for_result from zope.event import notify from zope.component import createObject, queryUtility, getUtility from zope.component.hooks import setSite, clearSite from zope.catalog.interfaces import ICatalog from zope.security.interfaces import Unauthorized from zope.securitypolicy.interfaces import IPrincipalRoleManager from zope.testbrowser.testing import Browser from zope.interface import implementedBy from zope.schema.fieldproperty import FieldProperty from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase from waeup.kofa.app import University from waeup.kofa.payments.interfaces import IPayer from waeup.kofa.students.payments import StudentOnlinePayment from waeup.kofa.students.student import Student from waeup.kofa.students.studylevel import StudentStudyLevel from waeup.kofa.university.faculty import Faculty from waeup.kofa.university.department import Department from waeup.kofa.interfaces import IUserAccount, IJobManager, VALIDATED, CREATED from waeup.kofa.authentication import LocalRoleSetEvent from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED from waeup.kofa.tests.test_async import FunctionalAsyncTestCase from waeup.kofa.browser.tests.test_pdf import samples_dir PH_LEN = 15911 # Length of placeholder file SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg') SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp') URL_LECTURER_LANDING = 'http://localhost/app/my_courses' curr_year = datetime.now().year def lookup_submit_value(name, value, browser): """Find a button with a certain value.""" for num in range(0, 100): try: button = browser.getControl(name=name, index=num) if button.value.endswith(value): return button except IndexError: break return None class StudentsFullSetup(FunctionalTestCase): # A test case that only contains a setup and teardown # # Complete setup for students handlings is rather complex and # requires lots of things created before we can start. This is a # setup that does all this, creates a university, creates PINs, # etc. so that we do not have to bother with that in different # test cases. layer = FunctionalLayer def setUp(self): super(StudentsFullSetup, self).setUp() # Setup a sample site for each test app = University() self.dc_root = tempfile.mkdtemp() app['datacenter'].setStoragePath(self.dc_root) # Prepopulate the ZODB... self.getRootFolder()['app'] = app # we add the site immediately after creation to the # ZODB. Catalogs and other local utilities are not setup # before that step. self.app = self.getRootFolder()['app'] # Set site here. Some of the following setup code might need # to access grok.getSite() and should get our new app then setSite(app) # Add student with subobjects student = createObject('waeup.Student') student.firstname = u'Anna' student.lastname = u'Tester' student.reg_number = u'123' student.matric_number = u'234' student.sex = u'm' student.email = 'aa@aa.ng' student.phone = u'1234' student.date_of_birth = date(1981, 2, 4) self.app['students'].addStudent(student) self.student_id = student.student_id self.student = self.app['students'][self.student_id] # Set password IUserAccount( self.app['students'][self.student_id]).setPassword('spwd') self.login_path = 'http://localhost/app/login' self.container_path = 'http://localhost/app/students' self.manage_container_path = self.container_path + '/@@manage' self.add_student_path = self.container_path + '/addstudent' self.student_path = self.container_path + '/' + self.student_id self.manage_student_path = self.student_path + '/manage_base' self.trigtrans_path = self.student_path + '/trigtrans' self.clearance_path = self.student_path + '/view_clearance' self.personal_path = self.student_path + '/view_personal' self.edit_clearance_path = self.student_path + '/cedit' self.manage_clearance_path = self.student_path + '/manage_clearance' self.edit_personal_path = self.student_path + '/edit_personal' self.manage_personal_path = self.student_path + '/manage_personal' self.studycourse_path = self.student_path + '/studycourse' self.payments_path = self.student_path + '/payments' self.acco_path = self.student_path + '/accommodation' self.history_path = self.student_path + '/history' # Create 5 access codes with prefix'PWD' pin_container = self.app['accesscodes'] pin_container.createBatch( datetime.utcnow(), 'some_userid', 'PWD', 9.99, 5) pins = pin_container['PWD-1'].values() self.pwdpins = [x.representation for x in pins] self.existing_pwdpin = self.pwdpins[0] parts = self.existing_pwdpin.split('-')[1:] self.existing_pwdseries, self.existing_pwdnumber = parts # Create 5 access codes with prefix 'CLR' pin_container.createBatch( datetime.now(), 'some_userid', 'CLR', 9.99, 5) pins = pin_container['CLR-1'].values() pins[0].owner = u'Hans Wurst' self.existing_clrac = pins[0] self.existing_clrpin = pins[0].representation parts = self.existing_clrpin.split('-')[1:] self.existing_clrseries, self.existing_clrnumber = parts # Create 2 access codes with prefix 'HOS' pin_container.createBatch( datetime.now(), 'some_userid', 'HOS', 9.99, 2) pins = pin_container['HOS-1'].values() self.existing_hosac = pins[0] self.existing_hospin = pins[0].representation parts = self.existing_hospin.split('-')[1:] self.existing_hosseries, self.existing_hosnumber = parts # Populate university self.certificate = createObject('waeup.Certificate') self.certificate.code = u'CERT1' self.certificate.application_category = 'basic' self.certificate.study_mode = 'ug_ft' self.certificate.start_level = 100 self.certificate.end_level = 500 self.certificate.school_fee_1 = 40000.0 self.certificate.school_fee_2 = 20000.0 self.app['faculties']['fac1'] = Faculty(code=u'fac1') self.app['faculties']['fac1']['dep1'] = Department(code=u'dep1') self.app['faculties']['fac1']['dep1'].certificates.addCertificate( self.certificate) self.course = createObject('waeup.Course') self.course.code = 'COURSE1' self.course.semester = 1 self.course.credits = 10 self.course.passmark = 40 self.app['faculties']['fac1']['dep1'].courses.addCourse( self.course) self.app['faculties']['fac1']['dep1'].certificates[ 'CERT1'].addCertCourse(self.course, level=100) # Configure university and hostels self.app['hostels'].accommodation_states = ['admitted'] self.app['hostels'].accommodation_session = 2004 delta = timedelta(days=10) self.app['hostels'].startdate = datetime.now(pytz.utc) - delta self.app['hostels'].enddate = datetime.now(pytz.utc) + delta self.app['configuration'].carry_over = True configuration = createObject('waeup.SessionConfiguration') configuration.academic_session = 2004 configuration.clearance_fee = 3456.0 configuration.transcript_fee = 4567.0 configuration.booking_fee = 123.4 configuration.maint_fee = 987.0 configuration.transfer_fee = 456.0 configuration.late_registration_fee = 345.0 self.app['configuration'].addSessionConfiguration(configuration) # Create a hostel with two beds hostel = Hostel() hostel.hostel_id = u'hall-1' hostel.hostel_name = u'Hall 1' hostel.maint_fee = 876.0 self.app['hostels'].addHostel(hostel) bed = Bed() bed.bed_id = u'hall-1_A_101_A' bed.bed_number = 1 bed.owner = NOT_OCCUPIED bed.bed_type = u'regular_male_fr' self.app['hostels'][hostel.hostel_id].addBed(bed) bed = Bed() bed.bed_id = u'hall-1_A_101_B' bed.bed_number = 2 bed.owner = NOT_OCCUPIED bed.bed_type = u'regular_female_fr' self.app['hostels'][hostel.hostel_id].addBed(bed) # Set study course attributes of test student self.student['studycourse'].certificate = self.certificate self.student['studycourse'].current_session = 2004 self.student['studycourse'].entry_session = 2004 self.student['studycourse'].current_verdict = 'A' self.student['studycourse'].current_level = 100 # Update the catalog notify(grok.ObjectModifiedEvent(self.student)) # Put the prepopulated site into test ZODB and prepare test # browser self.browser = Browser() self.browser.handleErrors = False def tearDown(self): super(StudentsFullSetup, self).tearDown() clearSite() shutil.rmtree(self.dc_root) class StudentsContainerUITests(StudentsFullSetup): # Tests for StudentsContainer class views and pages layer = FunctionalLayer def test_anonymous_access(self): # Anonymous users can't access students containers self.assertRaises( Unauthorized, self.browser.open, self.container_path) self.assertRaises( Unauthorized, self.browser.open, self.manage_container_path) return def test_manage_access(self): # Managers can access the view page of students # containers and can perform actions self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.container_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.container_path) self.browser.getLink("Manage students section").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.manage_container_path) return def test_add_search_delete_students(self): # Managers can add search and remove students self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_container_path) self.browser.getLink("Add student").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.add_student_path) self.browser.getControl(name="form.firstname").value = 'Bob' self.browser.getControl(name="form.lastname").value = 'Tester' self.browser.getControl(name="form.reg_number").value = '123' self.browser.getControl("Create student").click() self.assertTrue( 'Registration number exists already' in self.browser.contents) self.browser.getControl(name="form.reg_number").value = '1234' self.browser.getControl("Create student").click() self.assertTrue('Student record created' in self.browser.contents) # Registration and matric numbers must be unique self.browser.getLink("Manage").click() self.browser.getControl(name="form.reg_number").value = '123' self.browser.getControl("Save").click() self.assertMatches('...Registration number exists...', self.browser.contents) self.browser.getControl(name="form.reg_number").value = '789' self.browser.getControl(name="form.matric_number").value = '234' self.browser.getControl("Save").click() self.assertMatches('...Matriculation number exists...', self.browser.contents) # We can find a student with a certain student_id self.browser.open(self.container_path) self.browser.getControl("Find student(s)").click() self.assertTrue('Empty search string' in self.browser.contents) self.browser.getControl(name="searchtype").value = ['student_id'] self.browser.getControl(name="searchterm").value = self.student_id self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) # We can find a student in a certain session self.browser.open(self.container_path) self.browser.getControl(name="searchtype").value = ['current_session'] self.browser.getControl(name="searchterm").value = '2004' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) # Session fileds require integer values self.browser.open(self.container_path) self.browser.getControl(name="searchtype").value = ['current_session'] self.browser.getControl(name="searchterm").value = '2004/2005' self.browser.getControl("Find student(s)").click() self.assertTrue('Only year dates allowed' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['current_session'] self.browser.getControl(name="searchterm").value = '2004/2005' self.browser.getControl("Find student(s)").click() self.assertTrue('Only year dates allowed' in self.browser.contents) # We can find a student in a certain study_mode self.browser.open(self.container_path) self.browser.getControl(name="searchtype").value = ['current_mode'] self.browser.getControl(name="searchterm").value = 'ug_ft' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) # We can find a student in a certain department self.browser.open(self.container_path) self.browser.getControl(name="searchtype").value = ['depcode'] self.browser.getControl(name="searchterm").value = 'dep1' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) # We can find a student by searching for all kind of name parts self.browser.open(self.manage_container_path) self.browser.getControl("Find student(s)").click() self.assertTrue('Empty search string' in self.browser.contents) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'Anna Tester' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'Anna' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'Tester' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'An' self.browser.getControl("Find student(s)").click() self.assertFalse('Anna Tester' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'An*' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'tester' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'Tester Ana' self.browser.getControl("Find student(s)").click() self.assertFalse('Anna Tester' in self.browser.contents) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'Tester Anna' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) # The old searchterm will be used again self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) # We can find suspended students self.student.suspended = True notify(grok.ObjectModifiedEvent(self.student)) self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['suspended'] self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) self.browser.open(self.container_path) self.browser.getControl(name="searchtype").value = ['suspended'] self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) # The catalog is informed when studycourse objects have been # edited self.browser.open(self.studycourse_path + '/manage') self.browser.getControl(name="form.current_session").value = ['2010'] self.browser.getControl(name="form.entry_session").value = ['2010'] self.browser.getControl(name="form.entry_mode").value = ['ug_ft'] self.browser.getControl("Save").click() # We can find the student in the new session self.browser.open(self.manage_container_path) self.browser.getControl(name="searchtype").value = ['current_session'] self.browser.getControl(name="searchterm").value = '2010' self.browser.getControl("Find student(s)").click() self.assertTrue('Anna Tester' in self.browser.contents) ctrl = self.browser.getControl(name='entries') ctrl.getControl(value=self.student_id).selected = True self.browser.getControl("Remove selected", index=0).click() self.assertTrue('Successfully removed' in self.browser.contents) self.browser.getControl(name="searchtype").value = ['student_id'] self.browser.getControl(name="searchterm").value = self.student_id self.browser.getControl("Find student(s)").click() self.assertTrue('No student found' in self.browser.contents) self.browser.open(self.container_path) self.browser.getControl(name="searchtype").value = ['student_id'] self.browser.getControl(name="searchterm").value = self.student_id self.browser.getControl("Find student(s)").click() self.assertTrue('No student found' in self.browser.contents) return def test_add_graduated_students(self): # Managers can add search and remove students self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_container_path) self.browser.getLink("Add student").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.add_student_path) self.browser.getControl(name="form.firstname").value = 'Bob' self.browser.getControl(name="form.lastname").value = 'Tester' self.browser.getControl(name="form.reg_number").value = '1234' self.browser.getControl("Create graduated student").click() self.assertTrue('Student record created' in self.browser.contents) self.assertEqual(self.app['students']['K1000001'].state, 'graduated') return class OfficerUITests(StudentsFullSetup): # Tests for Student class views and pages def test_student_properties(self): self.student['studycourse'].current_level = 100 self.assertEqual(self.student.current_level, 100) self.student['studycourse'].current_session = 2011 self.assertEqual(self.student.current_session, 2011) self.student['studycourse'].current_verdict = 'A' self.assertEqual(self.student.current_verdict, 'A') return def test_studylevelmanagepage(self): studylevel = StudentStudyLevel() studylevel.level = 100 cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1'] self.student['studycourse'].addStudentStudyLevel(cert, studylevel) self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.studycourse_path + '/100/manage') self.assertEqual( self.browser.url, self.studycourse_path + '/100/manage') self.assertEqual(self.browser.headers['Status'], '200 Ok') def test_basic_auth(self): self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open('http://localhost/app') self.browser.getLink("Logout").click() self.assertTrue('You have been logged out' in self.browser.contents) # But we are still logged in since we've used basic # authentication here. Wikipedia says: Existing browsers # retain authentication information until the tab or browser # is closed or the user clears the history. HTTP does not # provide a method for a server to direct clients to discard # these cached credentials. This means that there is no # effective way for a server to "log out" the user without # closing the browser. This is a significant defect that # requires browser manufacturers to support a "logout" user # interface element ... self.assertTrue('Manager' in self.browser.contents) def test_basic_auth_base64(self): auth_token = base64.b64encode('mgr:mgrpw') self.browser.addHeader('Authorization', 'Basic %s' % auth_token) self.browser.open(self.manage_container_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') def test_manage_access(self): # Managers can access the pages of students # and can perform actions self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.student_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.student_path) self.browser.getLink("Trigger").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') # Managers can trigger transitions self.browser.getControl(name="transition").value = ['admit'] self.browser.getControl("Save").click() # Managers can edit base self.browser.open(self.student_path) self.browser.getLink("Manage").click() self.assertEqual(self.browser.url, self.manage_student_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.browser.getControl(name="form.firstname").value = 'John' self.browser.getControl(name="form.lastname").value = 'Tester' self.browser.getControl(name="form.reg_number").value = '345' self.browser.getControl(name="password").value = 'secret' self.browser.getControl(name="control_password").value = 'secret' self.browser.getControl("Save").click() self.assertMatches('...Form has been saved...', self.browser.contents) self.browser.open(self.student_path) self.browser.getLink("Clearance Data").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.clearance_path) self.browser.getLink("Manage").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.manage_clearance_path) self.browser.getControl(name="form.date_of_birth").value = '09/10/1961' self.browser.getControl("Save").click() self.assertMatches('...Form has been saved...', self.browser.contents) self.browser.open(self.student_path) self.browser.getLink("Personal Data").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.personal_path) self.browser.getLink("Manage").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.manage_personal_path) self.browser.open(self.personal_path) self.assertTrue('Updated' in self.browser.contents) self.browser.getLink("Edit").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.edit_personal_path) self.browser.getControl("Save").click() # perm_address is required in IStudentPersonalEdit self.assertMatches('...Required input is missing...', self.browser.contents) self.browser.getControl(name="form.perm_address").value = 'My address!' self.browser.getControl("Save").click() self.assertMatches('...Form has been saved...', self.browser.contents) # Managers can browse all subobjects self.browser.open(self.student_path) self.browser.getLink("Payments").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.payments_path) self.browser.open(self.student_path) self.browser.getLink("Accommodation").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.acco_path) self.browser.open(self.student_path) self.browser.getLink("History").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.history_path) self.assertMatches('...Admitted by Manager...', self.browser.contents) # Only the Application Slip does not exist self.assertFalse('Application Slip' in self.browser.contents) return def test_flash_notice(self): self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.student_path) self.assertFalse('alert alert-warning' in self.browser.contents) self.student.flash_notice = u'Happy Birthday!' self.browser.open(self.student_path) self.assertTrue( '