## $Id: test_browser.py 12772 2015-03-16 10:02:14Z henrik $
##
## Copyright (C) 2014 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 customer-related UI components.
"""
import shutil
import tempfile
import logging
import base64
from decimal import Decimal
from datetime import datetime, timedelta, date
from StringIO import StringIO
import os
import grok
from zc.async.testing import wait_for_result
from zope.event import notify
from zope.component import createObject, queryUtility, getUtility
from zope.component.hooks import setSite, clearSite
from zope.schema.interfaces import ConstraintNotSatisfied
from zope.catalog.interfaces import ICatalog
from zope.security.interfaces import Unauthorized
from zope.securitypolicy.interfaces import IPrincipalRoleManager
from zope.testbrowser.testing import Browser
from hurry.workflow.interfaces import (
    IWorkflowInfo, IWorkflowState, InvalidTransitionError)
from waeup.ikoba.testing import FunctionalLayer, FunctionalTestCase
from waeup.ikoba.app import Company
from waeup.ikoba.interfaces import (
    IUserAccount, IJobManager, APPROVED, SUBMITTED,
    IFileStoreNameChooser, NotIdValue)
from waeup.ikoba.imagestorage import ExtFileStore
from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
from waeup.ikoba.interfaces import VERIFIED
from waeup.ikoba.browser.tests.test_pdf import samples_dir
from waeup.ikoba.products.productoptions import ProductOption
from waeup.ikoba.customers.interfaces import CurrencyMismatch
from waeup.ikoba.payments.payment import Payment
from waeup.ikoba.payments.interfaces import IPayer, IPayable, STATE_PAID

PH_LEN = 15911  # Length of placeholder file

SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
SAMPLE_PDF = os.path.join(os.path.dirname(__file__), 'test_pdf.pdf')


def lookup_submit_value(name, value, browser):
    """Find a button with a certain value."""
    for num in range(0, 100):
        try:
            button = browser.getControl(name=name, index=num)
            if button.value.endswith(value):
                return button
        except IndexError:
            break
    return None


class CustomersFullSetup(FunctionalTestCase):
    # A test case that only contains a setup and teardown
    #
    # Complete setup for customers handlings is rather complex and
    # requires lots of things created before we can start. This is a
    # setup that does all this, creates a company etc.
    # so that we do not have to bother with that in different
    # test cases.

    layer = FunctionalLayer

    def setup_customizable_params(self):
        self._contract_category = u'sample'
        self._document_factory = 'waeup.CustomerSampleDocument'
        self._contract_factory = 'waeup.SampleContract'
        return

    def setUp(self):
        super(CustomersFullSetup, self).setUp()
        self.setup_customizable_params()
        # Setup a sample site for each test
        app = Company()
        self.dc_root = tempfile.mkdtemp()
        app['datacenter'].setStoragePath(self.dc_root)

        # Prepopulate the ZODB...
        self.getRootFolder()['app'] = app
        # we add the site immediately after creation to the
        # ZODB. Catalogs and other local utilities are not setup
        # before that step.
        self.app = self.getRootFolder()['app']
        # Set site here. Some of the following setup code might need
        # to access grok.getSite() and should get our new app then
        setSite(app)

        # Add some products
        self.product = createObject('waeup.Product')
        self.product.product_id = u'SAM'
        self.product.title = u'Our Sample Product'
        self.product.contract_category = self._contract_category
        self.product.valid_from = date(2014, 12, 4)
        self.product.tc_dict = {'en': u'Hello world'}
        prodoption = ProductOption()
        prodoption.title = u'First option'
        prodoption.fee = Decimal('99.9')
        prodoption.currency = 'USD'
        self.product.options = [prodoption, ]
        self.app['products'].addProduct(self.product)

        # Add customer with subobjects
        customer = createObject('waeup.Customer')
        customer.firstname = u'Anna'
        customer.lastname = u'Tester'
        customer.reg_number = u'123'
        customer.sex = u'm'
        customer.email = u'aa@aa.ng'
        customer.phone = u'1234'
        customer.date_of_birth = date(1981, 2, 4)
        self.app['customers'].addCustomer(customer)
        self.customer_id = customer.customer_id
        self.customer = self.app['customers'][self.customer_id]
        self.document = createObject(self._document_factory)
        self.document.title = u'My first document'
        self.document.document_id = u'DOC1'
        self.assertRaises(
            NotIdValue, setattr, self.document, 'document_id',
            u'id with spaces')
        self.customer['documents'].addDocument(self.document)
        self.contract = createObject(self._contract_factory)
        self.contract.contract_id = u'CON1'
        self.assertRaises(
            NotIdValue, setattr, self.contract, 'contract_id',
            u'id with spaces')
        self.customer['contracts'].addContract(self.contract)

        # Set password
        IUserAccount(
            self.app['customers'][self.customer_id]).setPassword('cpwd')

        self.login_path = 'http://localhost/app/login'
        self.container_path = 'http://localhost/app/customers'
        self.manage_container_path = self.container_path + '/@@manage'
        self.add_customer_path = self.container_path + '/addcustomer'
        self.customer_path = self.container_path + '/' + self.customer_id
        self.manage_customer_path = self.customer_path + '/manage_base'
        self.trigtrans_path = self.customer_path + '/trigtrans'
        self.history_path = self.customer_path + '/history'
        self.documents_path = self.customer_path + '/documents'
        self.contracts_path = self.customer_path + '/contracts'

        # Update the catalog
        notify(grok.ObjectModifiedEvent(self.customer))

        # Put the prepopulated site into test ZODB and prepare test
        # browser
        self.browser = Browser()
        self.browser.handleErrors = False

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


class CustomersContainerUITests(CustomersFullSetup):
    # Tests for CustomersContainer class views and pages

    layer = FunctionalLayer

    def test_anonymous_access(self):
        # Anonymous users can't access customers containers
        self.assertRaises(
            Unauthorized, self.browser.open, self.container_path)
        self.assertRaises(
            Unauthorized, self.browser.open, self.manage_container_path)
        return

    def test_manage_access(self):
        # Managers can access the view page of customers
        # containers and can perform actions
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.container_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.container_path)
        self.browser.getLink("Manage customer section").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.manage_container_path)
        return

    def test_add_search_delete_customers(self):
        # Managers can add search and remove customers
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.manage_container_path)
        self.browser.getLink("Add customer").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.add_customer_path)
        self.browser.getControl(name="form.firstname").value = 'Bob'
        self.browser.getControl(name="form.lastname").value = 'Tester'
        self.browser.getControl(name="form.reg_number").value = '123'
        self.browser.getControl("Create customer record").click()
        self.assertTrue('Registration number in use.'
            in self.browser.contents)
        self.browser.getControl(name="form.reg_number").value = '1234'
        self.browser.getControl("Create customer").click()
        self.assertTrue('Customer created' in self.browser.contents)

        # Registration must be unique
        self.browser.getLink("Manage").click()
        self.browser.getControl(name="form.reg_number").value = '123'
        self.browser.getControl("Save").click()
        self.assertMatches('...Registration number in use...',
                           self.browser.contents)

        # We can find a customer with a certain customer_id
        self.browser.open(self.container_path)
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Empty search string' in self.browser.contents)
        self.browser.getControl(name="searchtype").value = ['customer_id']
        self.browser.getControl(name="searchterm").value = self.customer_id
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)

        # We can find a customer by searching for all kind of name parts
        self.browser.open(self.manage_container_path)
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Empty search string' in self.browser.contents)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'Anna Tester'
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'Anna'
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'Tester'
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'An'
        self.browser.getControl("Find customer(s)").click()
        self.assertFalse('Anna Tester' in self.browser.contents)
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'An*'
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'tester'
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'Tester Ana'
        self.browser.getControl("Find customer(s)").click()
        self.assertFalse('Anna Tester' in self.browser.contents)
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'Tester Anna'
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)
        # The old searchterm will be used again
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)

        # We can find suspended customers
        self.customer.suspended = True
        notify(grok.ObjectModifiedEvent(self.customer))
        self.browser.open(self.manage_container_path)
        self.browser.getControl(name="searchtype").value = ['suspended']
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Anna Tester' in self.browser.contents)

        # Removed customers won't be found
        ctrl = self.browser.getControl(name='entries')
        ctrl.getControl(value=self.customer_id).selected = True
        self.browser.getControl("Remove selected", index=0).click()
        self.assertTrue('Successfully removed' in self.browser.contents)
        self.browser.getControl(name="searchtype").value = ['customer_id']
        self.browser.getControl(name="searchterm").value = self.customer_id
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('No customer found' in self.browser.contents)
        return


class OfficerUITests(CustomersFullSetup):
    # Tests for Customer class views and pages

    def setup_logging(self):
        # setup a log-handler that catches all fake mailer output
        self.stream = StringIO()
        handler = logging.StreamHandler(self.stream)
        logger = logging.getLogger('test.smtp')
        logger.addHandler(handler)
        logger.setLevel(logging.INFO)
        return

    def get_fake_smtp_output(self):
        # get output generated by fake mailer
        self.stream.flush()
        self.stream.seek(0)
        return self.stream.read()

    def teardown_logging(self):
        # remove the log handler for fake mailer output
        logger = logging.getLogger('test.smtp')
        handlers = [x for x in logger.handlers]
        for handler in handlers:
            logger.removeHandler(handler)
        return

    def test_basic_auth(self):
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open('http://localhost/app')
        self.browser.getLink("Logout").click()
        self.assertTrue('You have been logged out' in self.browser.contents)
        # But we are still logged in since we've used basic
        # authentication here.  Wikipedia says: Existing browsers
        # retain authentication information until the tab or browser
        # is closed or the user clears the history.  HTTP does not
        # provide a method for a server to direct clients to discard
        # these cached credentials. This means that there is no
        # effective way for a server to "log out" the user without
        # closing the browser. This is a significant defect that
        # requires browser manufacturers to support a "logout" user
        # interface element ...
        self.assertTrue('Manager' in self.browser.contents)

    def test_basic_auth_base64(self):
        auth_token = base64.b64encode('mgr:mgrpw')
        self.browser.addHeader('Authorization', 'Basic %s' % auth_token)
        self.browser.open(self.manage_container_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')

    def test_manage_access(self):
        # Managers can access the pages of customers
        # and can perform actions
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.customer_path)
        self.browser.getLink("Transition").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        # Managers can trigger transitions
        self.browser.getControl(name="transition").value = ['start']
        self.browser.getControl("Apply").click()
        # Managers can edit base
        self.browser.open(self.customer_path)
        self.browser.getLink("Manage").click()
        self.assertEqual(self.browser.url, self.manage_customer_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.browser.getControl(name="form.firstname").value = 'John'
        self.browser.getControl(name="form.lastname").value = 'Tester'
        self.browser.getControl(name="form.reg_number").value = '345'
        self.browser.getControl(name="password").value = 'secret'
        self.browser.getControl(name="control_password").value = 'secret'
        self.browser.getControl("Save").click()
        self.assertMatches('...Form has been saved...',
                           self.browser.contents)

    def test_manage_contact_customer(self):
        # Managers can contact customer
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path)
        self.browser.getLink("Send email").click()
        self.browser.getControl(
            name="form.subject").value = 'Important subject'
        self.browser.getControl(name="form.body").value = 'Hello!'
        self.browser.getControl("Send message now").click()
        self.assertTrue('Your message has been sent' in self.browser.contents)
        return

    def test_manage_upload_passport(self):
        # Managers can upload a file via the CustomerBaseManageFormPage
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.manage_customer_path)
        image = open(SAMPLE_IMAGE_BMP, 'rb')
        ctrl = self.browser.getControl(name='passportmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(image, filename='my_photo.bmp')
        self.browser.getControl(
            name='upload_passportmanageupload').click()
        self.assertTrue('jpg file format expected'
            in self.browser.contents)
        ctrl = self.browser.getControl(name='passportmanageupload')
        file_ctrl = ctrl.mech_control
        image = open(SAMPLE_IMAGE, 'rb')
        file_ctrl.add_file(image, filename='my_photo.jpg')
        self.browser.getControl(
            name='upload_passportmanageupload').click()
        self.assertTrue(
            'src="http://localhost/app/customers/K1000000/passport.jpg"'
            in self.browser.contents)
        # We remove the passport file again
        self.browser.open(self.manage_customer_path)
        self.browser.getControl('Delete').click()
        self.assertTrue('passport.jpg deleted' in self.browser.contents)

    def test_manage_workflow_send_transition_information(self):
        # Managers can pass through the whole workflow
        config = grok.getSite()['configuration']
        config.email_notification = True
        self.setup_logging()
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.customer = self.app['customers'][self.customer_id]
        self.browser.open(self.trigtrans_path)
        self.browser.getControl(name="transition").value = ['start']
        self.browser.getControl("Apply").click()
        self.browser.open(self.trigtrans_path)
        self.browser.getControl(name="transition").value = ['request']
        self.browser.getControl("Apply").click()
        self.browser.open(self.trigtrans_path)
        self.browser.getControl(name="transition").value = ['reject']
        self.browser.getControl("Apply").click()
        # The customer is being notified by email after rejection
        expected = [
            u'Sending email from no-reply@waeup.org to aa@aa.ng:',
            u'Message:', u'msg: MIME-Version: 1.0',
            u'msg: Content-Type: text/plain; charset="us-ascii"',
            u'msg: Content-Transfer-Encoding: 7bit',
            u'msg: From: Administrator <no-reply@waeup.org>',
            u'msg: To: Anna Tester <aa@aa.ng>',
            u'msg: Reply-To: Administrator <contact@waeup.org>',
            u'msg: Subject: Ikoba status change information',
            u'msg: ',
            u'msg: Dear Anna Tester,',
            u'msg: ',
            u'msg: The status of the following object has been changed:',
            u'msg: ', u'msg: Object Id: K1000000',
            u'msg: Title: Anna Tester',
            u'msg: Transition: customer registration rejected',
            u'msg: New state: started',
            u'msg: ',
            u'msg: Regards,',
            u'msg: ',
            u'msg: Manager',
            u'msg: ',
            u'msg: --',
            u'msg: Sample Company',
            u'msg: ',
            u'']
        self.assertEqual(expected, self.get_fake_smtp_output().split('\n'))
        self.browser.open(self.trigtrans_path)
        self.browser.getControl(name="transition").value = ['request']
        self.browser.getControl("Apply").click()
        self.browser.open(self.trigtrans_path)
        self.browser.getControl(name="transition").value = [
            'approve_provisionally']
        self.browser.getControl("Apply").click()
        self.browser.open(self.trigtrans_path)
        self.browser.getControl(name="transition").value = ['approve_finally']
        self.browser.getControl("Apply").click()
        self.browser.open(self.trigtrans_path)
        self.browser.getControl(name="transition").value = ['reset1']
        self.browser.getControl("Apply").click()
        self.teardown_logging()
        return

    def test_manage_import(self):
        # Managers can import customer data files
        datacenter_path = 'http://localhost/app/datacenter'
        # Prepare a csv file for customers
        open('customers.csv', 'wb').write(
"""firstname,lastname,reg_number,date_of_birth,email,phone,sex,password
Aaren,Pieri,1,1990-01-02,bb@aa.ng,1234,m,mypwd1
Claus,Finau,2,1990-01-03,cc@aa.ng,1234,m,mypwd1
Brit,Berson,2,1990-01-04,dd@aa.ng,1234,m,mypwd1
""")
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(datacenter_path)
        self.browser.getLink('Upload data').click()
        filecontents = StringIO(open('customers.csv', 'rb').read())
        filewidget = self.browser.getControl(name='uploadfile:file')
        filewidget.add_file(filecontents, 'text/plain', 'customers.csv')
        self.browser.getControl(name='SUBMIT').click()
        self.browser.getLink('Process data').click()
        button = lookup_submit_value(
            'select', 'customers_zope.mgr.csv', self.browser)
        button.click()
        importerselect = self.browser.getControl(name='importer')
        modeselect = self.browser.getControl(name='mode')
        importerselect.getControl('Customer Processor').selected = True
        modeselect.getControl(value='create').selected = True
        self.browser.getControl('Proceed to step 3').click()
        self.assertTrue('Header fields OK' in self.browser.contents)
        self.browser.getControl('Perform import').click()
        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
        self.assertTrue(
            'Successfully processed 2 rows' in self.browser.contents)
        self.assertTrue('Batch processing finished' in self.browser.contents)

        # The customers are properly indexed and we can
        # thus find a customer in  the department
        self.browser.open(self.manage_container_path)
        # We can search for a new customer by name ...
        self.browser.getControl(name="searchtype").value = ['fullname']
        self.browser.getControl(name="searchterm").value = 'Claus'
        self.browser.getControl("Find customer(s)").click()
        self.assertTrue('Claus Finau' in self.browser.contents)
        # ... and check if the imported password has been properly set
        ctrl = self.browser.getControl(name='entries')
        value = ctrl.options[0]
        claus = self.app['customers'][value]
        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
        return

    def test_activate_deactivate_buttons(self):
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path)
        self.browser.getLink("Deactivate").click()
        self.assertTrue(
            'Customer account has been deactivated.' in self.browser.contents)
        self.assertTrue(
            'Base Data (account deactivated)' in self.browser.contents)
        self.assertTrue(self.customer.suspended)
        self.browser.getLink("Activate").click()
        self.assertTrue(
            'Customer account has been activated.' in self.browser.contents)
        self.assertFalse(
            'Base Data (account deactivated)' in self.browser.contents)
        self.assertFalse(self.customer.suspended)
        # History messages have been added ...
        self.browser.getLink("History").click()
        self.assertTrue(
            'Customer account deactivated by Manager<br />'
            in self.browser.contents)
        self.assertTrue(
            'Customer account activated by Manager<br />'
            in self.browser.contents)
        # ... and actions have been logged.
        logfile = os.path.join(
            self.app['datacenter'].storage, 'logs', 'customers.log')
        logcontent = open(logfile).read()
        self.assertTrue(
            'zope.mgr - customers.browser.CustomerDeactivatePage - '
            'K1000000 - account deactivated' in logcontent)
        self.assertTrue(
            'zope.mgr - customers.browser.CustomerActivatePage - '
            'K1000000 - account activated' in logcontent)

    def test_login_as_customer(self):
        # CustomerImpersonators can login as customer
        self.app['users'].addUser('mrofficer', 'mrofficersecret')
        self.app['users']['mrofficer'].email = 'mrofficer@foo.ng'
        self.app['users']['mrofficer'].title = 'Harry Actor'
        prmglobal = IPrincipalRoleManager(self.app)
        prmglobal.assignRoleToPrincipal(
            'waeup.CustomerImpersonator', 'mrofficer')
        prmglobal.assignRoleToPrincipal('waeup.CustomersManager', 'mrofficer')
        self.assertEqual(self.customer.state, 'created')
        # Login as customer impersonator
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = 'mrofficer'
        self.browser.getControl(name="form.password").value = 'mrofficersecret'
        self.browser.getControl("Login").click()
        self.assertMatches('...You logged in...', self.browser.contents)
        self.browser.open(self.customer_path)
        self.browser.getLink("Login as").click()
        self.browser.getControl("Set password now").click()
        temp_password = self.browser.getControl(name='form.password').value
        self.browser.getControl("Login now").click()
        self.assertMatches(
            '...You successfully logged in as...', self.browser.contents)
        # Status has changed
        self.assertEqual(self.customer.state, 'started')
        # We are logged in as customer and can see the 'My Data' tab
        self.assertMatches(
            '...<a href="#" class="dropdown-toggle"'
            ' data-toggle="dropdown">...',
            self.browser.contents)
        self.assertMatches(
            '...My Data...',
            self.browser.contents)
        self.browser.getLink("Logout").click()
        # The customer can't login with the original password ...
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...Your account has been temporarily deactivated...',
            self.browser.contents)
        # ... but with the temporary password
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = temp_password
        self.browser.getControl("Login").click()
        self.assertMatches('...You logged in...', self.browser.contents)
        # Creation of temp_password is properly logged
        logfile = os.path.join(
            self.app['datacenter'].storage, 'logs', 'customers.log')
        logcontent = open(logfile).read()
        self.assertTrue(
            'mrofficer - customers.browser.LoginAsCustomerStep1 - K1000000 - '
            'temp_password generated: %s' % temp_password in logcontent)


class CustomerUITests(CustomersFullSetup):
    # Tests for Customer class views and pages

    def test_customer_login_with_email(self):
        self.assertEqual(self.customer.state, 'created')
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer.email
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertEqual(self.browser.url, self.customer_path)
        self.assertTrue('You logged in' in self.browser.contents)
        # Status has changed
        self.assertEqual(self.customer.state, 'started')
        return

    def test_customer_change_password(self):
        # Customers can change the password
        self.assertEqual(self.customer.state, 'created')
        self.customer.personal_updated = datetime.utcnow()
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertEqual(self.browser.url, self.customer_path)
        self.assertTrue('You logged in' in self.browser.contents)
        # Status has changed
        self.assertEqual(self.customer.state, 'started')
        # Change password
        self.browser.getLink("Change password").click()
        self.browser.getControl(name="change_password").value = 'pw'
        self.browser.getControl(
            name="change_password_repeat").value = 'pw'
        self.browser.getControl("Save").click()
        self.assertTrue('Password must have at least' in self.browser.contents)
        self.browser.getControl(name="change_password").value = 'new_password'
        self.browser.getControl(
            name="change_password_repeat").value = 'new_passssword'
        self.browser.getControl("Save").click()
        self.assertTrue('Passwords do not match' in self.browser.contents)
        self.browser.getControl(name="change_password").value = 'new_password'
        self.browser.getControl(
            name="change_password_repeat").value = 'new_password'
        self.browser.getControl("Save").click()
        self.assertTrue('Password changed' in self.browser.contents)
        # We are still logged in. Changing the password hasn't thrown us out.
        self.browser.getLink("Base Data").click()
        self.assertEqual(self.browser.url, self.customer_path)
        # We can logout
        self.browser.getLink("Logout").click()
        self.assertTrue('You have been logged out' in self.browser.contents)
        self.assertEqual(self.browser.url, 'http://localhost/app/index')
        # We can login again with the new password
        self.browser.getLink("Login").click()
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'new_password'
        self.browser.getControl("Login").click()
        self.assertEqual(self.browser.url, self.customer_path)
        self.assertTrue('You logged in' in self.browser.contents)
        return

    def test_customer_edit_upload_upload_and_request(self):
        # Customer cant login if their password is not set
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...You logged in...', self.browser.contents)
        self.browser.getLink("Edit").click()
        self.browser.getControl(name="form.email").value = 'new_email@aa.ng'
        self.browser.getControl("Save", index=0).click()
        self.assertMatches('...Form has been saved...',
                           self.browser.contents)
        self.browser.getControl("Save and request registration").click()
        self.assertMatches('...Passport picture is missing...',
                           self.browser.contents)
        self.assertEqual(self.customer.state, 'started')
        # Customer must upload a passport picture. We are already on
        # the upload page.
        ctrl = self.browser.getControl(name='passporteditupload')
        file_obj = open(SAMPLE_IMAGE, 'rb')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
        self.browser.getControl(
            name='upload_passporteditupload').click()
        self.assertTrue(
            'src="http://localhost/app/customers/K1000000/passport.jpg"'
            in self.browser.contents)
        self.browser.getControl(name="CANCEL").click()
        self.assertEqual(self.browser.url, self.customer_path)
        self.browser.getLink("Edit").click()
        self.browser.getControl("Save and request registration").click()
        self.assertMatches('...Registration form has been submitted...',
                           self.browser.contents)
        self.assertEqual(self.customer.state, 'requested')
        # Customer can view history
        self.browser.getLink("History").click()
        self.assertMatches('...Customer created by system...',
            self.browser.contents)

    def test_customer_login(self):
        # Customer cant login if their password is not set
        self.customer.password = None
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertTrue(
            'You entered invalid credentials.' in self.browser.contents)
        # We set the password again
        IUserAccount(
            self.app['customers'][self.customer_id]).setPassword('cpwd')
        # Customers can't login if their account is suspended/deactivated
        self.customer.suspended = True
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...<div class="alert alert-warning">'
            'Your account has been deactivated.</div>...',
            self.browser.contents)
        # If suspended_comment is set this message will be flashed instead
        self.customer.suspended_comment = u'Aetsch baetsch!'
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...<div class="alert alert-warning">Aetsch baetsch!</div>...',
            self.browser.contents)
        self.customer.suspended = False
        # Customers can't login if a temporary password has been set and
        # is not expired
        self.app['customers'][self.customer_id].setTempPassword(
            'anybody', 'temp_cpwd')
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...Your account has been temporarily deactivated...',
            self.browser.contents)
        # The customer can login with the temporary password
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'temp_cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...You logged in...', self.browser.contents)
        # Customer can view the base data
        self.browser.open(self.customer_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.customer_path)
        # When the password expires ...
        delta = timedelta(minutes=11)
        self.app['customers'][self.customer_id].temp_password[
            'timestamp'] = datetime.utcnow() - delta
        self.app['customers'][self.customer_id]._p_changed = True
        # ... the customer will be automatically logged out
        self.assertRaises(
            Unauthorized, self.browser.open, self.customer_path)
        # Then the customer can login with the original password
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...You logged in...', self.browser.contents)

    def test_change_password_request(self):
        self.browser.open('http://localhost/app/changepw')
        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
        self.browser.getControl("Send login credentials").click()
        self.assertTrue('An email with' in self.browser.contents)


class CustomerRegistrationTests(CustomersFullSetup):
    # Tests for customer registration

    layer = FunctionalLayer

    def test_request_pw(self):
        # Customer with wrong number can't be found.
        self.browser.open('http://localhost/app/requestpw')
        self.browser.getControl(name="form.firstname").value = 'Anna'
        self.browser.getControl(name="form.number").value = 'anynumber'
        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
        self.browser.getControl("Send login credentials").click()
        self.assertTrue('No customer found.'
            in self.browser.contents)
        # Anonymous is not informed that firstname verification failed.
        # It seems that the record doesn't exist.
        self.browser.open('http://localhost/app/requestpw')
        self.browser.getControl(name="form.firstname").value = 'Johnny'
        self.browser.getControl(name="form.number").value = '123'
        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
        self.browser.getControl("Send login credentials").click()
        self.assertTrue('No customer found.'
            in self.browser.contents)
        # Even with the correct firstname we can't register if a
        # password has been set and used.
        self.browser.getControl(name="form.firstname").value = 'Anna'
        self.browser.getControl(name="form.number").value = '123'
        self.browser.getControl("Send login credentials").click()
        self.assertTrue('Your password has already been set and used.'
            in self.browser.contents)
        self.browser.open('http://localhost/app/requestpw')
        self.app['customers'][self.customer_id].password = None
        # The firstname field, used for verification, is not case-sensitive.
        self.browser.getControl(name="form.firstname").value = 'aNNa'
        self.browser.getControl(name="form.number").value = '123'
        self.browser.getControl(name="form.email").value = 'new@yy.zz'
        self.browser.getControl("Send login credentials").click()
        # Yeah, we succeded ...
        self.assertTrue('Your request was successful.'
            in self.browser.contents)
        # ... and  customer can be found in the catalog via the email address
        cat = queryUtility(ICatalog, name='customers_catalog')
        results = list(
            cat.searchResults(
            email=('new@yy.zz', 'new@yy.zz')))
        self.assertEqual(self.customer, results[0])
        logfile = os.path.join(
            self.app['datacenter'].storage, 'logs', 'main.log')
        logcontent = open(logfile).read()
        self.assertTrue(
            'zope.anybody - customers.browser.CustomerRequestPasswordPage - '
            '123 (K1000000) - new@yy.zz' in logcontent)
        return

    def test_create_account(self):
        self.browser.open('http://localhost/app/createaccount')
        self.browser.getControl(name="form.firstname").value = 'Ruben'
        self.browser.getControl(name="form.lastname").value = 'Gonzales'
        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
        self.browser.getControl("Send login credentials").click()
        # Email address exists.
        self.assertTrue('Email address in use.' in self.browser.contents)
        self.browser.getControl(name="form.email").value = 'newcustomer@xx.zz'
        self.browser.getControl("Send login credentials").click()
        self.assertTrue(
            'Your request was successful.' in self.browser.contents)
        # Customer can be found in the catalog via the email address
        cat = queryUtility(ICatalog, name='customers_catalog')
        results = list(
            cat.searchResults(
            email=('newcustomer@xx.zz', 'newcustomer@xx.zz')))
        self.assertEqual(self.app['customers']['K1000001'], results[0])
        self.assertEqual(self.app['customers']['K1000001'].firstname, 'Ruben')
        self.assertEqual(
            self.app['customers']['K1000001'].lastname, 'Gonzales')
        logfile = os.path.join(
            self.app['datacenter'].storage, 'logs', 'main.log')
        logcontent = open(logfile).read()
        self.assertTrue(
            'zope.anybody - customers.browser.CustomerCreateAccountPage - '
            'K1000001 - newcustomer@xx.zz' in logcontent)
        return


class CustomerDataExportTests(CustomersFullSetup, FunctionalAsyncTestCase):
    # Tests for CustomersContainer class views and pages

    layer = FunctionalLayer

    def wait_for_export_job_completed(self):
        # helper function waiting until the current export job is completed
        manager = getUtility(IJobManager)
        job_id = self.app['datacenter'].running_exports[0][0]
        job = manager.get(job_id)
        wait_for_result(job)
        return job_id

    def test_datacenter_export(self):
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open('http://localhost/app/datacenter/@@export')
        self.browser.getControl(name="exporter").value = ['customers']
        self.browser.getControl("Create CSV file").click()

        # When the job is finished and we reload the page...
        job_id = self.wait_for_export_job_completed()
        # ... the csv file can be downloaded ...
        self.browser.open('http://localhost/app/datacenter/@@export')
        self.browser.getLink("Download").click()
        self.assertEqual(self.browser.headers['content-type'],
            'text/csv; charset=UTF-8')
        self.assertTrue(
            'filename="WAeUP.Ikoba_customers_%s.csv' % job_id in
            self.browser.headers['content-disposition'])
        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
        job_id = self.app['datacenter'].running_exports[0][0]
        # ... and discarded
        self.browser.open('http://localhost/app/datacenter/@@export')
        self.browser.getControl("Discard").click()
        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
        # Creation, downloading and discarding is logged
        logfile = os.path.join(
            self.app['datacenter'].storage, 'logs', 'datacenter.log')
        logcontent = open(logfile).read()
        self.assertTrue(
            'zope.mgr - browser.pages.ExportCSVPage - exported: '
            'customers, job_id=%s'
            % job_id in logcontent
            )
        self.assertTrue(
            'zope.mgr - browser.pages.ExportCSVView - downloaded: '
            'WAeUP.Ikoba_customers_%s.csv, job_id=%s'
            % (job_id, job_id) in logcontent
            )
        self.assertTrue(
            'zope.mgr - browser.pages.ExportCSVPage - discarded: '
            'job_id=%s' % job_id in logcontent
            )


class DocumentUITests(CustomersFullSetup):
    # Tests for customer document related views and pages

    def test_manage_document(self):
        # Managers can access the pages of customer documentsconter
        # and can perform actions
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.customer_path)
        self.browser.open(self.customer_path)
        self.browser.getLink("Documents", index=1).click()
        self.browser.getControl("Add document").click()
        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
        self.browser.getControl(name="form.title").value = 'My PDF Document'
        self.browser.getControl("Add document").click()
        self.assertTrue('PDF Document added.' in self.browser.contents)
        docid = [i for i in self.customer['documents'].keys()
                 if len(i) > 10][0]
        document = self.customer['documents'][docid]

        # Document can be edited
        self.browser.getLink(docid[:9]).click()
        self.browser.getLink("Manage").click()
        self.browser.getControl(name="form.title").value = 'My second doc'
        self.browser.getControl("Save").click()
        self.assertTrue('Form has been saved.' in self.browser.contents)
        self.browser.getLink("View").click()
        self.assertEqual(self.browser.url,
            self.documents_path + '/' + docid + '/index')

        # Transitions can be performed
        self.browser.getLink("Transition").click()
        self.browser.getControl(name="transition").value = ['submit']
        self.browser.getControl("Apply").click()
        self.browser.getLink("Transition").click()
        # Document can only be verified if customer is approved
        self.browser.getControl(name="transition").value = ['verify']
        self.browser.getControl("Apply").click()
        self.assertTrue(
            'Customer has not yet been approved' in self.browser.contents)
        IWorkflowState(self.customer).setState(APPROVED)
        # Document can only be verified if files have been uploaded before
        self.browser.getLink("Transition").click()
        self.browser.getControl(name="transition").value = ['verify']
        self.browser.getControl("Apply").click()
        self.assertTrue('No file uploaded' in self.browser.contents)
        self.assertEqual(document.state, 'submitted')
        # We set state here manually (verification is tested in
        # test_verify_document)
        IWorkflowState(document).setState(VERIFIED)

        # Manage button and form is no longer available
        self.browser.open(self.documents_path + '/' + docid + '/index')
        self.assertFalse(
            'href="http://localhost/app/customers/K1000000/'
            'documents/%s/manage"'
            % docid in self.browser.contents)
        self.browser.open(self.documents_path + '/' + docid + '/manage')
        self.assertTrue(
            'The requested form is locked (read-only)'
            in self.browser.contents)

        # Documents can be removed
        self.browser.getLink("Documents", index=1).click()
        ctrl = self.browser.getControl(name='val_id')
        ctrl.getControl(value=document.document_id).selected = True
        self.browser.getControl("Remove selected", index=0).click()
        self.assertTrue('Successfully removed' in self.browser.contents)

        # All actions are being logged
        logfile = os.path.join(
            self.app['datacenter'].storage, 'logs', 'customers.log')
        logcontent = open(logfile).read()

        self.assertTrue(
            'INFO - system - K1000000 - DOC1 - Document created'
            in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - customers.browser.DocumentAddFormPage '
            '- K1000000 - added: PDF Document %s'
            % document.document_id in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - customers.browser.DocumentManageFormPage '
            '- K1000000 - %s - saved: title' % docid
            in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - K1000000 - %s - Submitted for verification'
            % docid in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - customers.browser.DocumentsManageFormPage '
            '- K1000000 - removed: %s' % docid
            in logcontent)

    def test_edit_sample_document(self):
        # Customers can manage documents under certain conditions
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...You logged in...', self.browser.contents)
        self.browser.getLink("Documents").click()
        self.browser.getControl("Add document").click()
        self.assertTrue(
            'The requested form is locked' in self.browser.contents)
        # Customer is in wrong state
        IWorkflowState(self.customer).setState(APPROVED)
        self.browser.getControl("Add document").click()
        self.browser.getControl(name="doctype").value = [
            'CustomerSampleDocument']
        self.browser.getControl(name="form.title").value = 'My Sample Document'
        self.browser.getControl("Add document").click()
        self.assertTrue('Sample Document added.' in self.browser.contents)
        docid = [i for i in self.customer['documents'].keys()
                 if len(i) > 10][0]
        document = self.customer['documents'][docid]
        self.browser.getControl(name="form.title").value = 'My second doc'
        self.browser.getControl("Save").click()
        self.assertEqual(document.title, 'My second doc')
        self.assertTrue('Form has been saved.' in self.browser.contents)
        self.browser.getLink("View").click()
        self.assertEqual(
            self.browser.url, self.documents_path + '/%s/index' % docid)
        # Customer can upload a document.
        self.browser.getLink("Edit").click()
        ctrl = self.browser.getControl(name='samplescaneditupload')
        file_obj = open(SAMPLE_IMAGE, 'rb')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(file_obj, filename='my_document.jpg')
        self.browser.getControl(
            name='upload_samplescaneditupload').click()
        self.assertTrue(
            'href="http://localhost/app/customers/K1000000/'
            'documents/%s/sample"'
            % docid in self.browser.contents)
        # Customer can submit the form. The form is also saved.
        self.browser.getControl(name="form.title").value = 'My third doc'
        self.browser.getControl("Final Submit").click()
        self.assertEqual(document.title, 'My third doc')
        self.assertEqual(document.state, 'submitted')
        self.assertTrue(
            'Document State: submitted for verification'
            in self.browser.contents)
        # Customer can't edit the document once it has been submitted
        self.browser.open(self.documents_path + '/%s/edit' % docid)
        self.assertTrue(
            'The requested form is locked' in self.browser.contents)

    def test_manage_upload_sample_file(self):
        # Managers can upload a file via the DocumentManageFormPage
        # The image is stored even if form has errors
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path + '/documents/DOC1/manage')
        # Create a pseudo image file and select it to be uploaded
        image = open(SAMPLE_IMAGE, 'rb')
        ctrl = self.browser.getControl(name='samplescanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
        # The Save action does not upload files
        self.browser.getControl("Save").click()  # submit form
        self.assertFalse(
            'href="http://localhost/app/customers/K1000000/'
            'documents/DOC1/sample"'
            in self.browser.contents)
        # ... but the correct upload submit button does
        image = open(SAMPLE_IMAGE)
        ctrl = self.browser.getControl(name='samplescanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
        self.browser.getControl(
            name='upload_samplescanmanageupload').click()
        self.assertTrue(
            'href="http://localhost/app/customers/K1000000/'
            'documents/DOC1/sample"'
            in self.browser.contents)
        # Browsing the link shows a real image
        self.browser.open('sample')
        self.assertEqual(
            self.browser.headers['content-type'], 'image/jpeg')
        self.assertEqual(len(self.browser.contents), 2787)
        # We can't reupload a file. The existing file must be deleted first.
        self.browser.open(self.customer_path + '/documents/DOC1/manage')
        self.assertFalse(
            'upload_samplescanmanageupload' in self.browser.contents)
        # File must be deleted first
        self.browser.getControl(name='delete_samplescanmanageupload').click()
        self.assertTrue(
            'sample deleted' in self.browser.contents)
        # Uploading a file which is bigger than 150k will raise an error
        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
        ctrl = self.browser.getControl(name='samplescanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(big_image, filename='my_sample_scan.jpg')
        self.browser.getControl(
            name='upload_samplescanmanageupload').click()
        self.assertTrue(
            'Uploaded file is too big' in self.browser.contents)
        # We do not rely on filename extensions given by uploaders
        image = open(SAMPLE_IMAGE, 'rb')  # a jpg-file
        ctrl = self.browser.getControl(name='samplescanmanageupload')
        file_ctrl = ctrl.mech_control
        # Tell uploaded file is bmp
        file_ctrl.add_file(image, filename='my_sample_scan.bmp')
        self.browser.getControl(
            name='upload_samplescanmanageupload').click()
        self.assertTrue(
            # jpg file was recognized
            'File sample.jpg uploaded.' in self.browser.contents)
        # Delete file again
        self.browser.getControl(name='delete_samplescanmanageupload').click()
        self.assertTrue(
            'sample deleted' in self.browser.contents)
        # File names must meet several conditions
        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
        ctrl = self.browser.getControl(name='samplescanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(bmp_image, filename='my_sample_scan.bmp')
        self.browser.getControl(
            name='upload_samplescanmanageupload').click()
        self.assertTrue('Only the following extensions are allowed'
            in self.browser.contents)

    def test_verify_document(self):
        IWorkflowState(self.customer).setState('approved')
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path + '/documents/DOC1/manage')
        image = open(SAMPLE_IMAGE, 'rb')
        ctrl = self.browser.getControl(name='samplescanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
        self.browser.getControl(
            name='upload_samplescanmanageupload').click()
        IWorkflowState(self.document).setState(SUBMITTED)
        # Only after verifying the document, sample_md5 is set
        self.assertEqual(
            getattr(self.document, 'sample_md5', None), None)
        self.browser.open(self.documents_path + '/DOC1/trigtrans')
        self.browser.getControl(name="transition").value = ['verify']
        self.browser.getControl("Apply").click()
        self.assertEqual(
            getattr(self.document, 'sample_md5', None),
                    '1d1ab893e87c240afb2104d61ddfe180')

    def test_manage_upload_pdf_file(self):
        # Managers can upload a file via the DocumentManageFormPage
        # The image is stored even if form has errors
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path + '/documents')
        self.browser.getControl("Add document").click()
        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
        self.browser.getControl(name="form.title").value = 'My PDF Document'
        self.browser.getControl("Add document").click()
        docid = [
            i for i in self.customer['documents'].keys() if len(i) > 10][0]
        self.browser.open(self.documents_path + '/%s/manage' % docid)
        # Create a pseudo image file and select it to be uploaded
        image = open(SAMPLE_IMAGE, 'rb')
        ctrl = self.browser.getControl(name='pdfscanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
        self.browser.getControl(
            name='upload_pdfscanmanageupload').click()
        self.assertTrue(
            'pdf file format expected' in self.browser.contents)
        ctrl = self.browser.getControl(name='pdfscanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(image, filename='my_sample_scan.pdf')
        self.browser.getControl(
            name='upload_pdfscanmanageupload').click()
        self.assertTrue(
            'Could not determine file type' in self.browser.contents)
        pdf = open(SAMPLE_PDF, 'rb')
        ctrl = self.browser.getControl(name='pdfscanmanageupload')
        file_ctrl = ctrl.mech_control
        file_ctrl.add_file(pdf, filename='my_sample_scan.pdf')
        self.browser.getControl(
            name='upload_pdfscanmanageupload').click()
        self.assertTrue(
            'href="http://localhost/app/customers/K1000000/'
            'documents/%s/sample.pdf">%s.pdf</a>'
            % (docid, docid[:9]) in self.browser.contents)
        # Browsing the link shows a real pdf
        self.browser.open('sample.pdf')
        self.assertEqual(
            self.browser.headers['content-type'], 'application/pdf')
        # The name of the downloaded file will be different
        self.assertEqual(
            self.browser.headers['Content-Disposition'],
            'attachment; filename="%s.pdf' % docid[:9])

    def test_view_slips(self):
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        # Officers can open the document overview
        self.browser.open(self.customer_path + '/documents')
        self.browser.getLink("Download documents overview").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(
            self.browser.headers['Content-Type'], 'application/pdf')
        path = os.path.join(samples_dir(), 'documents_overview_slip.pdf')
        open(path, 'wb').write(self.browser.contents)
        print "Sample PDF overview_slip.pdf written to %s" % path
        # Officers can open document slips which shows a thumbnail of
        # the jpeg file attached.
        file_id = IFileStoreNameChooser(self.document).chooseName(
            attr='sample.jpg')
        fs = ExtFileStore(root=self.dc_root)
        jpegfile = open(SAMPLE_IMAGE, 'rb')
        fs.createFile(file_id, jpegfile)
        self.browser.open(self.customer_path + '/documents/DOC1')
        self.browser.getLink("Download document slip").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(
            self.browser.headers['Content-Type'], 'application/pdf')
        path = os.path.join(samples_dir(), 'document_slip.pdf')
        open(path, 'wb').write(self.browser.contents)
        print "Sample document_slip.pdf written to %s" % path
        # Officers can open merged pdf document slips
        pdfdocument = createObject('waeup.CustomerPDFDocument')
        pdfdocument.title = u'My first document'
        self.customer['documents'].addDocument(pdfdocument)
        # Add pdf file
        file_id = IFileStoreNameChooser(pdfdocument).chooseName(
            attr='sample.pdf')
        fs = ExtFileStore(root=self.dc_root)
        pdffile = open(SAMPLE_PDF, 'rb')
        fs.createFile(file_id, pdffile)
        docid = [i for i in self.customer['documents'].keys()
                 if len(i) > 10][0]
        self.browser.open(self.customer_path + '/documents/' + docid)
        self.browser.getLink("Download document slip").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(
            self.browser.headers['Content-Type'], 'application/pdf')
        path = os.path.join(samples_dir(), 'pdfdocument_slip.pdf')
        open(path, 'wb').write(self.browser.contents)
        print "Sample pdfdocument_slip.pdf written to %s" % path

    def test_get_setmd5_file(self):
        # A proper file name chooser is registered for customer documents.
        # This is not a UI test. It's just a functional test.
        file_id = IFileStoreNameChooser(self.document).chooseName(
            attr='sample')
        fs = ExtFileStore(root=self.dc_root)
        fs.createFile(file_id, StringIO('my sample 1'))
        result = fs.getFileByContext(self.document, attr='sample')
        self.assertEqual(
            file_id, '__file-customerdocument__01000/'
            'K1000000/sample_DOC1_K1000000')
        self.assertEqual(result.read(), 'my sample 1')
        self.assertEqual(
            self.document.connected_files[0][1].read(), 'my sample 1')
        self.document.setMD5()
        self.assertEqual(
            self.document.sample_md5, 'a406995ee8eb6772bacf51aa4b0caa24')
        return


class ContractUITests(CustomersFullSetup):
    # Tests for contract related views and pages

    never_ending_button_text = (
        'Select payment method (final submission)')

    def setup_payment(self):
        payer = IPayer(self.customer)
        payable = IPayable(self.contract)
        self.payment = Payment(payer, payable)
        self.payment.gateway_service = 'demo_creditcard'
        self.payment.state = STATE_PAID
        self.app['payments'][self.payment.payment_id] = self.payment

    def add_product_option(self, contract):
        prodoption = ProductOption()
        prodoption.title = u'Any product option'
        prodoption.fee = Decimal('88.8')
        prodoption.currency = 'EUR'
        contract.product_options = [prodoption, ]

    def test_multiple_currencies(self):
        prodoption1 = ProductOption()
        prodoption1.title = u'Any product option in Euros'
        prodoption1.fee = Decimal('88.8')
        prodoption1.currency = 'EUR'
        prodoption2 = ProductOption()
        prodoption2.title = u'Any product option in Dollars'
        prodoption2.fee = Decimal('99.9')
        prodoption2.currency = 'USD'
        self.assertRaises(CurrencyMismatch,
                          setattr, self.contract,
                          'product_options',
                          [prodoption1, prodoption2]
                          )

    def prepare_payment_select(self):
        IWorkflowState(self.customer).setState('approved')
        IWorkflowState(self.document).setState('verified')
        self.contract.document_object = self.document
        self.add_product_option(self.contract)
        IWorkflowState(self.contract).setState('created')
        # login as customer
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()

    def test_manage_contract(self):
        # Managers can access the pages of customer contractsconter
        # and can perform actions
        IWorkflowState(self.customer).setState(APPROVED)
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.customer_path)
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(self.browser.url, self.customer_path)
        self.browser.open(self.customer_path)
        self.browser.getLink("Contracts").click()
        self.browser.getControl("Add contract").click()
        self.browser.getControl(name="contype").value = ['SampleContract']
        self.browser.getControl("Add contract").click()
        self.assertTrue('Sample Contract added.' in self.browser.contents)
        conid = [i for i in self.customer['contracts'].keys()
                 if len(i) > 10][0]
        contract = self.customer['contracts'][conid]
        self.assertEqual(
            self.browser.url,
            self.contracts_path + '/%s/selectproduct' % conid)
        # SAM is in the correct contract_category
        self.assertTrue('<option value="SAM">' in self.browser.contents)
        # So far last_product_id is None.
        self.assertTrue(
            self.customer['contracts'][conid].last_product_id is None)
        self.browser.getControl(name="form.product_object").value = ['SAM']
        self.browser.getControl("Save and proceed").click()
        self.assertEqual(
            self.browser.url, self.contracts_path + '/%s/manage' % conid)
        self.browser.getLink("View").click()
        self.assertEqual(
            self.browser.url, self.contracts_path + '/%s/index' % conid)
        self.assertEqual(contract.tc_dict, {'en': u'Hello world'})

        # Transitions can be performed
        self.browser.getLink("Transition").click()
        self.browser.getControl(name="transition").value = ['submit']
        self.browser.getControl("Apply").click()
        self.browser.getLink("Transition").click()
        self.browser.getControl(name="transition").value = ['approve']
        self.browser.getControl("Apply").click()
        self.assertEqual(contract.state, 'approved')

        # Even in state approved the official use data can be edited
        self.browser.open(self.contracts_path + '/%s/index' % conid)
        self.browser.getLink("Manage official data").click()
        self.browser.getControl(name="form.comment").value = u'Nice place'
        self.browser.getControl("Save").click()
        self.assertEqual(contract.comment, 'Nice place')
        self.assertTrue('Form has been saved.' in self.browser.contents)

        # Contracts can be removed
        self.browser.getLink("Contracts").click()
        ctrl = self.browser.getControl(name='val_id')
        ctrl.getControl(value=contract.contract_id).selected = True
        self.browser.getControl("Remove selected", index=0).click()
        self.assertTrue('Successfully removed' in self.browser.contents)

        # All actions are being logged
        logfile = os.path.join(
            self.app['datacenter'].storage, 'logs', 'customers.log')
        logcontent = open(logfile).read()
        self.assertTrue(
            'INFO - zope.mgr - K1000000 - %s - Contract created' % conid
            in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - customers.browser.ContractAddFormPage '
            '- K1000000 - added: Sample Contract %s'
            % contract.contract_id in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - K1000000 - %s - Contract submitted' % conid
            in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - K1000000 - %s - Contract approved' % conid
            in logcontent)
        self.assertTrue(
            'INFO - zope.mgr - customers.browser.ContractsFormPage '
            '- K1000000 - removed: %s' % conid
            in logcontent)

    def test_edit_sample_contract(self):
        # We add a second product.
        product = createObject('waeup.Product')
        product.product_id = u'LIC'
        product.title = u'Our License Product'
        product.contract_category = u'license'
        product.valid_from = date(2014, 12, 4)
        self.app['products'].addProduct(product)
        # Customers can manage contracts under certain conditions
        self.browser.open(self.login_path)
        self.browser.getControl(name="form.login").value = self.customer_id
        self.browser.getControl(name="form.password").value = 'cpwd'
        self.browser.getControl("Login").click()
        self.assertMatches(
            '...You logged in...', self.browser.contents)
        self.browser.getLink("Contracts").click()
        # Customer is in wrong state
        self.assertFalse('Add contract' in self.browser.contents)
        self.browser.open(self.contracts_path + '/addcontract')
        self.assertTrue(
            'The requested form is locked' in self.browser.contents)
        IWorkflowState(self.customer).setState(APPROVED)
        self.browser.open(self.contracts_path)
        # Now customer can add a contract
        self.browser.getControl("Add contract").click()
        self.browser.getControl(name="contype").value = ['SampleContract']
        self.browser.getControl("Add contract").click()
        self.assertTrue('Sample Contract added.' in self.browser.contents)
        conid = [i for i in self.customer['contracts'].keys()
                 if len(i) > 10][0]
        contract = self.customer['contracts'][conid]
        self.assertEqual(
            self.browser.url,
            self.contracts_path + '/%s/selectproduct' % conid)
        # SAM is in the correct contract_category ...
        self.assertTrue('<option value="SAM">' in self.browser.contents)
        # ... but LIC not.
        self.assertFalse('<option value="LIC">' in self.browser.contents)
        # So far last_product_id is None.
        self.assertTrue(
            self.customer['contracts'][conid].last_product_id is None)
        self.browser.getControl(name="form.product_object").value = ['SAM']
        self.browser.getControl("Save and proceed").click()
        self.assertEqual(
            self.browser.url, self.contracts_path + '/%s/edit' % conid)
        # Document is a required field on edit form page.
        self.browser.getControl("Save").click()
        self.assertTrue(
            'Document: <span class="error">Required input is missing.</span>'
            in self.browser.contents)
        # But our document can't be selected because it's not submitted
        self.assertFalse('My first document' in self.browser.contents)
        IWorkflowState(self.document).setState(SUBMITTED)
        self.browser.open(self.contracts_path + '/%s/edit' % conid)
        self.browser.getControl(name="form.document_object").value = ['DOC1']
        self.browser.getControl("Save").click()
        # After saving the form, last_product_id and other attributes are set
        self.assertTrue('Form has been saved.' in self.browser.contents)
        self.assertEqual(
            self.customer['contracts'][conid].last_product_id, 'SAM')
        self.assertEqual(contract.title, 'Our Sample Product')
        self.assertEqual(contract.product_object, self.product)
        self.assertEqual(contract.document_object, self.document)
        # Saving the form again does not unset last_product_id
        self.browser.getControl("Save").click()
        self.assertEqual(
            self.customer['contracts'][conid].last_product_id, 'SAM')
        self.assertTrue('Form has been saved.' in self.browser.contents)
        # So far we have not yet set product options.
        # Unfortunately, we can't set them in test browser
        prodoption = ProductOption()
        prodoption.title = u'Any product option'
        prodoption.fee = Decimal('88.8')
        prodoption.currency = 'EUR'
        contract.product_options = [prodoption, ]
        self.browser.open(self.contracts_path + '/%s/edit' % conid)
        # We can see both the stored and the recent product options
        # from the chosen product.
        self.assertTrue(
            '<option selected="selected" value="Any product option_88.8_EUR">'
            'Any product option @ 88.8 Euro</option>'
            in self.browser.contents)
        self.assertTrue('<option value="First option_99.9_USD">First option '
                        '@ 99.9 US Dollar</option>' in self.browser.contents)
        # In test browser we can at least replace the option
        self.browser.getControl(
            name="form.product_options.0.").value = ['First option_99.9_USD']
        self.assertEqual(
            contract.product_options[0].title, 'Any product option')
        self.browser.getControl("Save").click()
        self.assertEqual(contract.product_options[0].title, 'First option')
        self.browser.getLink("View").click()
        self.assertTrue(
            '<span>First option @ 99.9 US Dollar</span>'
            in self.browser.contents)
        self.assertEqual(
            self.browser.url, self.contracts_path + '/%s/index' % conid)
        # An href attribute is referring to the document and product objects
        self.assertTrue('<a href="http://localhost/app/products/SAM">SAM -'
            in self.browser.contents)
        self.assertTrue(
            '<a href="http://localhost/app/customers/K1000000/'
            'documents/DOC1">DOC1 -'
            in self.browser.contents)
        # Customer can submit the form if confirmation box is ticket.
        # The form is also saved.
        self.browser.getLink("Edit").click()
        self.browser.getControl("Proceed to checkout").click()
        self.assertTrue(
            'confirm your acceptance of these by ticking'
            in self.browser.contents)
        self.assertEqual(contract.state, 'created')
        self.browser.getControl(name="confirm_tc").value = True
        self.browser.getControl("Proceed to checkout").click()
        self.assertEqual(contract.state, 'created')
        radio_ctrl = self.browser.getControl(name='gw')
        radio_ctrl.value = [radio_ctrl.options[0]]  # pick first payment opt
        self.browser.getControl("Select payment method").click()
        # Select demo payment
        self.browser.getControl(name="cc_number").value = '123456789012345'
        self.browser.getControl(name="csc").value = '1234'
        self.browser.getControl("Authorize Payment").click()
        self.assertTrue(
            'Your payment will now be verified'
            in self.browser.contents)
        self.assertEqual(contract.state, 'awaiting')
        # Customer can't edit the contract once it has been submitted
        self.browser.open(self.contracts_path + '/%s/edit' % conid)
        self.assertTrue(
            'The requested form is locked' in self.browser.contents)

    def test_view_slips(self):
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        # Officers can open the contract overview
        self.browser.open(self.customer_path + '/contracts')
        self.browser.getLink("Download contracts overview").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(
            self.browser.headers['Content-Type'], 'application/pdf')
        path = os.path.join(samples_dir(), 'contracts_overview_slip.pdf')
        open(path, 'wb').write(self.browser.contents)
        print "Sample PDF overview_slip.pdf written to %s" % path
        # Officers can open contract slips.
        # First we add a submitted document and a product.
        IWorkflowState(self.document).setState(SUBMITTED)
        self.contract.document_object = self.document
        self.contract.product_object = self.product
        self.contract.tc_dict = {'en': u'<strong>Hello world</strong>'}
        self.contract.valid_from = date(2015, 12, 4)
        self.contract.valid_to = 'anything'
        self.contract.title = u'Contract Title'
        self.browser.open(self.customer_path + '/contracts/CON1')
        self.browser.getLink("Download contract slip").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(
            self.browser.headers['Content-Type'], 'application/pdf')
        path = os.path.join(samples_dir(), 'contract_slip.pdf')
        open(path, 'wb').write(self.browser.contents)
        print "Sample contract_slip.pdf written to %s" % path

    def test_view_receipt(self):
        self.add_product_option(self.contract)
        self.setup_payment()
        IWorkflowState(self.document).setState(SUBMITTED)
        self.contract.document_object = self.document
        self.contract.product_object = self.product
        self.contract.title = u'Contract Title'
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        # Officers can open the receipt
        self.browser.open(self.customer_path + '/contracts/CON1')
        self.browser.getLink("Download payment receipt").click()
        self.assertEqual(self.browser.headers['Status'], '200 Ok')
        self.assertEqual(
            self.browser.headers['Content-Type'], 'application/pdf')
        path = os.path.join(samples_dir(), 'contract_payment_receipt.pdf')
        open(path, 'wb').write(self.browser.contents)
        print "Sample contract_payment_receipt.pdf written to %s" % path

    def test_contract_approval(self):
        # This is not a UI test. It's just a functional test.
        self.assertRaises(ConstraintNotSatisfied,
            self.contract.document_object, self.document)
        # Document must be at least submitted
        IWorkflowState(self.document).setState('submitted')
        self.contract.document_object = self.document
        IWorkflowState(self.contract).setState('submitted')
        self.assertRaises(InvalidTransitionError,
            IWorkflowInfo(self.contract).fireTransition, 'approve')
        # Customer must be approved and
        # document must be verified for the approval of contracts
        IWorkflowState(self.document).setState('verified')
        IWorkflowState(self.customer).setState('approved')
        IWorkflowInfo(self.contract).fireTransition('approve')
        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')

    def test_contract_approval_in_UI(self):
        # Now let's see what the UI says when trying to approve a contract
        # with unverified documents.
        IWorkflowState(self.customer).setState('approved')
        IWorkflowState(self.document).setState('submitted')
        self.contract.document_object = self.document
        IWorkflowState(self.contract).setState('submitted')
        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
        self.browser.open(self.contracts_path + '/CON1/trigtrans')
        self.browser.getControl(name="transition").value = ['approve']
        self.browser.getControl("Apply").click()
        # InvalidTransitionError is catched
        self.assertTrue(
            '<div class="alert alert-warning">Attached documents '
            'must be verified first.</div>'
            in self.browser.contents)
        self.browser.open(self.contracts_path + '/CON1/trigtrans')
        IWorkflowState(self.document).setState('verified')
        self.browser.getControl(name="transition").value = ['approve']
        self.browser.getControl("Apply").click()
        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')

    def test_select_payment(self):
        # select payment
        self.prepare_payment_select()
        self.browser.open('%s/CON1/edit' % self.contracts_path)
        self.browser.getControl("Proceed to checkout").click()
        self.assertTrue(
            "Select payment method" in self.browser.contents)
        self.assertTrue(
            'Credit Card (Demo Payments)' in self.browser.contents)

    def test_select_payment_no_choice(self):
        # we get warned if no payment was selected
        self.prepare_payment_select()
        self.browser.open(
            '%s/CON1/select_payment_method' % self.contracts_path)
        self.browser.getControl(self.never_ending_button_text).click()
        self.assertTrue(
            'Please pick a payment method' in self.browser.contents)

    def test_select_payment_demo_provider(self):
        # we can proceed with payments if we select a payment method
        self.prepare_payment_select()
        self.browser.open(
            '%s/CON1/select_payment_method' % self.contracts_path)
        radio_ctrl = self.browser.getControl(name='gw')
        radio_ctrl.displayValue = ['Credit Card (Demo Payments)']
        self.browser.getControl(self.never_ending_button_text).click()
