## $Id: test_browser.py 7685 2012-02-23 08:44:57Z 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 shutil
import tempfile
from StringIO import StringIO
from datetime import datetime, date, timedelta
from mechanize import LinkNotFoundError
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.sirp.testing import FunctionalLayer, FunctionalTestCase
from waeup.sirp.app import University
from waeup.sirp.configuration import SessionConfiguration
from waeup.sirp.applicants.container import ApplicantsContainer
from waeup.sirp.applicants.applicant import Applicant
from waeup.sirp.interfaces import (
    IExtFileStore, IFileStoreNameChooser, IUserAccount)
from waeup.sirp.university.faculty import Faculty
from waeup.sirp.university.department import Department

PH_LEN = 2059  # Length of placeholder file

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.manage_root_path = self.root_path + '/@@manage'
        self.add_container_path = self.root_path + '/@@add'
        self.container_path = 'http://localhost/app/applicants/app2009'
        self.manage_container_path = self.container_path + '/@@manage'

        # Add an applicants container
        applicantscontainer = ApplicantsContainer()
        applicantscontainer.code = u'app2009'
        applicantscontainer.prefix = 'app'
        applicantscontainer.year = 2009
        applicantscontainer.application_category = 'basic'
        delta = timedelta(days=10)
        applicantscontainer.startdate = date.today() - delta
        applicantscontainer.enddate = date.today() + delta
        self.app['applicants']['app2009'] = applicantscontainer
        self.applicantscontainer = self.app['applicants']['app2009']

        # Populate university
        certificate = createObject('waeup.Certificate')
        certificate.code = 'CERT1'
        certificate.application_category = 'basic'
        certificate.start_level = 100
        certificate.end_level = 500
        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='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 = Applicant()
        # reg_number is the only field which has to be preset here
        # because managers are allowed to edit this required field
        self.applicant.reg_number = u'1234'
        app['applicants']['app2009'].addApplicant(self.applicant)
        IUserAccount(
            self.app['applicants']['app2009'][
            self.applicant.application_number]).setPassword('apwd')
        self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % (
            'app2009', self.applicant.application_number, 'manage')
        self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % (
            'app2009', self.applicant.application_number, 'edit')
        self.view_path = 'http://localhost/app/applicants/%s/%s' % (
            'app2009', self.applicant.application_number)

    def login(self):
        # Perform an applicant login. This creates an applicant 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.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.lga").value = ['foreigner']
        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 application 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 application section' in self.browser.contents)
        # There is a manage link
        link = self.browser.getLink('Manage application section')
        link.click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.manage_root_path)
        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

    # We have no local roles yet
    #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(self.manage_root_path)
    #    self.browser.getControl(name="user").value = ['bob']
    #    self.browser.getControl(name="local_role").value = [
    #        'waeup.local.ApplicationsOfficer']
    #    self.browser.getControl("Add local role").click()
    #    self.assertTrue('<td>bob</td>' in self.browser.contents)
    #    # Remove the role assigned
    #    ctrl = self.browser.getControl(name='role_id')
    #    ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True
    #    self.browser.getControl("Remove selected local roles").click()
    #    self.assertTrue('Successfully removed:' in self.browser.contents)
    #    self.assertFalse('<td>bob</td>' 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("Cancel").click()
        self.assertEqual(self.browser.url, self.root_path)
        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 = ['2010']
        self.browser.getControl(name="form.provider").value = [
            'waeup.sirp.applicants.ApplicantsContainer']
        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("app2010").click()
        self.assertTrue('<h1>General Studies 2010/2011</h1>'
            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 = ['2010']
        self.browser.getControl(name="form.provider").value = [
            'waeup.sirp.applicants.ApplicantsContainer']
        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='app2010').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 = ['2010']
        self.browser.getControl(name="form.provider").value = [
            'waeup.sirp.applicants.ApplicantsContainer']
        #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']['app2010']
        ctrl = self.browser.getControl(name='val_id')
        ctrl.getControl(value='app2010').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("Save").click()
        self.assertTrue('Form has been saved' in self.browser.contents)
        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_add_delete_applicants(self):
        # 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.getControl("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)
        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)
        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

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)
        # Open pdf slip
        self.browser.open(self.slip_path)
        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_edit_applicant(self):
        # Applicants can edit their record
        self.browser.open(self.login_path)
        self.login()
        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.browser.getControl("Save").click()
        self.assertMatches('...Form has been saved...', 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 <img> link included
        self.assertTrue(
              '<img src="passport.jpg" />' 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 <img> link included
        self.assertTrue(
            '<img src="passport.jpg" />' 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 * 21  # A string of 21 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 <img> link included
        self.assertTrue(
            '<img src="passport.jpg" />' 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 <img> link included
        self.assertTrue(
            '<img src="passport.jpg" />' 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 <img> link included
        self.assertTrue(
            '<img src="passport.jpg" />' 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)

    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_pay_acceptance_fee(self):
        self.login()
        self.browser.open(self.edit_path)
        # Payment tickets can't be created before the form has been validated
        self.browser.getControl("Add online payment ticket").click()
        self.assertTrue('Required input is missing' in self.browser.contents)
        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()
        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 = 2009
        configuration.acceptance_fee = 200
        self.app['configuration'].addSessionConfiguration(configuration)
        self.browser.open(self.edit_path)
        self.fill_correct_values()
        self.browser.getControl("Add online payment ticket").click()
        self.assertMatches('...Payment ticket created...',
                           self.browser.contents)
        # Payment ticket can be removed if they haven't received a
        # valid callback
        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()
        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
        # The pdf payment receipt can't yet be opened
        self.browser.open(payment_url + '/payment_receipt.pdf')
        self.assertMatches('...Ticket not yet paid...',
                           self.browser.contents)
        # Request callback
        self.browser.open(payment_url)
        self.browser.getLink("Request callback").click()
        self.assertMatches('...Valid callback received...',
                          self.browser.contents)
        # Callback can't be applied twice
        self.browser.open(payment_url + '/callback')
        self.assertMatches(
            "...Transition 'pay' requires 'started' as source state...",
            self.browser.contents)
        # The payment receipt can be downloaded now
        self.browser.open(payment_url)
        self.browser.getLink("Download payment receipt").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'],
                         'application/pdf')
        # Applicant is is in state 'paid'
        self.browser.open(self.view_path)
        self.assertMatches('...paid...',
                           self.browser.contents)
        state = IWorkflowState(self.applicant).getState()
        self.assertTrue(state == 'paid')

    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('Final Submit' in self.browser.contents)
        IWorkflowInfo(self.applicant).fireTransition('pay')
        self.browser.open(self.edit_path)
        self.assertTrue('Final Submit' in self.browser.contents)
        self.fill_correct_values() # fill other fields with correct values
        self.browser.getControl("Final Submit").click()
        # We forgot to upload a passport picture
        self.assertTrue(
            'No passport picture uploaded' in self.browser.contents)
        # 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("Final 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
        self.browser.getControl("Final Submit").click()
        self.assertTrue(
            '... submitted ...' in self.browser.contents)
        self.browser.goBack(count=1)
        self.browser.getControl("Save").click()
        self.assertTrue(
            'The requested form is locked' in self.browser.contents)
        self.browser.goBack(count=1)
        self.browser.getControl("Final Submit").click()
        self.assertTrue(
            'The requested form is locked' 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(
            '<option selected="selected" value="CERT1">' in self.browser.contents)
        # Now we remove the certificate
        del self.app['faculties']['fac1']['dep1'].certificates['CERT1']
        # The certificate is still shown in display mode
        self.browser.open(self.view_path)
        self.assertTrue(
            'Unnamed Certificate' in self.browser.contents)
        # The certificate is still selectable in edit mode so that it won't
        # be automatically replaced by another (arbitrary) certificate
        self.browser.open(self.edit_path)
        self.assertTrue(
            '<option selected="selected" value="CERT1">' in self.browser.contents)
        # Consequently, the certificate is still shown after saving the form
        self.browser.getControl("Save").click()
        self.browser.open(self.view_path)
        self.assertTrue(
            'Unnamed Certificate' in self.browser.contents)
        # Even if we add a new certificate the previous (removed)
        # certificate is shown
        certificate = createObject('waeup.Certificate')
        certificate.code = 'CERT2'
        certificate.title = 'New Certificate'
        certificate.application_category = 'basic'
        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
            certificate)
        self.browser.open(self.edit_path)
        self.assertTrue(
            '<option selected="selected" value="CERT1">' in self.browser.contents)

class ApplicantRegisterTests(ApplicantsFullSetup):
    # Tests for applicant registration

    layer = FunctionalLayer

    def test_register_applicant(self):
        # An applicant can register himself.
        self.browser.open(self.container_path)
        self.browser.getLink("Register for application").click()
        # Fill the edit form with suitable values
        self.browser.getControl(name="form.firstname").value = 'John'
        self.browser.getControl(name="form.lastname").value = 'Tester'
        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
        self.browser.getControl(name="form.phone.country").value = '234'
        self.browser.getControl(name="form.phone.area").value = '555'
        self.browser.getControl(name="form.phone.extension").value = '6666666'
        self.browser.getControl("Get login credentials").click()
        self.assertEqual(self.browser.url,
            self.container_path + '/registration_complete?email=xx%40yy.zz')
        return

    def test_register_applicant_wo_phone(self):
        # We don't require the phone number when registering
        self.browser.open(self.container_path)
        self.browser.getLink("Register for application").click()
        # Fill the edit form with suitable values
        self.browser.getControl(name="form.firstname").value = 'John'
        self.browser.getControl(name="form.lastname").value = 'Tester'
        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
        self.browser.getControl("Get login credentials").click()
        self.assertEqual(self.browser.url,
            self.container_path + '/registration_complete?email=xx%40yy.zz')
        return
