##
## test_browser.py
## Login : <uli@pu.smp.net>
## Started on  Tue Mar 29 11:31:11 2011 Uli Fouquet
## $Id$
## 
## 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
from mechanize import LinkNotFoundError
from zope.component import createObject
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
from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
from waeup.sirp.app import University
from waeup.sirp.applicants.container import ApplicantsContainer
from waeup.sirp.applicants.applicant import Applicant
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.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.ac_prefix = 'APP'
        applicantscontainer.prefix = 'app'
        applicantscontainer.year = 2009
        applicantscontainer.application_category = 'basic'
        self.app['applicants']['app2009'] = applicantscontainer

        # Populate university
        certificate = createObject('waeup.Certificate')
        certificate.code = 'CERT1'
        certificate.application_category = 'basic'
        self.app['faculties']['fac1'] = Faculty()
        self.app['faculties']['fac1']['dep1'] = Department()
        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()
        self.pin_applicant = unicode(self.pins[1])
        self.applicant.access_code = self.pin_applicant
        app['applicants']['app2009'][self.pin_applicant] = self.applicant

    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)

    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)

    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(self.manage_root_path)
        self.browser.getControl(name="user").value = ['bob']
        self.browser.getControl(name="local_role").value = [
            'waeup.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.ac_prefix").value = ['APP']
        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.open(self.add_container_path)
        self.browser.getControl("Cancel").click()
        self.assertEqual(self.browser.url, self.manage_root_path + '#tab-1')
        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()
        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

    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.ac_series").value = self.existing_series
        self.browser.getControl(name="form.ac_number").value = self.existing_number
        self.browser.getControl("Create application record").click()
        self.assertTrue('Application initialized' in self.browser.contents)
        self.browser.open(self.add_applicant_path)
        self.browser.getControl(name="form.ac_series").value = '123'
        self.browser.getControl(name="form.ac_number").value = '456'
        self.browser.getControl("Create application record").click()
        self.assertTrue('is not a valid access code' 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')
        ctrl.getControl(value=self.existing_pin).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)
        existing_pin = self.pins[2]
        parts = existing_pin.split('-')[1:]
        existing_series, existing_number = parts
        self.browser.getControl(name="form.ac_series").value = existing_series
        self.browser.getControl(name="form.ac_number").value = existing_number
        self.browser.getControl("Create application record").click()
        self.assertTrue('Application initialized' in self.browser.contents)
        self.browser.open(self.container_manage_path)
        self.assertTrue(existing_pin in self.browser.contents)
        del self.app['applicants']['app2009'][existing_pin]
        ctrl = self.browser.getControl(name='val_id')
        ctrl.getControl(value=existing_pin).selected = True
        self.browser.getControl("Remove selected", index=0).click()
        self.assertMatches('...Could not delete...', self.browser.contents)
        return

    def test_manage_and_view_applicant(self):
        # Managers can manage applicants
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.applicant_path = 'http://localhost/app/applicants/app2009/%s' % self.pin_applicant
        self.applicant_view_path = self.applicant_path + '/index'
        self.applicant_manage_path = self.applicant_path + '/edit_full'
        self.applicant_slip_path = self.applicant_path + '/application_slip.pdf'
        self.browser.open(self.applicant_manage_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.browser.getControl(name="form.email").value = 'abc'
        self.browser.getControl("Save").click()
        self.assertMatches('...Invalid email address...', self.browser.contents)
        self.browser.getControl(name="form.email").value = 'abc@def.gh'
        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.course_admitted").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="transition").value = ['start']
        self.browser.getControl("Save").click()
        self.assertMatches('...Form has been saved...', self.browser.contents)
        self.assertMatches('...Application started by zope.mgr...', self.browser.contents)
        self.browser.open(self.applicant_view_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.browser.open(self.applicant_slip_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
        self.browser.open(self.applicant_manage_path)
        self.browser.getControl(name="form.course_admitted").value = []
        self.browser.getControl("Save").click()
        self.browser.open(self.applicant_slip_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
        return

    def test_view_applicant(self):
        # Applicants can login and view their application
        self.login_path = 'http://localhost/app/applicants/app2009/login'
        self.browser.open(self.login_path)
        pin = self.pins[2]
        parts = pin.split('-')[1:]
        existing_series, existing_number = parts
        ac_series = self.browser.getControl(name="form.ac_series")
        ac_series.value = existing_series
        ac_number = self.browser.getControl(name="form.ac_number")
        ac_number.value = existing_number
        self.browser.getControl(name="SUBMIT").click()
        self.assertTrue(self.browser.url != self.login_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        return

    def test_passport_edit_view(self):
        # We get a default image after login
        login_path = 'http://localhost/app/applicants/app2009/login'
        self.browser.open(login_path)
        pin = self.pins[2]
        parts = pin.split('-')[1:]
        existing_series, existing_number = parts
        ac_series = self.browser.getControl(name="form.ac_series")
        ac_series.value = existing_series
        ac_number = self.browser.getControl(name="form.ac_number")
        ac_number.value = existing_number
        self.browser.getControl(name="SUBMIT").click()
        pin = self.pins[2]
        appl = self.getRootFolder()['app']['applicants']['app2009']
        appl = appl[pin]
        passp = appl.passport
        #import pdb; pdb.set_trace()
        passp_len = len(passp.file.read())
        self.assertEqual(passp_len, PH_LEN)
        #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg')
        image_url = "%s/%s" % (self.browser.url, 'passport.jpg')
        #self.browser.open(image_url)
        self.browser.open('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.login_path = 'http://localhost/app/applicants/app2009/login'
        self.browser.open(self.login_path)
        pin = self.pins[2]
        parts = pin.split('-')[1:]
        existing_series, existing_number = parts
        ac_series = self.browser.getControl(name="form.ac_series")
        ac_series.value = existing_series
        ac_number = self.browser.getControl(name="form.ac_number")
        ac_number.value = existing_number
        self.browser.getControl(name="SUBMIT").click()
        self.assertTrue(self.browser.url != self.login_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.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("Save").click()
        self.assertMatches('...Form has been saved...', 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('Data 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_local_roles_add_delete(self):
        # Managers can assign and delete local roles of applicants containers
        myusers = self.app['users']
        myusers.addUser('bob', 'bobssecret')
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="user").value = ['bob']
        self.browser.getControl(name="local_role").value = [
            'waeup.ApplicationsOfficer']
        self.browser.getControl("Add local role").click()
        self.assertTrue('<td>bob</td>' in self.browser.contents)
        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

class LoginTest(FunctionalTestCase):
    # Here we check login view of applicants containers.
    #
    # Tests in here do only cover login attempts without any PINs
    # created before.

    layer = FunctionalLayer

    def setUp(self):
        super(LoginTest, self).setUp()

        # Setup a sample site for each test
        app = University()
        self.dc_root = tempfile.mkdtemp()
        app['datacenter'].setStoragePath(self.dc_root)
        self.login_path = 'http://localhost/app/applicants/testapplicants/login'

        # Add an applicants container where we can login (or not)
        applicantscontainer = ApplicantsContainer()
        applicantscontainer.ac_prefix = 'APP'
        app['applicants']['testapplicants'] = applicantscontainer

        # Put the prepopulated site into test ZODB and prepare test
        # browser
        self.getRootFolder()['app'] = app
        self.browser = Browser()
        self.browser.handleErrors = False

    def tearDown(self):
        super(LoginTest, self).tearDown()
        shutil.rmtree(self.dc_root)

    def test_anonymous_access(self):
        # Anonymous users can access a login page
        self.browser.open(self.login_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        return

    def test_anonymous_invalid_creds(self):
        # Anonymous users giving invalid credentials stay at the page
        self.browser.open(self.login_path)
        # We do not give credentials but send the form as-is
        submit = self.browser.getControl(name='SUBMIT')
        submit.click()
        # We are still at the same page...
        self.assertEqual(self.browser.url, self.login_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        return

    def test_anonymous_invalid_creds_warning(self):
        # Entering wrong credentials will yield a warning
        self.browser.open(self.login_path)
        # We do not give credentials but send the form as-is
        submit = self.browser.getControl(name='SUBMIT')
        submit.click()
        self.assertTrue(
            'Entered credentials are invalid' in self.browser.contents)
        return

    def test_manager_no_warnings(self):
        # Browsing the login screen as a manager, won't raise warnings
        # Authenticate ourself as manager
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.login_path)
        # Submit the form w/o any credentials
        self.browser.getControl(name="SUBMIT").click()
        self.assertTrue(
            'Entered credentials are invalid' not in self.browser.contents)
        return

    def test_manager_no_redirect(self):
        # Browsing the login screen as a manager won't trigger a redirect
        # Authenticate ourself as manager
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.login_path)
        # Submit the form w/o any credentials
        self.browser.getControl(name="SUBMIT").click()
        self.assertEqual(self.browser.url, self.login_path)
        return

    def test_display_entered_values(self):
        # After submit the entered values are displayed in the form
        self.browser.open(self.login_path)
        # Enter some value we can look for after submit
        ac_series = self.browser.getControl(name="form.ac_series")
        ac_series.value = '666'
        self.browser.getControl(name="SUBMIT").click()
        self.assertTrue('666' in self.browser.contents)
        return

class LoginTestWithPINs(LoginTest):
    # Here we check login view of applicants containers with PINs provided.

    # As setting up pins is time-consuming we only set them up when
    # really needed (i.e. in this separate TestCase).

    layer = FunctionalLayer

    def setUp(self):
        super(LoginTestWithPINs, self).setUp()

        # Create 5 access codes with prefix'FOO' and cost 9.99 each
        pin_container = self.getRootFolder()['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
        self.browser.handleErrors = False

    def tearDown(self):
        super(LoginTestWithPINs, self).tearDown()

    def test_anonymous_valid_login(self):
        # If we enter valid credentials, we get to the applicants form
        self.browser.open(self.login_path)
        # Enter some value we can look for after submit
        ac_series = self.browser.getControl(name="form.ac_series")
        ac_series.value = self.existing_series
        ac_number = self.browser.getControl(name="form.ac_number")
        ac_number.value = self.existing_number
        self.browser.getControl(name="SUBMIT").click()
        # We should be redirected to applicants form.
        self.assertTrue(self.browser.url != self.login_path)
        # Applicants see their Access Code in the contact form
        self.browser.getLink("Contact").click()
        self.assertTrue(
            'Access Code:' in self.browser.contents)
        return

    def test_anonymous_invalid_login(self):
        # If we enter wrong credentials we won't get far
        self.browser.open(self.login_path)
        # Enter some value we can look for after submit
        ac_series = self.browser.getControl(name="form.ac_series")
        ac_series.value = 'illegal series'
        ac_number = self.browser.getControl(name="form.ac_number")
        ac_number.value = 'invalid number'
        self.browser.getControl(name="SUBMIT").click()
        # We get a warning message
        self.assertTrue(
            'Entered credentials are invalid' in self.browser.contents)
        # We stay at the login page (no redirect)
        self.assertTrue(self.browser.url == self.login_path)
        return

class ApplicantsPassportTests(ApplicantsFullSetup):
    # Tests for uploading/browsing the passport image of appplicants

    layer = FunctionalLayer

    def setUp(self):
        super(ApplicantsPassportTests, self).setUp()
        self.login_path = 'http://localhost/app/applicants/app2009/login'
        self.pin = self.pins[2]
        self.existing_series, self.existing_number = self.pin.split('-')[1:]
        self.edit_path = 'http://localhost/app/applicants/app2009/%s/edit' % (
            self.pin)
        self.edit_full_path = 'http://localhost/app/applicants/%s/%s/%s' % (
            'app2009', self.pin, 'edit_full')

    def tearDown(self):
        super(ApplicantsPassportTests, self).tearDown()

    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)
        ac_series = self.browser.getControl(name="form.ac_series")
        ac_series.value = self.existing_series
        ac_number = self.browser.getControl(name="form.ac_number")
        ac_number.value = self.existing_number
        self.browser.getControl(name="SUBMIT").click()
        self.applicant = self.app['applicants']['app2009'][self.pin]

    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']

    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.edit_path)
        # There is a correct <img> link included
        self.assertTrue(
            '<img src="placeholder_m.jpg" />' in self.browser.contents)
        # Browsing the link shows a real image
        self.browser.open(self.image_url('placeholder_m.jpg'))
        self.assertEqual(
            self.browser.headers['content-type'], 'image/jpeg')
        self.assertEqual(len(self.browser.contents), PH_LEN)

    def test_after_login_default_stored_in_imagestorage(self):
        # After login the applicants placeholder image is stored in
        # an imagestorage
        storage = self.app['images']
        # In the beginning, the storage is empty
        self.assertEqual([x for x in storage.keys()], [])
        self.login()
        # After login, it is filled
        self.assertEqual(
            [x for x in storage.keys()],
            [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])
        file_id = self.applicant.passport.data
        self.assertEqual(
            file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
        # The stored image can be fetched
        fd = storage.retrieveFile(file_id)
        file_len = len(fd.read())
        self.assertEqual(file_len, PH_LEN)

    def test_after_submit_default_browsable(self):
        # After submitting an applicant form the default image is
        # still visible
        self.login()
        self.browser.getControl("Save").click() # submit form
        # There is a correct <img> link included
        self.assertTrue(
            '<img src="placeholder_m.jpg" />' in self.browser.contents)
        # Browsing the link shows a real image
        self.browser.open(self.image_url('placeholder_m.jpg'))
        self.assertEqual(
            self.browser.headers['content-type'], 'image/jpeg')
        self.assertEqual(len(self.browser.contents), PH_LEN)

    def test_after_submit_default_stored_in_imagestorage(self):
        # After submitting an applicant form the default image is
        # correctly stored in an imagestorage
        self.login()
        self.browser.getControl("Save").click() # submit form
        storage = self.app['images']
        self.assertEqual(
            [x for x in storage.keys()],
            [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])
        file_id = self.applicant.passport.data
        self.assertEqual(
            file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
        # The stored image can be fetched
        fd = storage.retrieveFile(file_id)
        file_len = len(fd.read())
        self.assertEqual(file_len, PH_LEN)

    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()
        # 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="myphoto.jpg" />' in self.browser.contents)
        # Browsing the link shows a real image
        self.browser.open(self.image_url('myphoto.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()
        # 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 = self.app['images']
        self.assertTrue(
            u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
        # The stored image can be fetched
        fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
        file_len = len(fd.read())
        self.assertEqual(file_len, 31)
        # The image uploaded belongs to the applicant
        file_id = self.applicant.passport.data
        self.assertEqual(
            file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')

    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.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="myphoto.jpg" />' in self.browser.contents)
        # Browsing the link shows a real image
        self.browser.open(self.image_url('myphoto.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.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 = self.app['images']
        self.assertTrue(
            u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
        # The stored image can be fetched
        fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
        #fd = storage.retrieveFile(file_id)
        file_len = len(fd.read())
        self.assertEqual(file_len, 31)
        # The image uploaded belongs to the applicant
        file_id = self.applicant.passport.data
        self.assertEqual(
            file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')

    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() # Create applicant form
        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.edit_full_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')
        passport0 = self.applicant.passport
        self.browser.getControl("Save").click() # submit form with changed pic
        passport1 = self.applicant.passport
        self.browser.getControl("Save").click() # submit form w/o changes
        passport2 = self.applicant.passport
        self.assertTrue(passport0 != passport1)
        self.assertTrue(passport1 == passport2)
        self.assertTrue(passport1 is passport2)
        return

    def test_final_submit(self):
        # Make sure that a correctly filled form with passport picture
        # can be submitted
        self.login() # Create applicant form
        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("Final Submit").click() # (finally) submit form
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertTrue(
            'Passport confirmation box not ticked' in self.browser.contents)
        self.browser.getControl(name="confirm_passport").value = True
        IWorkflowInfo(self.applicant).fireTransition('submit')
        self.browser.getControl("Final Submit").click() # submit form again
        self.assertTrue(
            'Wrong state' in self.browser.contents)
        IWorkflowInfo(self.applicant).fireTransition('reset1')
        # Now do the whole thing again but with correct state
        self.login() 
        self.fill_correct_values() 
        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(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)
        #import pdb; pdb.set_trace()
        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() # Create applicant form
        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()
        self.browser.getLink("Logout").click()

        # Login as manager and lock the form
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.edit_full_path)
        self.browser.getControl(name="form.locked").value = True
        self.browser.getControl("Save").click()
        self.browser.getLink("Logout").click()

        # Login as applicant again and try to open the edit form
        self.login()
        self.browser.open(self.edit_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        #print self.browser.contents
        self.assertTrue(
            'The requested form is locked' in self.browser.contents)
        return
