## $Id: test_browser.py 14949 2018-02-09 09:17:58Z 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 applicant-related UI components. """ import os import pytz import shutil import tempfile import grok import logging from datetime import datetime from StringIO import StringIO from datetime import datetime, date, timedelta from mechanize import LinkNotFoundError from zc.async.testing import wait_for_result from zope.securitypolicy.interfaces import IPrincipalRoleManager from zope.event import notify from zope.catalog.interfaces import ICatalog from zope.component import createObject, getUtility from zope.component.hooks import setSite, clearSite from zope.security.interfaces import Unauthorized from zope.testbrowser.testing import Browser 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.configuration import SessionConfiguration from waeup.kofa.refereeentries import RefereeEntry from waeup.kofa.applicants.container import ApplicantsContainer from waeup.kofa.applicants.applicant import Applicant from waeup.kofa.interfaces import ( IExtFileStore, IFileStoreNameChooser, IUserAccount, IJobManager) from waeup.kofa.university.faculty import Faculty from waeup.kofa.university.department import Department from waeup.kofa.mandates.mandate import RefereeReportMandate from waeup.kofa.tests.test_async import FunctionalAsyncTestCase PH_LEN = 15911 # Length of placeholder file session_1 = datetime.now().year - 2 container_name_1 = u'app%s' % session_1 session_2 = datetime.now().year - 1 container_name_2 = u'app%s' % session_2 SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg') class ApplicantsFullSetup(FunctionalTestCase): # A test case that only contains a setup and teardown # # Complete setup for applicants 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(ApplicantsFullSetup, 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) self.login_path = 'http://localhost/app/login' self.root_path = 'http://localhost/app/applicants' self.search_path = 'http://localhost/app/applicants/search' self.manage_root_path = self.root_path + '/@@manage' self.add_container_path = self.root_path + '/@@add' self.container_path = 'http://localhost/app/applicants/%s' % container_name_1 self.manage_container_path = self.container_path + '/@@manage' # Add an applicants container applicantscontainer = ApplicantsContainer() applicantscontainer.code = container_name_1 applicantscontainer.prefix = 'app' applicantscontainer.year = session_1 applicantscontainer.title = u'This is the %s container' % container_name_1 applicantscontainer.application_category = 'basic' applicantscontainer.mode = 'create' applicantscontainer.strict_deadline = True delta = timedelta(days=10) applicantscontainer.startdate = datetime.now(pytz.utc) - delta applicantscontainer.enddate = datetime.now(pytz.utc) + delta self.app['applicants'][container_name_1] = applicantscontainer self.applicantscontainer = self.app['applicants'][container_name_1] # Populate university certificate = createObject('waeup.Certificate') certificate.code = 'CERT1' certificate.application_category = 'basic' certificate.start_level = 100 certificate.end_level = 500 certificate.study_mode = u'ug_ft' self.certificate = certificate self.app['faculties']['fac1'] = Faculty() # The code has explicitely to be set, otherwise we don't # find created students in their department self.app['faculties']['fac1']['dep1'] = Department(code=u'dep1') self.department = self.app['faculties']['fac1']['dep1'] self.app['faculties']['fac1']['dep1'].certificates.addCertificate( certificate) # Put the prepopulated site into test ZODB and prepare test # browser self.browser = Browser() self.browser.handleErrors = False # Create 5 access codes with prefix'FOO' and cost 9.99 each pin_container = self.app['accesscodes'] pin_container.createBatch( datetime.now(), 'some_userid', 'APP', 9.99, 5) pins = pin_container[pin_container.keys()[0]].values() self.pins = [x.representation for x in pins] self.existing_pin = self.pins[0] parts = self.existing_pin.split('-')[1:] self.existing_series, self.existing_number = parts # Add an applicant self.applicant = createObject('waeup.Applicant') # reg_number is the only field which has to be preset here # because managers are allowed to edit this required field self.applicant.firstname = u'Joan' self.applicant.reg_number = u'1234' self.applicant.course1 = certificate app['applicants'][container_name_1].addApplicant(self.applicant) IUserAccount( self.app['applicants'][container_name_1][ self.applicant.application_number]).setPassword('apwd') self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % ( container_name_1, self.applicant.application_number, 'manage') self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % ( container_name_1, self.applicant.application_number, 'edit') self.view_path = 'http://localhost/app/applicants/%s/%s' % ( container_name_1, self.applicant.application_number) def login(self): # Perform an applicant login. This creates an application record. # # This helper also sets `self.applicant`, which is the # applicant object created. self.browser.open(self.login_path) self.browser.getControl( name="form.login").value = self.applicant.applicant_id self.browser.getControl(name="form.password").value = 'apwd' self.browser.getControl("Login").click() def fill_correct_values(self): # Fill the edit form with suitable values self.browser.getControl(name="form.firstname").value = 'John' self.browser.getControl(name="form.middlename").value = 'Anthony' self.browser.getControl(name="form.lastname").value = 'Tester' self.browser.getControl(name="form.course1").value = ['CERT1'] self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' self.browser.getControl(name="form.sex").value = ['m'] self.browser.getControl(name="form.email").value = 'xx@yy.zz' def tearDown(self): super(ApplicantsFullSetup, self).tearDown() clearSite() shutil.rmtree(self.dc_root) class ApplicantsRootUITests(ApplicantsFullSetup): # Tests for ApplicantsRoot class layer = FunctionalLayer def test_anonymous_access(self): # Anonymous users can access applicants root self.browser.open(self.root_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertFalse( 'Manage ' in self.browser.contents) return def test_anonymous_no_actions(self): # Make sure anonymous users cannot access actions self.browser.open(self.root_path) self.assertRaises( LookupError, self.browser.getControl, "Add local role") # Manage screen neither linked nor accessible for anonymous self.assertRaises( LinkNotFoundError, self.browser.getLink, 'Manage applicants section') self.assertRaises( Unauthorized, self.browser.open, self.manage_root_path) # Add container screen not accessible for anonymous self.assertRaises( Unauthorized, self.browser.open, self.add_container_path) return def test_manage_access(self): # Managers can access the manage pages of applicants root self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.root_path) self.assertTrue('Manage applicants section' in self.browser.contents) # There is a manage link link = self.browser.getLink('Manage applicants section') link.click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.manage_root_path) return def test_hide_container(self): self.browser.open(self.root_path) self.assertTrue( '' 'This is the %s container' % (container_name_1, container_name_1) in self.browser.contents) self.app['applicants'][container_name_1].hidden = True self.browser.open(self.root_path) # Anonymous users can't see hidden containers self.assertFalse( '' 'This is the %s container' % (container_name_1, container_name_1) in self.browser.contents) self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.root_path) self.assertTrue( '' 'This is the %s container' % (container_name_1, container_name_1) in self.browser.contents) return def test_search(self): # Managers can access the manage pages of applicants root self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_path) self.fill_correct_values() self.browser.getControl("Save").click() self.browser.open(self.root_path) self.assertTrue('Manage applicants section' in self.browser.contents) # There is a search link link = self.browser.getLink('Find applicants') link.click() self.assertEqual(self.browser.headers['Status'], '200 Ok') # We can find an applicant ... # ... via his name self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'John' self.browser.getControl("Find applicant").click() self.assertTrue('John Anthony Tester' in self.browser.contents) self.browser.getControl(name="searchtype").value = ['fullname'] self.browser.getControl(name="searchterm").value = 'Tester' self.browser.getControl("Find applicant").click() self.assertTrue('John Anthony Tester' in self.browser.contents) self.browser.open(self.search_path) # ... and via his reg_number ... self.browser.getControl(name="searchtype").value = ['reg_number'] self.browser.getControl(name="searchterm").value = '2345' self.browser.getControl("Find applicant").click() self.assertFalse('John Anthony Tester' in self.browser.contents) self.browser.getControl(name="searchtype").value = ['reg_number'] self.browser.getControl(name="searchterm").value = '1234' self.browser.getControl("Find applicant").click() self.assertTrue('John Anthony Tester' in self.browser.contents) # ... and not via his application_number ... self.browser.getControl(name="searchtype").value = ['applicant_id'] self.browser.getControl( name="searchterm").value = self.applicant.application_number self.browser.getControl("Find applicant").click() self.assertFalse('John Anthony Tester' in self.browser.contents) # ... but ia his applicant_id ... self.browser.getControl(name="searchtype").value = ['applicant_id'] self.browser.getControl( name="searchterm").value = self.applicant.applicant_id self.browser.getControl("Find applicant").click() self.assertTrue('John Anthony Tester' in self.browser.contents) # ... and via his email self.browser.getControl(name="searchtype").value = ['email'] self.browser.getControl(name="searchterm").value = 'xx@yy.zz' self.browser.getControl("Find applicant").click() self.assertTrue('John Anthony Tester' in self.browser.contents) return def test_manage_actions_access(self): # Managers can access the action on manage screen self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_root_path) self.browser.getControl("Add local role").click() self.assertTrue('No user selected' in self.browser.contents) return def test_local_roles_add_delete(self): # Managers can assign and delete local roles of applicants root myusers = self.app['users'] myusers.addUser('bob', 'bobssecret') self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open('http://localhost/app/faculties/fac1/dep1/manage') self.browser.getControl(name="user").value = ['bob'] self.browser.getControl(name="local_role").value = [ 'waeup.local.ApplicationsManager'] self.browser.getControl("Add local role").click() self.assertTrue('bob' in self.browser.contents) # Remove the role assigned ctrl = self.browser.getControl(name='role_id') ctrl.getControl( value='bob|waeup.local.ApplicationsManager').selected = True self.browser.getControl("Remove selected local roles").click() self.assertTrue( 'Local role successfully removed: bob|waeup.local.ApplicationsManager' in self.browser.contents) self.assertFalse('bob' in self.browser.contents) return def test_add_delete_container(self): # Managers can add and delete applicants containers self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_root_path) self.browser.getControl("Add applicants container").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.add_container_path) self.browser.getControl(name="form.prefix").value = ['app'] self.browser.getControl("Add applicants container").click() self.assertTrue( 'There were errors' in self.browser.contents) self.browser.getControl(name="form.prefix").value = ['app'] self.browser.getControl(name="form.year").value = [str(session_2)] self.browser.getControl(name="form.mode").value = ['create'] self.browser.getControl( name="form.application_category").value = ['basic'] self.browser.getControl("Add applicants container").click() self.assertTrue('Added:' in self.browser.contents) self.browser.getLink(container_name_1).click() self.assertTrue('Manage applicants container' in self.browser.contents) self.browser.open(self.add_container_path) self.browser.getControl("Cancel").click() self.assertEqual(self.browser.url, self.manage_root_path) self.browser.open(self.add_container_path) self.browser.getControl(name="form.prefix").value = ['app'] self.browser.getControl(name="form.year").value = [str(session_2)] self.browser.getControl(name="form.mode").value = ['create'] self.browser.getControl( name="form.application_category").value = ['basic'] self.browser.getControl("Add applicants container").click() self.assertTrue('exists already in the database' in self.browser.contents) self.browser.open(self.manage_root_path) ctrl = self.browser.getControl(name='val_id') ctrl.getControl(value=container_name_2).selected = True self.browser.getControl("Remove selected", index=0).click() self.assertTrue('Successfully removed:' in self.browser.contents) self.browser.open(self.add_container_path) self.browser.getControl(name="form.prefix").value = ['app'] self.browser.getControl(name="form.year").value = [str(session_2)] self.browser.getControl(name="form.mode").value = ['create'] #self.browser.getControl(name="form.ac_prefix").value = ['APP'] self.browser.getControl( name="form.application_category").value = ['basic'] self.browser.getControl("Add applicants container").click() del self.app['applicants'][container_name_2] ctrl = self.browser.getControl(name='val_id') ctrl.getControl(value=container_name_2).selected = True self.browser.getControl("Remove selected", index=0).click() self.assertMatches('...Could not delete...', self.browser.contents) return class ApplicantsContainerUITests(ApplicantsFullSetup): # Tests for ApplicantsContainer class views and pages layer = FunctionalLayer def test_anonymous_access(self): # Anonymous users can access applicants containers self.browser.open(self.container_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertFalse( 'Manage ' in self.browser.contents) return def test_manage_access(self): # Managers can access the manage pages of applicants # containers and can perform actions self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_container_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.manage_container_path) self.browser.getControl(name="form.application_fee").value = '200' self.browser.getControl("Save").click() self.assertTrue('Form has been saved' in self.browser.contents) logfile = os.path.join( self.app['datacenter'].storage, 'logs', 'applicants.log') logcontent = open(logfile).read() self.assertTrue( 'zope.mgr - applicants.browser.ApplicantsContainerManageFormPage - ' '%s - saved: application_fee\n' % container_name_1 in logcontent) self.browser.getControl("Remove selected", index=0).click() self.assertTrue('No applicant selected' in self.browser.contents) self.browser.getControl("Add local role").click() self.assertTrue('No user selected' in self.browser.contents) self.browser.getControl("Cancel", index=0).click() self.assertEqual(self.browser.url, self.container_path) return def test_statistics(self): self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.container_path) self.browser.getLink("Container statistics").click() self.assertTrue('initialized' in self.browser.contents) self.assertTrue('1' in self.browser.contents) self.assertEqual(self.applicantscontainer.statistics[0], {'not admitted': 0, 'started': 0, 'created': 0, 'admitted': 0, 'submitted': 0, 'initialized': 1, 'paid': 0, 'processed': 0}) #self.assertEqual(self.applicantscontainer.statistics[1], # {u'fac1': 0}) IWorkflowState(self.applicant).setState('submitted') notify(grok.ObjectModifiedEvent(self.applicant)) self.assertEqual(self.applicantscontainer.statistics[0], {'not admitted': 0, 'started': 0, 'created': 0, 'admitted': 0, 'submitted': 1, 'initialized': 0, 'paid': 0, 'processed': 0}) #self.assertEqual(self.applicantscontainer.statistics[1], # {u'fac1': 1}) return def test_add_delete_applicants(self): # Check the global role map first role_manager = IPrincipalRoleManager(grok.getSite()) principals = role_manager.getPrincipalsForRole('waeup.Applicant') self.assertEqual(len(principals), 1) self.assertEqual(principals[0][0], self.applicant.applicant_id) # Managers can add and delete applicants self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.add_applicant_path = self.container_path + '/addapplicant' self.container_manage_path = self.container_path + '/@@manage' self.browser.open(self.container_manage_path) self.browser.getLink("Add applicant").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.add_applicant_path) self.browser.getControl(name="form.firstname").value = 'Alois' self.browser.getControl(name="form.middlename").value = 'Kofi' self.browser.getControl(name="form.lastname").value = 'Bettermann' self.browser.getControl(name="form.email").value = 'xx@yy.zz' self.browser.getControl("Create application record").click() self.assertTrue('Application initialized' in self.browser.contents) # The global role map has been extended role_manager = IPrincipalRoleManager(grok.getSite()) principals = role_manager.getPrincipalsForRole('waeup.Applicant') self.assertEqual(len(principals), 2) self.browser.open(self.container_manage_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') ctrl = self.browser.getControl(name='val_id') value = ctrl.options[0] ctrl.getControl(value=value).selected = True self.browser.getControl("Remove selected", index=0).click() self.assertTrue('Successfully removed:' in self.browser.contents) # The global role map has been reduced role_manager = IPrincipalRoleManager(grok.getSite()) principals = role_manager.getPrincipalsForRole('waeup.Applicant') self.assertEqual(len(principals), 1) self.browser.open(self.add_applicant_path) self.browser.getControl(name="form.firstname").value = 'Albert' self.browser.getControl(name="form.lastname").value = 'Einstein' self.browser.getControl(name="form.email").value = 'xx@yy.zz' self.browser.getControl("Create application record").click() self.assertTrue('Application initialized' in self.browser.contents) return def test_prefill_purge_container(self): # Managers can pre-fill containers in create mode self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.prefill_path = self.container_path + '/prefill' self.container_manage_path = self.container_path + '/@@manage' self.browser.open(self.container_manage_path) self.browser.getLink("Pre-fill").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.url, self.prefill_path) self.browser.getControl(name="number").value = ['10'] self.browser.getControl("Pre-fill").click() self.assertTrue('10 application records created.' in self.browser.contents) self.browser.open(self.container_manage_path) self.assertTrue('This container contains 10 unused pre-filled records.' in self.browser.contents) self.assertEqual(self.applicantscontainer.counts[0], 11) self.assertEqual(self.applicantscontainer.counts[1], 1) # In update mode we can't pre-fill the container self.applicantscontainer.mode = 'update' self.browser.open(self.container_manage_path) self.browser.getLink("Pre-fill").click() self.assertTrue('Container must be in create mode to be pre-filled.' in self.browser.contents) self.browser.open(self.manage_root_path) # Number of total records is 11 self.assertTrue('11' in self.browser.contents) # The statistics have not changed self.browser.open(self.container_path) self.browser.getLink("Container statistics").click() self.assertTrue('1' in self.browser.contents) self.assertEqual(self.applicantscontainer.statistics[0], {'not admitted': 0, 'started': 0, 'created': 0, 'admitted': 0, 'submitted': 0, 'initialized': 1, 'paid': 0, 'processed': 0}) # Container can be purged IWorkflowState(self.applicant).setState('submitted') self.browser.open(self.container_manage_path) self.browser.getLink("Purge").click() self.browser.getControl("Remove").click() self.assertTrue('10 application records purged' in self.browser.contents) self.assertEqual(self.applicantscontainer.counts[0], 1) self.assertEqual(self.applicantscontainer.counts[1], 1) IWorkflowState(self.applicant).setState('initialized') self.browser.open(self.container_manage_path) self.browser.getLink("Purge").click() self.browser.getControl("Remove").click() self.assertTrue('1 application records purged' in self.browser.contents) self.assertEqual(self.applicantscontainer.counts[0], 0) self.assertEqual(self.applicantscontainer.counts[1], 0) return def init_officer(self): # Create application officer self.app['users'].addUser('mrappl', 'mrapplsecret') self.app['users']['mrappl'].email = 'mrappl@foo.ng' self.app['users']['mrappl'].title = 'Carlo Pitter' prmglobal = IPrincipalRoleManager(self.app) prmglobal.assignRoleToPrincipal('waeup.ApplicationsManager', 'mrappl') # Login as officer self.browser.open(self.login_path) self.browser.getControl(name="form.login").value = 'mrappl' self.browser.getControl(name="form.password").value = 'mrapplsecret' self.browser.getControl("Login").click() def test_student_creation_permission(self): self.init_officer() self.browser.open(self.container_path + '/manage') self.browser.getControl("Create students").click() self.assertTrue('You don\'t have permission to create student records' in self.browser.contents) prmglobal = IPrincipalRoleManager(self.app) prmglobal.assignRoleToPrincipal('waeup.StudentsCreator', 'mrappl') self.browser.getControl("Create students").click() self.assertTrue('No applicant selected' in self.browser.contents) class ApplicantUITests(ApplicantsFullSetup): # Tests for uploading/browsing the passport image of appplicants layer = FunctionalLayer def test_manage_and_view_applicant(self): # Managers can manage applicants self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.slip_path = self.view_path + '/application_slip.pdf' self.browser.open(self.manage_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.fill_correct_values() # Fire transition self.browser.getControl(name="transition").value = ['start'] self.browser.getControl("Save").click() # Be sure that the empty phone field does not show wrong error message self.assertFalse('Required input is missing' in self.browser.contents) self.assertMatches('...Form has been saved...', self.browser.contents) self.assertMatches('...Application started by Manager...', self.browser.contents) self.browser.open(self.view_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') # Change course_admitted self.browser.open(self.manage_path) self.browser.getControl(name="form.course_admitted").value = ['CERT1'] self.browser.getControl("Save").click() self.assertMatches('...Form has been saved...', self.browser.contents) # Change password self.browser.getControl(name="password").value = 'secret' self.browser.getControl(name="control_password").value = 'secre' self.browser.getControl("Save").click() self.assertMatches('...Passwords do not match...', self.browser.contents) 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) # Pdf slip can't be opened and download button is not available self.assertFalse('Download application slip' in self.browser.contents) self.browser.open(self.slip_path) self.assertTrue( 'Please pay and submit before trying to download the application slip.' in self.browser.contents) # If applicant is in correct state the pdf slip can be opened. IWorkflowState(self.applicant).setState('submitted') self.browser.open(self.manage_path) self.browser.getLink("Download application slip").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') # Managers can view applicants even if certificate has been removed del self.app['faculties']['fac1']['dep1'].certificates['CERT1'] self.browser.open(self.view_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.browser.open(self.slip_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') return def test_passport_edit_view(self): # We get a default image after login self.browser.open(self.login_path) self.login() self.browser.open(self.browser.url + '/passport.jpg') self.assertEqual(self.browser.headers['status'], '200 Ok') self.assertEqual(self.browser.headers['content-type'], 'image/jpeg') self.assertTrue('JFIF' in self.browser.contents) self.assertEqual( self.browser.headers['content-length'], str(PH_LEN)) def test_applicant_login(self): self.applicant.suspended = True self.login() self.assertTrue( 'You entered invalid credentials.' in self.browser.contents) self.applicant.suspended = False self.browser.getControl("Login").click() self.assertTrue( 'You logged in.' in self.browser.contents) def test_maintenance_mode(self): config = grok.getSite()['configuration'] self.login() # Applicant logged in. self.assertTrue('You logged in' in self.browser.contents) self.assertTrue("Joan None" in self.browser.contents) # If maintenance mode is enabled, applicant is immediately logged out. config.maintmode_enabled_by = u'any_user' self.assertRaises( Unauthorized, self.browser.open, 'http://localhost/app/faculties') self.browser.open('http://localhost/app/login') self.assertTrue('The portal is in maintenance mode' in self.browser.contents) # Applicant really can't login if maintenance mode is enabled. self.login() # A second warning is raised. self.assertTrue( 'The portal is in maintenance mode. You can\'t login!' in self.browser.contents) return def test_applicant_access(self): # Applicants can edit their record self.browser.open(self.login_path) self.login() self.assertTrue( 'You logged in.' in self.browser.contents) self.browser.open(self.edit_path) self.assertTrue(self.browser.url != self.login_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.fill_correct_values() self.assertTrue(IUserAccount(self.applicant).checkPassword('apwd')) self.browser.getControl("Save").click() self.assertMatches('...Form has been saved...', self.browser.contents) # Applicants don't see manage and search links ... self.browser.open(self.root_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertFalse('Search' in self.browser.contents) self.assertFalse('Manage applicants section' in self.browser.contents) # ... and can't access the manage page self.assertRaises( Unauthorized, self.browser.open, self.manage_path) return def test_message_for_created(self): IWorkflowState(self.applicant).setState('created') self.applicant.student_id = u'my id' self.browser.open(self.login_path) self.login() self.assertTrue( 'You logged in.' in self.browser.contents) self.assertTrue( 'Congratulations! You have been offered provisional' ' admission into the %s/%s Academic Session of' ' Sample University. Your student record has been created for you.' % (session_1, session_1 + 1) in self.browser.contents) self.assertTrue( 'Then enter your new student credentials: user name= my id,' ' password = %s.' % self.applicant.application_number in self.browser.contents) return def image_url(self, filename): return self.edit_path.replace('edit', filename) def test_after_login_default_browsable(self): # After login we see the placeholder image in the edit view self.login() self.assertEqual(self.browser.url, self.view_path) self.browser.open(self.edit_path) # There is a correct link included self.assertTrue( '' in self.browser.contents) # Browsing the link shows a real image self.browser.open(self.image_url('passport.jpg')) self.assertEqual( self.browser.headers['content-type'], 'image/jpeg') self.assertEqual(len(self.browser.contents), PH_LEN) def test_after_submit_default_browsable(self): # After submitting an applicant form the default image is # still visible self.login() self.browser.open(self.edit_path) self.browser.getControl("Save").click() # submit form # There is a correct link included self.assertTrue( '' in self.browser.contents) # Browsing the link shows a real image self.browser.open(self.image_url('passport.jpg')) self.assertEqual( self.browser.headers['content-type'], 'image/jpeg') self.assertEqual(len(self.browser.contents), PH_LEN) def test_uploaded_image_respects_file_size_restriction(self): # When we upload an image that is too big ( > 10 KB) we will # get an error message self.login() self.browser.open(self.edit_path) # Create a pseudo image file and select it to be uploaded in form photo_content = 'A' * 1024 * 51 # A string of 11 KB size pseudo_image = StringIO(photo_content) ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # submit form # There is a correct link included self.assertTrue( '' in self.browser.contents) # We get a warning message self.assertTrue( 'Uploaded image is too big' in self.browser.contents) # Browsing the image gives us the default image, not the # uploaded one. self.browser.open(self.image_url('passport.jpg')) self.assertEqual( self.browser.headers['content-type'], 'image/jpeg') self.assertEqual(len(self.browser.contents), PH_LEN) # There is really no file stored for the applicant img = getUtility(IExtFileStore).getFile( IFileStoreNameChooser(self.applicant).chooseName()) self.assertTrue(img is None) def test_uploaded_image_browsable_w_errors(self): # We can upload a different image and browse it after submit, # even if there are still errors in the form self.login() self.browser.open(self.edit_path) # Create a pseudo image file and select it to be uploaded in form photo_content = 'I pretend to be a graphics file' pseudo_image = StringIO(photo_content) ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # submit form # There is a correct link included self.assertTrue( '' in self.browser.contents) # Browsing the link shows a real image self.browser.open(self.image_url('passport.jpg')) self.assertEqual( self.browser.headers['content-type'], 'image/jpeg') self.assertEqual(self.browser.contents, photo_content) def test_uploaded_image_stored_in_imagestorage_w_errors(self): # After uploading a new passport pic the file is correctly # stored in an imagestorage self.login() self.browser.open(self.edit_path) # Create a pseudo image file and select it to be uploaded in form pseudo_image = StringIO('I pretend to be a graphics file') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # submit form storage = getUtility(IExtFileStore) file_id = IFileStoreNameChooser(self.applicant).chooseName() pseudo_image.seek(0) # reset our file data source self.assertEqual( storage.getFile(file_id).read(), pseudo_image.read()) return def test_uploaded_image_browsable_wo_errors(self): # We can upload a different image and browse it after submit, # if there are no errors in form self.login() self.browser.open(self.edit_path) self.fill_correct_values() # fill other fields with correct values # Create a pseudo image file and select it to be uploaded in form pseudo_image = StringIO('I pretend to be a graphics file') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # submit form # There is a correct link included self.assertTrue( '' in self.browser.contents) # Browsing the link shows a real image self.browser.open(self.image_url('passport.jpg')) self.assertEqual( self.browser.headers['content-type'], 'image/jpeg') self.assertEqual(len(self.browser.contents), 31) def test_uploaded_image_stored_in_imagestorage_wo_errors(self): # After uploading a new passport pic the file is correctly # stored in an imagestorage if form contains no errors self.login() self.browser.open(self.edit_path) self.fill_correct_values() # fill other fields with correct values # Create a pseudo image file and select it to be uploaded in form pseudo_image = StringIO('I pretend to be a graphics file') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # submit form storage = getUtility(IExtFileStore) file_id = IFileStoreNameChooser(self.applicant).chooseName() # The stored image can be fetched fd = storage.getFile(file_id) file_len = len(fd.read()) self.assertEqual(file_len, 31) # When an applicant is removed, also the image is gone. del self.app['applicants'][container_name_1][self.applicant.application_number] fd = storage.getFile(file_id) self.assertTrue(fd is None) def test_uploaded_images_equal(self): # Make sure uploaded images do really differ if we eject a # change notfication (and do not if we don't) self.login() self.browser.open(self.edit_path) self.fill_correct_values() # fill other fields with correct values self.browser.getControl("Save").click() # submit form # Now go on as an officer self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_path) # Create a pseudo image file and select it to be uploaded in form pseudo_image = StringIO('I pretend to be a graphics file') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') file_id = IFileStoreNameChooser(self.applicant).chooseName() setSite(self.app) passport0 = getUtility(IExtFileStore).getFile(file_id) self.browser.getControl("Save").click() # submit form with changed pic passport1 = getUtility(IExtFileStore).getFile(file_id).read() self.browser.getControl("Save").click() # submit form w/o changes passport2 = getUtility(IExtFileStore).getFile(file_id).read() self.assertTrue(passport0 is None) self.assertTrue(passport0 != passport1) self.assertTrue(passport1 == passport2) return def test_upload_image_by_manager_with_logging(self): self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_path) # Create a pseudo image file and select it to be uploaded in form photo_content = 'A' * 1024 * 5 # A string of 5 KB size pseudo_image = StringIO(photo_content) ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # submit form # Even though the form could not be saved ... self.assertTrue( 'Required input is missing' in self.browser.contents) # ... the file has been successfully uploaded logfile = os.path.join( self.app['datacenter'].storage, 'logs', 'applicants.log') logcontent = open(logfile).read() self.assertTrue( 'zope.mgr - applicants.browser.ApplicantManageFormPage - ' '%s - saved: passport' % (self.applicant.applicant_id) in logcontent) def test_application_slip_with_non_jpg_image(self): IWorkflowState(self.applicant).setState('submitted') self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_path) # Create a pseudo image file and select it to be uploaded in form photo_content = 'A' * 1024 * 5 # A string of 5 KB size pseudo_image = StringIO(photo_content) ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # submit form self.browser.open(self.manage_path) self.browser.getLink("Download application slip").click() self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertMatches( '...Your image file is corrupted. Please replace...', self.browser.contents) def test_pay_portal_application_fee(self): self.login() self.browser.open(self.edit_path) self.fill_correct_values() # We have to save the form otherwise the filled fields will be cleared # after adding an online payment, because adding an online payment # requires a filled form but does not save it self.browser.getControl("Save").click() # Payment section does not appear if application fee isn't set self.assertFalse('Payment Tickets' in self.browser.contents) self.assertFalse('Add online payment' in self.browser.contents) self.applicantscontainer.application_fee = 200.0 self.browser.open(self.edit_path) self.browser.getControl("Add online payment ticket").click() # Session object missing self.assertTrue( 'Session configuration object is not available' in self.browser.contents) configuration = SessionConfiguration() configuration.academic_session = session_1 self.app['configuration'].addSessionConfiguration(configuration) self.browser.open(self.edit_path) self.browser.getControl("Add online payment ticket").click() self.assertMatches('...Payment ticket created...', self.browser.contents) self.assertMatches('...Activation Code...', self.browser.contents) self.assertTrue( '200.0' in self.browser.contents) # Payment ticket can be removed if they haven't received a # valid callback self.browser.open(self.edit_path) ctrl = self.browser.getControl(name='val_id') value = ctrl.options[0] ctrl.getControl(value=value).selected = True self.browser.getControl("Remove selected", index=0).click() self.assertMatches('...Successfully removed...', self.browser.contents) # We will try the callback request view self.browser.getControl("Add online payment ticket").click() self.browser.open(self.edit_path) ctrl = self.browser.getControl(name='val_id') value = ctrl.options[0] self.browser.getLink(value).click() self.assertMatches('...Amount Authorized...', self.browser.contents) payment_url = self.browser.url payment_id = self.applicant.keys()[0] payment = self.applicant[payment_id] self.assertEqual(payment.p_item,'This is the %s container' % container_name_1) self.assertEqual(payment.p_session, session_1) self.assertEqual(payment.p_category,'application') self.assertEqual(payment.amount_auth,200.0) # Applicant is payer of the payment ticket. self.assertEqual( IPayer(payment).display_fullname, 'John Anthony Tester') self.assertEqual( IPayer(payment).id, self.applicant.applicant_id) self.assertEqual(IPayer(payment).faculty, 'N/A') self.assertEqual(IPayer(payment).department, 'N/A') # The pdf payment slip can't yet be opened #self.browser.open(payment_url + '/payment_receipt.pdf') #self.assertMatches('...Ticket not yet paid...', # self.browser.contents) # Approve payment # Applicants can't approve payments self.assertRaises( Unauthorized, self.browser.open, payment_url + '/approve') # We approve the payment by bypassing the view payment.approve() # Applicant is is not yet in state 'paid' because it was only # the payment which we set to paid self.browser.open(self.view_path) self.assertMatches('...started...', self.browser.contents) self.assertTrue(self.applicant.state == 'started') # Let's logout and approve the payment as manager self.browser.getLink("Logout").click() self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') # First we reset the payment payment.r_amount_approved = 0.0 payment.r_code = u'' payment.p_state = 'unpaid' payment.r_desc = u'' payment.payment_date = None self.browser.open(payment_url) self.browser.getLink("Approve payment").click() self.assertEqual(payment.p_state, 'paid') self.assertEqual(payment.r_amount_approved, 200.0) self.assertEqual(payment.r_code, 'AP') self.assertTrue(self.applicant.state == 'paid') # Approval is logged in students.log ... logfile = os.path.join( self.app['datacenter'].storage, 'logs', 'applicants.log') logcontent = open(logfile).read() self.assertTrue( 'zope.mgr - applicants.browser.OnlinePaymentApprovePage - ' '%s - approved' % self.applicant.applicant_id in logcontent) # ... and in payments.log logfile = os.path.join( self.app['datacenter'].storage, 'logs', 'payments.log') logcontent = open(logfile).read() self.assertTrue( '"zope.mgr",%s,%s,application,200.0,AP,,,,,,\n' % (self.applicant.applicant_id, payment.p_id) in logcontent) # Payment slips can't be downloaded ... payment_id = self.applicant.keys()[0] self.browser.open(self.view_path + '/' + payment_id) self.browser.getLink("Download payment slip").click() self.assertTrue( 'Please submit the application form before trying to download payment slips.' in self.browser.contents) # ... unless form is submitted. self.browser.open(self.view_path + '/edit') image = open(SAMPLE_IMAGE, 'rb') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(image, filename='myphoto.jpg') self.browser.getControl(name="confirm_passport").value = True self.browser.getControl("Finally Submit").click() self.browser.open(self.view_path + '/' + payment_id) self.browser.getLink("Download payment slip").click() self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') return def prepare_special_container(self): # Add special application container container_name = u'special%s' % session_1 applicantscontainer = ApplicantsContainer() applicantscontainer.code = container_name applicantscontainer.prefix = 'special' applicantscontainer.year = session_1 applicantscontainer.title = u'This is a special app container' applicantscontainer.application_category = 'no' applicantscontainer.mode = 'create' applicantscontainer.strict_deadline = True delta = timedelta(days=10) applicantscontainer.startdate = datetime.now(pytz.utc) - delta applicantscontainer.enddate = datetime.now(pytz.utc) + delta self.app['applicants'][container_name] = applicantscontainer # Add an applicant applicant = createObject('waeup.Applicant') # reg_number is the only field which has to be preset here # because managers are allowed to edit this required field applicant.reg_number = u'12345' self.special_applicant = applicant self.app['applicants'][container_name].addApplicant(applicant) IUserAccount( self.app['applicants'][container_name][ applicant.application_number]).setPassword('apwd') # Add session configuration object self.configuration = SessionConfiguration() self.configuration.academic_session = session_1 #self.configuration.transcript_fee = 200.0 self.configuration.clearance_fee = 300.0 self.app['configuration'].addSessionConfiguration(self.configuration) def test_pay_special_fee(self): self.prepare_special_container() # Login self.browser.open(self.login_path) self.browser.getControl( name="form.login").value = self.special_applicant.applicant_id self.browser.getControl(name="form.password").value = 'apwd' self.browser.getControl("Login").click() applicant_path = self.browser.url self.browser.getLink("Edit application record").click() self.browser.getControl(name="form.firstname").value = 'John' self.browser.getControl(name="form.middlename").value = 'Anthony' self.browser.getControl(name="form.lastname").value = 'Tester' self.browser.getControl(name="form.special_application").value = [ 'transcript'] self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' self.browser.getControl(name="form.email").value = 'xx@yy.zz' self.configuration.transcript_fee = 0.0 self.browser.getControl("Save").click() self.browser.getControl("Add online payment ticket").click() self.assertMatches('...Amount could not be determined...', self.browser.contents) self.configuration.transcript_fee = 200.0 self.browser.getLink("Edit application record").click() self.browser.getControl("Add online payment ticket").click() self.assertMatches('...Payment ticket created...', self.browser.contents) self.assertTrue( 'Transcript Fee' in self.browser.contents) self.assertTrue( 'This is a special app container' in self.browser.contents) self.assertTrue( '200.0' in self.browser.contents) self.assertEqual(len(self.special_applicant.keys()), 1) # The applicant's workflow state is paid ... self.special_applicant.payments[0].approveApplicantPayment() self.assertEqual(self.special_applicant.state, 'paid') self.browser.open(applicant_path + '/edit') # ... but he can create further tickets. self.browser.getControl(name="form.special_application").value = [ 'clearance'] self.browser.getControl("Save").click() self.browser.getControl("Add online payment ticket").click() self.assertMatches('...Payment ticket created...', self.browser.contents) self.browser.open(applicant_path) self.assertTrue( 'Acceptance Fee' in self.browser.contents) self.assertEqual(len(self.special_applicant.keys()), 2) # Second payment can also be approved wthout error message flashtype, msg, log = self.special_applicant.payments[1].approveApplicantPayment() self.assertEqual(flashtype, 'success') self.assertEqual(msg, 'Payment approved') # Payment slips can't be downloaded ... payment_id = self.special_applicant.keys()[0] self.browser.open(applicant_path + '/' + payment_id) self.browser.getLink("Download payment slip").click() self.assertTrue( 'Please submit the application form before trying to download payment slips.' in self.browser.contents) # ... unless form is submitted. self.browser.open(applicant_path + '/edit') image = open(SAMPLE_IMAGE, 'rb') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(image, filename='myphoto.jpg') self.browser.getControl(name="confirm_passport").value = True self.browser.getControl("Finally Submit").click() self.browser.open(applicant_path + '/' + payment_id) self.browser.getLink("Download payment slip").click() self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') return def test_final_submit(self): # Make sure that a correctly filled form with passport picture # can be submitted (only) after payment self.login() self.browser.getLink("Edit application record").click() self.assertFalse('Finally Submit' in self.browser.contents) IWorkflowInfo(self.applicant).fireTransition('pay') self.browser.open(self.edit_path) self.assertTrue('Finally Submit' in self.browser.contents) self.fill_correct_values() # fill other fields with correct values self.browser.getControl("Save").click() self.browser.getControl("Finally Submit").click() # We forgot to upload a passport picture self.assertTrue( 'No passport picture uploaded' in self.browser.contents) # Use a real image file and select it to be uploaded in form image = open(SAMPLE_IMAGE, 'rb') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(image, filename='myphoto.jpg') self.browser.getControl("Finally Submit").click() # (finally) submit form # The picture has been uploaded but the form cannot be submitted # since the passport confirmation box was not ticked self.assertTrue( 'Passport picture confirmation box not ticked' in self.browser.contents) self.browser.getControl(name="confirm_passport").value = True # If application period has expired and strict-deadline is set # applicants do notsee edit button and can't open # the edit form. self.applicantscontainer.enddate = datetime.now(pytz.utc) self.browser.open(self.view_path) self.assertFalse( 'Edit application record' in self.browser.contents) self.browser.open(self.edit_path) self.assertTrue( 'form is locked' in self.browser.contents) # We can either postpone the enddate ... self.applicantscontainer.enddate = datetime.now( pytz.utc) + timedelta(days=10) self.browser.open(self.edit_path) self.browser.getControl(name="confirm_passport").value = True self.browser.getControl("Finally Submit").click() self.assertTrue( 'Application submitted' in self.browser.contents) # ... or allow submission after deadline. IWorkflowState(self.applicant).setState('paid') self.applicant.locked = False self.applicantscontainer.strict_deadline = False self.browser.open(self.edit_path) self.browser.getControl(name="confirm_passport").value = True self.browser.getControl("Finally Submit").click() self.assertTrue( 'Application submitted' in self.browser.contents) return def test_locking(self): # Make sure that locked forms can't be submitted self.login() self.browser.open(self.edit_path) self.fill_correct_values() # fill other fields with correct values # Create a pseudo image file and select it to be uploaded in form pseudo_image = StringIO('I pretend to be a graphics file') ctrl = self.browser.getControl(name='form.passport') file_ctrl = ctrl.mech_control file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') self.browser.getControl("Save").click() # Now we lock the form self.applicant.locked = True self.browser.open(self.edit_path) self.assertEqual(self.browser.headers['Status'], '200 Ok') self.assertTrue( 'The requested form is locked' in self.browser.contents) return def test_certificate_removed(self): self.login() self.browser.open(self.edit_path) self.fill_correct_values() self.browser.getControl("Save").click() self.browser.open(self.view_path) self.assertTrue( 'Unnamed Certificate' in self.browser.contents) self.browser.open(self.edit_path) self.assertTrue( '